Skip to content

Commit d8154ce

Browse files
committed
Intent: streamline weight coherence
Operators involved: [Coherence, Self-organization] Affected invariants: [#1, #7] Key changes: - expose kahan_sum at module scope - scan weights once for negatives and totals - lazy helper exports to break circular imports Expected risks/dissonances: import indirection may hide missing symbols; mitigated via __getattr__ guard Metrics: C(t), Si, νf, phase unchanged Equivalence map: None
1 parent a9c2a5b commit d8154ce

File tree

2 files changed

+37
-20
lines changed

2 files changed

+37
-20
lines changed

src/tnfr/collections_utils.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
from .logging_utils import get_logger, warn_once
1212
from .value_utils import convert_value
13+
from .helpers.numeric import kahan_sum
1314

1415
T = TypeVar("T")
1516

@@ -186,10 +187,20 @@ def _handle_negative_weights(
186187
error_on_negative: bool,
187188
warn_once: bool,
188189
) -> float:
189-
"""Clamp negative weights and adjust total according to policy."""
190-
negatives = {k: w for k, w in weights.items() if w < 0}
191-
from .helpers.numeric import kahan_sum # local import to avoid circular dependency
192-
total = kahan_sum(weights.values())
190+
"""Clamp negative weights and adjust total according to policy.
191+
192+
Negative detection and total accumulation share a single pass using
193+
Kahan summation.
194+
"""
195+
negatives: dict[str, float] = {}
196+
197+
def values() -> Iterable[float]:
198+
for k, w in weights.items():
199+
if w < 0:
200+
negatives[k] = w
201+
yield w
202+
203+
total = kahan_sum(values())
193204
if not negatives:
194205
return total
195206
if error_on_negative:
@@ -263,7 +274,6 @@ def normalize_counter(
263274
counts: Mapping[str, float | int],
264275
) -> tuple[dict[str, float], float]:
265276
"""Normalize a ``Counter`` returning proportions and total."""
266-
from .helpers.numeric import kahan_sum # local import to avoid circular dependency
267277
total = kahan_sum(counts.values())
268278
if total <= 0:
269279
return {}, 0
@@ -279,7 +289,6 @@ def mix_groups(
279289
) -> dict[str, float]:
280290
"""Aggregate values of ``dist`` according to ``groups``."""
281291
out: dict[str, float] = dict(dist)
282-
from .helpers.numeric import kahan_sum # local import to avoid circular dependency
283292
out.update(
284293
{
285294
f"{prefix}{label}": kahan_sum(dist.get(k, 0.0) for k in keys)

src/tnfr/helpers/__init__.py

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
11
from __future__ import annotations
22

3-
from ..collections_utils import (
4-
MAX_MATERIALIZE_DEFAULT,
5-
ensure_collection,
6-
normalize_weights,
7-
normalize_counter,
8-
mix_groups,
9-
)
103
from ..graph_utils import mark_dnfr_prep_dirty
114

125
from .numeric import (
@@ -21,13 +14,6 @@
2114
neighbor_phase_mean,
2215
neighbor_phase_mean_list,
2316
)
24-
from ..glyph_history import (
25-
push_glyph,
26-
recent_glyph,
27-
ensure_history,
28-
last_glyph,
29-
count_glyphs,
30-
)
3117
from .cache import (
3218
get_graph,
3319
get_graph_mapping,
@@ -74,3 +60,25 @@
7460
"get_graph_mapping",
7561
"mark_dnfr_prep_dirty",
7662
)
63+
64+
65+
def __getattr__(name: str):
66+
if name in {
67+
"MAX_MATERIALIZE_DEFAULT",
68+
"ensure_collection",
69+
"normalize_weights",
70+
"normalize_counter",
71+
"mix_groups",
72+
}:
73+
from .. import collections_utils as _cu
74+
return getattr(_cu, name)
75+
if name in {
76+
"push_glyph",
77+
"recent_glyph",
78+
"ensure_history",
79+
"last_glyph",
80+
"count_glyphs",
81+
}:
82+
from .. import glyph_history as _gh
83+
return getattr(_gh, name)
84+
raise AttributeError(f"module {__name__!r} has no attribute {name}")

0 commit comments

Comments
 (0)