Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FhY Utilities #9

Merged
merged 18 commits into from
Nov 10, 2024
Merged
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
f79cbe5
Packaging and Typing Improvements (#8)
christopherpriebe Nov 6, 2024
256e74c
Upgrade to Strict Type Checking (#7)
christopherpriebe Nov 6, 2024
7d2f6cc
feat: Added copy methods to constraint and param.
christopherpriebe Nov 8, 2024
4c94b1c
fix(identifier): Removed args from the docstring.
christopherpriebe Nov 9, 2024
16eb4a1
fix(identifier): Changed identifier module docstring.
christopherpriebe Nov 9, 2024
26a0f5a
feat(utils.enum): Added string and integer enum fix for Python 3.10.
christopherpriebe Nov 9, 2024
8c214ec
feat(error): Added an error registration system and a custom type error.
christopherpriebe Nov 9, 2024
b2f53c3
fix: Added type annotations to pytest fixtures in test_stack.
christopherpriebe Nov 9, 2024
1d12763
feat(types): Added core type system.
christopherpriebe Nov 9, 2024
327db20
feat(error): Added symbol table error.
christopherpriebe Nov 9, 2024
71e4e6e
feat(symbol_table): Added core symbol table.
christopherpriebe Nov 9, 2024
e179490
fix(enum): Typing fixes.
christopherpriebe Nov 9, 2024
c9970b8
fix(types): Typing fix.
christopherpriebe Nov 9, 2024
9e5cdad
fix: Updated internal type variables to be private.
christopherpriebe Nov 9, 2024
1243ca7
fix(symbol_table): Fixed typing issues.
christopherpriebe Nov 9, 2024
efaf17d
fix(__init__): Added missing imports for new features.
christopherpriebe Nov 9, 2024
9f758ee
Merge branch 'dev' into dev-utils
christopherpriebe Nov 10, 2024
e11d474
fix(utils.enum): Typing fix.
christopherpriebe Nov 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions src/fhy_core/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""FhY compiler core utilities."""

__version__ = "0.0.1"
__version__ = "0.0.2"


from .constraint import (
@@ -9,6 +9,7 @@
InSetConstraint,
NotInSetConstraint,
)
from .error import FhYCoreTypeError, SymbolTableError, register_error
from .expression import (
BinaryExpression,
BinaryOperation,
@@ -31,4 +32,12 @@
PermParam,
RealParam,
)
from .utils import Lattice, PartiallyOrderedSet, Stack, invert_dict, invert_frozen_dict
from .utils import (
IntEnum,
Lattice,
PartiallyOrderedSet,
Stack,
StrEnum,
invert_dict,
invert_frozen_dict,
)
27 changes: 26 additions & 1 deletion src/fhy_core/constraint.py
Original file line number Diff line number Diff line change
@@ -10,7 +10,12 @@
from abc import ABC, abstractmethod
from typing import Any

from .expression import Expression, LiteralExpression, simplify_expression
from .expression import (
Expression,
LiteralExpression,
copy_expression,
simplify_expression,
)
from .identifier import Identifier


@@ -32,6 +37,10 @@ def is_satisfied(self, values: dict[Identifier, Any]) -> bool:

"""

@abstractmethod
def copy(self) -> "Constraint":
"""Return a shallow copy of the constraint."""


class EquationConstraint(Constraint):
"""Represents an equation constraint."""
@@ -49,6 +58,10 @@ def is_satisfied(self, values: dict[Identifier, Expression]) -> bool:
and result.value
)

def copy(self) -> "EquationConstraint":
new_constraint = EquationConstraint(copy_expression(self._expression))
return new_constraint


class InSetConstraint(Constraint):
"""Represents an in-set constraint."""
@@ -67,6 +80,12 @@ def is_satisfied(self, values: dict[Identifier, Any]) -> bool:
values[variable] in self._valid_values for variable in self._variables
)

def copy(self) -> "InSetConstraint":
new_constraint = InSetConstraint(
self._variables.copy(), self._valid_values.copy()
)
return new_constraint


class NotInSetConstraint(Constraint):
"""Represents a not-in-set constraint."""
@@ -84,3 +103,9 @@ def is_satisfied(self, values: dict[Identifier, Any]) -> bool:
return any(
values[variable] not in self._invalid_values for variable in self._variables
)

def copy(self) -> "NotInSetConstraint":
new_constraint = NotInSetConstraint(
self._variables.copy(), self._invalid_values.copy()
)
return new_constraint
28 changes: 28 additions & 0 deletions src/fhy_core/error.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""Core compiler errors and error registration."""

COMPILER_ERRORS: dict[type[Exception], str] = {}


def register_error(error: type[Exception]) -> type[Exception]:
"""Decorator to register custom compiler exceptions.

Args:
error: Custom exception to be registered.

Returns:
Custom exception registered

"""
COMPILER_ERRORS[error] = error.__doc__ or error.__name__

return error


@register_error
class FhYCoreTypeError(TypeError):
"""Core type error."""


@register_error
class SymbolTableError(Exception):
"""Symbol table error."""
6 changes: 3 additions & 3 deletions src/fhy_core/expression/parser.py
Original file line number Diff line number Diff line change
@@ -40,12 +40,12 @@ def _build_token_pattern(*patterns: str) -> re.Pattern[str]:
)


OperationType = TypeVar("OperationType")
_OperationType = TypeVar("_OperationType")


def _get_symbol_to_operation_subdict(
parent_dict: frozendict[str, OperationType], *symbols: str
) -> frozendict[str, OperationType]:
parent_dict: frozendict[str, _OperationType], *symbols: str
) -> frozendict[str, _OperationType]:
return frozendict(
{
symbol: operation
11 changes: 4 additions & 7 deletions src/fhy_core/identifier.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
"""Unique identifier for named FhY objects."""
"""Unique identifier for named compiler objects."""

__all__ = ["Identifier"]

__all__ = ["Identifier"]

from typing import Any


class Identifier:
"""Unique name.

Args:
name_hint: Variable name.

"""
"""Unique name."""

_next_id: int = 0
_id: int
63 changes: 49 additions & 14 deletions src/fhy_core/param/core.py
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@
]

from abc import ABC
from collections.abc import Hashable, Sequence
from collections.abc import Collection, Hashable, Sequence
from typing import Any, Generic, TypeVar

from fhy_core.constraint import (
@@ -21,7 +21,8 @@
from fhy_core.expression import IdentifierExpression, LiteralExpression
from fhy_core.identifier import Identifier

H = TypeVar("H", bound=Hashable)
_H = TypeVar("_H", bound=Hashable)
_T = TypeVar("_T")


def _is_values_unique_in_sequence_without_set(values: Sequence[Any]) -> bool:
@@ -36,17 +37,14 @@ def _is_values_unique_in_sorted_sequence(values: Sequence[Any]) -> bool:
return all(values[i] != values[i + 1] for i in range(len(values) - 1))


def _is_values_unique_in_sequence_with_set(values: Sequence[H]) -> bool:
def _is_values_unique_in_sequence_with_set(values: Collection[_H]) -> bool:
return len(values) == len(set(values))


T = TypeVar("T")


class Param(ABC, Generic[T]):
class Param(ABC, Generic[_T]):
"""Abstract base class for constrained parameters."""

_value: T | None
_value: _T | None
_variable: Identifier
_constraints: list[Constraint]

@@ -82,7 +80,7 @@ def is_constraints_satisfied(self, value: Any) -> bool:
return False
return True

def get_value(self) -> T:
def get_value(self) -> _T:
"""Return the parameter value.

Raises:
@@ -93,7 +91,7 @@ def get_value(self) -> T:
raise ValueError("Parameter is not set.")
return self._value

def set_value(self, value: T) -> None:
def set_value(self, value: _T) -> None:
"""Set the parameter value.

Args:
@@ -116,6 +114,28 @@ def add_constraint(self, constraint: Constraint) -> None:
"""
self._constraints.append(constraint)

def copy(self) -> "Param[_T]":
"""Return a shallow copy of the parameter."""
new_param = self.__class__(self._variable)
new_param._value = self._value
self.copy_constraints_to_new_param(self, new_param)
return new_param

@staticmethod
def copy_constraints_to_new_param(
param: "Param[_T]", new_param: "Param[_T]"
) -> None:
"""Copy constraints from one parameter to another.

Args:
param: Parameter to copy constraints from.
new_param: Parameter to copy constraints to.

"""
new_param._constraints = [
constraint.copy() for constraint in param._constraints
]


class RealParam(Param[str | float]):
"""Real-valued parameter."""
@@ -171,24 +191,29 @@ def add_constraint(self, constraint: Constraint) -> None:
)
return super().add_constraint(constraint)

def copy(self) -> "OrdinalParam":
new_param = OrdinalParam(self._all_values, self._variable)
self.copy_constraints_to_new_param(self, new_param)
return new_param


class CategoricalParam(Param[H]):
class CategoricalParam(Param[_H]):
"""Categorical parameter.

Note:
All values must be hashable.

"""

_categories: set[H]
_categories: set[_H]

def __init__(self, categories: Sequence[H], name: Identifier | None = None):
def __init__(self, categories: Collection[_H], name: Identifier | None = None):
super().__init__(name)
if not _is_values_unique_in_sequence_with_set(categories):
raise ValueError("Values must be unique.")
self._categories = set(categories)

def set_value(self, value: H) -> None:
def set_value(self, value: _H) -> None:
if value not in self._categories:
raise ValueError("Value is not in the set of allowed categories.")
return super().set_value(value)
@@ -201,6 +226,11 @@ def add_constraint(self, constraint: Constraint) -> None:
)
return super().add_constraint(constraint)

def copy(self) -> "CategoricalParam[_H]":
new_param = CategoricalParam[_H](self._categories.copy(), self._variable)
self.copy_constraints_to_new_param(self, new_param)
return new_param


class PermParam(Param[tuple[Any, ...]]):
"""Permutation parameter.
@@ -241,3 +271,8 @@ def add_constraint(self, constraint: Constraint) -> None:
"permutation parameters."
)
return super().add_constraint(constraint)

def copy(self) -> "PermParam":
new_param = PermParam(self._all_values, self._variable)
self.copy_constraints_to_new_param(self, new_param)
return new_param
Loading