diff --git a/legacy/query.py b/legacy/query.py new file mode 100644 index 0000000..da955f5 --- /dev/null +++ b/legacy/query.py @@ -0,0 +1,129 @@ +import enum +from functools import cached_property +from dataclasses import dataclass +from typing import Iterator +from .common import DeviceKind, Circuit + + +class ResponsePriority(enum.Enum): + """ + The priority of the result. + """ + + LESS_DEVICES = enum.auto() + """Less devices is the first priority.""" + MORE_ACCURACY = enum.auto() + """More accuracy is the first priority.""" + + +@dataclass +class Request: + """ + All request infomation for the resolver. + """ + + device_kind: DeviceKind + """The kind of device to resolve.""" + target_value: float + """The target value of the device.""" + tolerance: float + """The tolerance of the device in absolute value.""" + response_priority: ResponsePriority + """The priority principle when sorting response items.""" + count_limit: int + """The limited count of results.""" + + +class ResponseItem: + """ + The possible solution given by the resolver. + """ + + __circuit: Circuit + """The circuit of the response item.""" + __device_kind: DeviceKind + """The kind of device of this circuit.""" + __target_value: float + """The target value of this circuit.""" + + def __init__( + self, circuit: Circuit, device_kind: DeviceKind, target_value: float + ) -> None: + self.__circuit = circuit + self.__device_kind = device_kind + self.__target_value = target_value + + @property + def circuit(self) -> Circuit: + """ + The circuit of this response item. + + :return: The circuit. + """ + return self.circuit + + @cached_property + def device_count(self) -> int: + """ + The device count of this circuit. + + :return: The device count. + """ + return self.circuit.device_scale.to_device_count() + + @cached_property + def value(self) -> float: + """ + The value of this circuit. + + :return: The value. + """ + return self.__circuit.compute(self.__device_kind) + + @cached_property + def difference(self) -> float: + """ + The absolute difference between the target value and the value of this circuit. + + :return: The absolute difference. + """ + return abs(self.__target_value - self.value) + + @cached_property + def relative_difference(self) -> float: + """ + The relative difference between the target value and the value of this circuit. + + :return: The relative difference. + """ + return self.difference / self.__target_value + + +class Response: + """ + The collection of possible solutions given by the resolver. + + For getting the response items, please use the iterator protocol. + """ + + __sorted_items: list[ResponseItem] + """The sorted items by priority and difference.""" + + def __init__(self, request: Request, candidates: Iterator[Circuit]) -> None: + self.__sorted_items = list( + ResponseItem(item, request.device_kind, request.target_value) + for item in candidates + ) + + # Sort by different strategy + match request.response_priority: + case ResponsePriority.LESS_DEVICES: + self.__sorted_items.sort(key=lambda x: (x.device_count, x.difference)) + case ResponsePriority.MORE_ACCURACY: + self.__sorted_items.sort(key=lambda x: x.difference) + + # Cut item by limit + self.__sorted_items = self.__sorted_items[:request.count_limit] + + def __iter__(self) -> Iterator[ResponseItem]: + return iter(self.__sorted_items) diff --git a/legacy/resolver/__init__.py b/legacy/resolver/__init__.py index 4943829..ba4f47e 100644 --- a/legacy/resolver/__init__.py +++ b/legacy/resolver/__init__.py @@ -1,11 +1,9 @@ -from .common import Resolver, ResultPriority, ResolverRequest +from .common import Resolver from .lut import LutResolver from .astar import AStarResolver __all__ = [ 'Resolver', - 'ResultPriority', - 'ResolverRequest', 'LutResolver', 'AStarResolver' ] diff --git a/legacy/resolver/astar.py b/legacy/resolver/astar.py index 4474523..35a569a 100644 --- a/legacy/resolver/astar.py +++ b/legacy/resolver/astar.py @@ -1,5 +1,5 @@ from typing import Iterator -from .common import Resolver, ResolverRequest, ResultPriority +from .common import Resolver, Request, ResultPriority from ..dataset import DatasetCollection from ..common import Circuit @@ -12,5 +12,5 @@ class AStarResolver(Resolver): pass - def resolve(self, request: ResolverRequest) -> Iterator[Circuit]: + def resolve(self, request: Request) -> Iterator[Circuit]: pass diff --git a/legacy/resolver/common.py b/legacy/resolver/common.py index 04166e8..95e82f2 100644 --- a/legacy/resolver/common.py +++ b/legacy/resolver/common.py @@ -1,38 +1,5 @@ -import enum from abc import ABC, abstractmethod -from dataclasses import dataclass -from typing import Iterator -from ..common import DeviceKind, Circuit - - -class ResultPriority(enum.Enum): - """ - The priority of the result. - """ - - LESS_DEVICES = enum.auto() - """Less devices is the first priority.""" - MORE_ACCURACY = enum.auto() - """More accuracy is the first priority.""" - - -@dataclass -class ResolverRequest: - """ - All request infomation for the resolver. - """ - - device_kind: DeviceKind - """The kind of device to resolve.""" - target_value: float - """The target value of the device.""" - tolerance: float - """The tolerance of the device in absolute value.""" - result_priority: ResultPriority - """The priority of the result.""" - count_limit: int - """The limited count of results.""" - +from ..query import Request, Response class Resolver(ABC): """ @@ -40,5 +7,5 @@ class Resolver(ABC): """ @abstractmethod - def resolve(self, request: ResolverRequest) -> Iterator[Circuit]: + def resolve(self, request: Request) -> Response: pass diff --git a/legacy/resolver/lut.py b/legacy/resolver/lut.py index ab2103e..505285f 100644 --- a/legacy/resolver/lut.py +++ b/legacy/resolver/lut.py @@ -2,9 +2,10 @@ import heapq from itertools import chain, product from typing import Iterable, Iterator from functools import cached_property -from .common import Resolver, ResolverRequest, ResultPriority +from .common import Resolver from ..dataset import DatasetCollection, Dataset -from ..common import Circuit, DeviceKind, JointKind, LcrConnException +from ..common import Circuit, DeviceKind, JointKind +from ..query import Request, Response class LutItem: @@ -67,13 +68,13 @@ class ResultBucket(Iterable[LutItem]): def score(self) -> float: """The score associated with this item.""" return self.__score - + @property def item(self) -> LutItem: """The underlying LutItem.""" return self.__item - def __lt__(self, other: 'ResultBucket.ResultBucketItem') -> bool: + def __lt__(self, other: "ResultBucket.ResultBucketItem") -> bool: # heapq is a min-heap: it always pops the smallest element. # We invert the comparison so that an item with a larger score # is considered "smaller", effectively turning the min-heap @@ -179,7 +180,7 @@ class LutResolver(Resolver): ) ] - def resolve(self, request: ResolverRequest) -> Iterator[Circuit]: + def resolve(self, request: Request) -> Response: # Fetch LUT by device kind lut: list[LutItem] match request.device_kind: @@ -202,4 +203,4 @@ class LutResolver(Resolver): bucket.insert(item, difference) # Return result - return map(lambda item: item.circuit, bucket) + return Response(request, map(lambda item: item.circuit, bucket))