Skip to content
This repository has been archived by the owner on Dec 10, 2024. It is now read-only.

Commit

Permalink
Implement get_known_superranges
Browse files Browse the repository at this point in the history
  • Loading branch information
iopapamanoglou committed Nov 15, 2024
1 parent 9a4bd58 commit b866af4
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 39 deletions.
106 changes: 87 additions & 19 deletions src/faebryk/core/defaultsolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,27 @@
from faebryk.core.parameter import (
Add,
And,
Arithmetic,
ConstrainableExpression,
Divide,
Domain,
Expression,
GreaterOrEqual,
Is,
IsSubset,
LessOrEqual,
Multiply,
Numbers,
Or,
Parameter,
ParameterOperableHasNoLiteral,
ParameterOperatable,
Power,
Predicate,
Subtract,
has_implicit_constraints_recursive,
)
from faebryk.core.solver import Solver
from faebryk.libs.sets import P_Set, Range, Ranges
from faebryk.libs.sets import P_Set, Range, Ranges, Single
from faebryk.libs.units import HasUnit, Quantity, dimensionless
from faebryk.libs.util import EquivalenceClasses, unique_ref

Expand Down Expand Up @@ -892,7 +895,9 @@ def known_unconstrained(po: ParameterOperatable) -> bool:
class DefaultSolver(Solver):
timeout: int = 1000

def phase_one_no_guess_solving(self, g: Graph) -> None:
def phase_one_no_guess_solving(
self, g: Graph
) -> dict[ParameterOperatable, ParameterOperatable]:
logger.info(f"Phase 1 Solving: No guesses {'-' * 80}")

# TODO move into comment here
Expand Down Expand Up @@ -988,6 +993,9 @@ def phase_one_no_guess_solving(self, g: Graph) -> None:
or subset_dirty
)

# FIXME repr map should contain map from first to last
return repr_map

def get_any_single(
self,
operatable: ParameterOperatable,
Expand All @@ -997,15 +1005,6 @@ def get_any_single(
) -> Any:
raise NotImplementedError()

def assert_any_predicate[ArgType](
self,
predicates: list["Solver.PredicateWithInfo[ArgType]"],
lock: bool,
suppose_constraint: Predicate | None = None,
minimize: Expression | None = None,
) -> Solver.SolveResultAny[ArgType]:
raise NotImplementedError()

def find_and_lock_solution(self, G: Graph) -> Solver.SolveResultAll:
raise NotImplementedError()

Expand All @@ -1025,16 +1024,85 @@ def inspect_known_values(
) -> P_Set[bool]:
raise NotImplementedError()

# Could be exponentially many
def inspect_known_supersets_are_few(self, value: ParameterOperatable.Sets) -> bool:
raise NotImplementedError()

def inspect_get_known_supersets(
self, value: ParameterOperatable.Sets
) -> Iterable[P_Set]:
raise NotImplementedError()

def inspect_get_known_superranges(
self, value: ParameterOperatable.NumberLike
) -> Ranges:
# IMPORTANT ------------------------------------------------------------------------

# Could be exponentially many
def inspect_known_supersets_are_few(self, value: ParameterOperatable.Sets) -> bool:
return True

def inspect_get_known_superranges(self, value: ParameterOperatable) -> Ranges:
if not isinstance(value.domain, Numbers):
raise ValueError(f"Ranges only defined for numbers not {value.domain}")

# run phase 1 solver
# TODO caching
repr_map = self.phase_one_no_guess_solving(value.get_graph())
value = repr_map[value]

# check predicates (is, subset, greater, less)
try:
literal = value.get_literal(Is)
if Parameter.is_number_literal(literal):
return Ranges(Single(literal))
if isinstance(literal, P_Set):
if isinstance(literal, Range):
return Ranges(literal)
if isinstance(literal, Ranges):
return literal
except ParameterOperableHasNoLiteral:
pass

try:
literal = value.get_literal(IsSubset)
if Parameter.is_number_literal(literal):
return Ranges(Single(literal))
if isinstance(literal, P_Set):
if isinstance(literal, Range):
return Ranges(literal)
if isinstance(literal, Ranges):
return literal
except ParameterOperableHasNoLiteral:
pass

# TODO LT, GT
lower = None
try:
literal = value.get_literal(GreaterOrEqual)
if Parameter.is_number_literal(literal):
lower = literal
elif isinstance(literal, P_Set):
if isinstance(literal, Range):
lower = literal.max_elem()
elif isinstance(literal, Ranges):
lower = literal.max_elem()
except ParameterOperableHasNoLiteral:
lower = float("-inf") * HasUnit.get_units(value)

upper = None
try:
literal = value.get_literal(LessOrEqual)
if Parameter.is_number_literal(literal):
upper = literal
elif isinstance(literal, P_Set):
if isinstance(literal, Range):
upper = literal.min_elem()
elif isinstance(literal, Ranges):
upper = literal.min_elem()
except ParameterOperableHasNoLiteral:
upper = float("inf") * HasUnit.get_units(value)

return Ranges(Range(lower, upper))

def assert_any_predicate[ArgType](
self,
predicates: list["Solver.PredicateWithInfo[ArgType]"],
lock: bool,
suppose_constraint: Predicate | None = None,
minimize: Expression | None = None,
) -> Solver.SolveResultAny[ArgType]:
raise NotImplementedError()
32 changes: 16 additions & 16 deletions src/faebryk/core/parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,11 @@ class ParameterOperatable(Node):
operated_on: GraphInterface

@property
def domain(self) -> "Domain":
raise NotImplementedError()

def get_operations(self) -> set["Expression"]:
res = self.operated_on.get_connected_nodes(types=[Expression])
return cast(set[Expression], res)
def domain(self) -> "Domain": ...

def has_implicit_constraint(self) -> bool:
raise NotImplementedError()
def has_implicit_constraint(self) -> bool: ...

def has_implicit_constraints_recursive(self) -> bool:
raise NotImplementedError()
def has_implicit_constraints_recursive(self) -> bool: ...

@staticmethod
def sort_by_depth(
Expand Down Expand Up @@ -354,25 +347,32 @@ def if_then_else(
# ) -> None: ...

# ----------------------------------------------------------------------------------

def get_operators[T: "Expression"](self, types: type[T] | None = None) -> list[T]:
def get_operations[T: "Expression"](self, types: type[T] | None = None) -> set[T]:
if types is None:
types = Expression # type: ignore
types = cast(type[T], types)
assert issubclass(types, Expression)

return cast(list[T], self.operated_on.get_connected_nodes(types=[types]))
return cast(set[T], self.operated_on.get_connected_nodes(types=[types]))

def get_literal(self) -> Literal:
iss = self.get_operators(Is)
def get_literal(self, op: type["Expression"] | None = None) -> Literal:
if op is None:
op = Is
iss = self.get_operations(op)
try:
literal_is = find(o for i in iss for o in i.get_literal_operands())
except KeyErrorNotFound as e:
raise ParameterOperableHasNoLiteral(
self, f"Parameter {self} has no literal"
self, f"Parameter {self} has no literal for op {op}"
) from e
return literal_is

# type checks

@staticmethod
def is_number_literal(value: Any) -> bool:
return isinstance(value, (int, float, Unit, Quantity))


def has_implicit_constraints_recursive(po: ParameterOperatable.All) -> bool:
if isinstance(po, ParameterOperatable):
Expand Down
4 changes: 2 additions & 2 deletions src/faebryk/library/_F.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from faebryk.library.has_overriden_name import has_overriden_name
from faebryk.library.has_linked_pad import has_linked_pad
from faebryk.library.has_reference import has_reference
from faebryk.library.has_footprint_requirement import has_footprint_requirement
from faebryk.library.can_bridge import can_bridge
from faebryk.library.can_specialize import can_specialize
from faebryk.library.has_descriptive_properties import has_descriptive_properties
Expand All @@ -37,7 +38,6 @@
from faebryk.library.has_simple_value_representation import has_simple_value_representation
from faebryk.library.has_capacitance import has_capacitance
from faebryk.library.has_construction_dependency import has_construction_dependency
from faebryk.library.has_footprint_requirement import has_footprint_requirement
from faebryk.library.has_kicad_ref import has_kicad_ref
from faebryk.library.has_picker import has_picker
from faebryk.library.has_pcb_layout import has_pcb_layout
Expand All @@ -61,14 +61,14 @@
from faebryk.library.has_overriden_name_defined import has_overriden_name_defined
from faebryk.library.has_linked_pad_defined import has_linked_pad_defined
from faebryk.library.Symbol import Symbol
from faebryk.library.has_footprint_requirement_defined import has_footprint_requirement_defined
from faebryk.library.can_bridge_defined import can_bridge_defined
from faebryk.library.has_descriptive_properties_defined import has_descriptive_properties_defined
from faebryk.library.has_datasheet_defined import has_datasheet_defined
from faebryk.library.has_designator_defined import has_designator_defined
from faebryk.library.has_simple_value_representation_based_on_params import has_simple_value_representation_based_on_params
from faebryk.library.has_simple_value_representation_based_on_params_chain import has_simple_value_representation_based_on_params_chain
from faebryk.library.has_simple_value_representation_defined import has_simple_value_representation_defined
from faebryk.library.has_footprint_requirement_defined import has_footprint_requirement_defined
from faebryk.library.has_multi_picker import has_multi_picker
from faebryk.library.has_pcb_layout_defined import has_pcb_layout_defined
from faebryk.library.has_single_connection_impl import has_single_connection_impl
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def _get_value(self) -> str:

if isinstance(domain, Numbers):
unit = self.unit if self.unit is not None else self.param.units
if isinstance(value, (int, float, Unit, Quantity)):
if Parameter.is_number_literal(value):
# TODO If tolerance, maybe hint that it's weird there isn't any
return to_si_str(value, unit, 2)
if isinstance(value, P_Set):
Expand Down
2 changes: 1 addition & 1 deletion test/core/test_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from faebryk.core.defaultsolver import DefaultSolver
from faebryk.core.module import Module
from faebryk.core.node import Node
from faebryk.core.parameter import And, Or, Parameter
from faebryk.core.parameter import And, Parameter
from faebryk.libs.library import L
from faebryk.libs.logging import setup_basic_logging
from faebryk.libs.sets import Range, Ranges
Expand Down

0 comments on commit b866af4

Please sign in to comment.