Skip to content

Commit 1581b1c

Browse files
committed
refactor(distribution): now there can be several analytical calculations for one characteristic
1 parent 5f01953 commit 1581b1c

1 file changed

Lines changed: 18 additions & 43 deletions

File tree

src/pysatl_core/distributions/strategies.py

Lines changed: 18 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
__copyright__ = "Copyright (c) 2025 PySATL project"
1212
__license__ = "SPDX-License-Identifier: MIT"
1313

14-
from typing import TYPE_CHECKING, Any, Protocol, cast
14+
from typing import TYPE_CHECKING, Protocol, cast
1515

1616
import numpy as np
1717

@@ -20,13 +20,14 @@
2020

2121
if TYPE_CHECKING:
2222
from collections.abc import Mapping
23+
from typing import Any
2324

2425
from pysatl_core.distributions.computation import (
2526
AnalyticalComputation,
2627
FittedComputationMethod,
28+
Method,
2729
)
2830
from pysatl_core.distributions.distribution import Distribution
29-
from pysatl_core.distributions.registry.graph import RegistryView
3031
from pysatl_core.types import GenericCharacteristicName, LabelName
3132

3233

@@ -125,30 +126,16 @@ def _pick_analytical_method(
125126
f"Characteristic '{state}' provides no labeled analytical computations."
126127
) from exc
127128

128-
@staticmethod
129-
def _pick_analytical_loop_method(
130-
state: GenericCharacteristicName,
131-
view: RegistryView,
132-
) -> Method[Any, Any] | None:
133-
"""
134-
Pick the first analytical self-loop method for a characteristic in a view.
135-
"""
136-
loops = view.analytical_variants(state)
137-
if not loops:
138-
return None
139-
return cast(Method[Any, Any], next(iter(loops.values())).method)
140-
141129
def query_method(
142130
self, state: GenericCharacteristicName, distr: Distribution, **options: Any
143131
) -> Method[Any, Any]:
144132
"""
145133
Resolve a computation method for the target characteristic.
146134
147135
Resolution order:
148-
1. Cached fitted method (if caching enabled)
149-
2. Analytical implementation for non-registry characteristics
150-
3. Analytical self-loop from the registry view
151-
4. Conversion path from analytical-loop characteristics via the graph
136+
1. Analytical implementation from the distribution
137+
2. Cached fitted method (if caching enabled)
138+
3. Conversion path from an analytical characteristic via the graph
152139
153140
Parameters
154141
----------
@@ -170,46 +157,34 @@ def query_method(
170157
If no analytical base exists, no conversion path is found,
171158
or a cycle is detected.
172159
"""
173-
# 1. Check cache if enabled
160+
# 1. Check for analytical implementation
161+
if state in distr.analytical_computations:
162+
return self._pick_analytical_method(state, distr.analytical_computations[state])
163+
164+
# 2. Check cache if enabled
174165
if self._enable_caching:
175166
cached = self._cache.get(state)
176167
if cached is not None:
177168
return cached
178169

179-
# 2. Require at least one analytical characteristic
170+
# 3. Require at least one analytical characteristic
180171
if not distr.analytical_computations:
181172
raise RuntimeError(
182173
"Distribution provides no analytical computations to ground conversions."
183174
)
184175

185-
# 3. Non-registry characteristics are resolved directly.
186-
# It covers the situation where user is providing their analytical computation which isn't
187-
# in the graph
188-
registry = characteristic_registry()
189-
if state not in registry.declared_characteristics:
190-
if state in distr.analytical_computations:
191-
return self._pick_analytical_method(state, distr.analytical_computations[state])
192-
raise RuntimeError(
193-
f"Characteristic '{state}' is not declared in the registry and has no "
194-
"analytical implementation in the distribution."
195-
)
196-
197-
# 4. Get filtered graph view for this distribution.
198-
view = registry.view(distr)
176+
# 4. Get filtered graph view for this distribution
177+
reg = characteristic_registry().view(distr)
199178

200179
self._push_guard(distr, state)
201180
try:
202-
loop_method = self._pick_analytical_loop_method(state, view)
203-
if loop_method is not None:
204-
return loop_method
205-
206-
# 5. Try each analytical-loop characteristic as a source
181+
# 5. Try each analytical characteristic as a source
207182
for src in distr.analytical_computations:
208-
if not view.analytical_variants(src):
209-
continue
183+
if src == state:
184+
return self._pick_analytical_method(src, distr.analytical_computations[src])
210185

211186
# Find conversion path in the graph
212-
path = view.find_path(src, state)
187+
path = reg.find_path(src, state)
213188
if not path:
214189
continue
215190

0 commit comments

Comments
 (0)