Skip to content

Commit 81e9579

Browse files
authored
Merge pull request #2798 from fermga/copilot/update-default-functional-links
feat: Make UM functional links default behavior
2 parents 45f6929 + 4876994 commit 81e9579

File tree

4 files changed

+43
-3
lines changed

4 files changed

+43
-3
lines changed

docs/API_CONTRACTS.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,11 +133,12 @@ This document formalizes the **structural invariants** and **API contracts** for
133133
- **Postconditions**:
134134
- `θ_new = θ_old + UM_theta_push * angle_diff(θ_neighbor_mean, θ_old)`
135135
- `0 ≤ UM_theta_push ≤ 1` (default: 0.25)
136-
- **Optional**: If `UM_FUNCTIONAL_LINKS=True`, may add edges based on:
136+
- By default (`UM_FUNCTIONAL_LINKS=True`), may add edges based on:
137137
- Phase similarity: `1 - |Δθ|/π`
138138
- EPI similarity: `1 - |EPI_i - EPI_j| / (|EPI_i| + |EPI_j| + ε)`
139139
- Sense index similarity: `1 - |Si_i - Si_j|`
140140
- Edge added if `compatibility ≥ UM_COMPAT_THRESHOLD` (default: 0.75)
141+
- Set `UM_FUNCTIONAL_LINKS=False` to disable link creation (edge case: phase-only sync)
141142
- `EPI`, `νf`, `ΔNFR` remain unchanged
142143
- **Structural Effect**: Aligns node phase with neighbor mean, enables structural coupling
143144
- **TNFR Invariants**:

docs/source/user-guide/OPERATORS_GUIDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ print(f"ΔNFR after coupling: {dnfr1:.3f} (reduced by mutual stabilization)")
224224
- `UM_BIDIRECTIONAL`: Enable bidirectional phase sync (default: True)
225225
- `UM_SYNC_VF`: Enable frequency synchronization (default: True)
226226
- `UM_STABILIZE_DNFR`: Enable ΔNFR stabilization (default: True)
227-
- `UM_FUNCTIONAL_LINKS`: Create edges based on compatibility (default: False)
227+
- `UM_FUNCTIONAL_LINKS`: Create edges based on compatibility (default: True)
228228

229229
**Contracts**:
230230
- Must verify phase compatibility before coupling

src/tnfr/operators/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -739,7 +739,7 @@ def _op_UM(node: NodeProtocol, gf: GlyphFactors) -> None: # UM — Coupling
739739
reduction_factor = 1.0 - (k_dnfr * mean_alignment)
740740
node.dnfr = node.dnfr * reduction_factor
741741

742-
if bool(node.graph.get("UM_FUNCTIONAL_LINKS", False)):
742+
if bool(node.graph.get("UM_FUNCTIONAL_LINKS", True)):
743743
thr = float(
744744
node.graph.get(
745745
"UM_COMPAT_THRESHOLD",

tests/unit/dynamics/test_operators.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,45 @@ def test_um_candidate_subset_proximity(graph_canon):
280280
assert not G.has_edge(0, 3)
281281

282282

283+
def test_um_functional_links_enabled_by_default(graph_canon):
284+
"""UM operator creates functional links by default (canonical behavior)."""
285+
G = graph_canon()
286+
inject_defaults(G)
287+
# Create nodes with similar phase, EPI, and Si for high compatibility
288+
for i, th in enumerate([0.0, 0.05, 0.1]):
289+
G.add_node(i, **{"theta": th, "EPI": 1.0, "Si": 0.8})
290+
291+
# Lower threshold to ensure links are created for testing
292+
G.graph["UM_COMPAT_THRESHOLD"] = 0.5
293+
294+
# Apply UM without explicitly setting UM_FUNCTIONAL_LINKS
295+
# Links should be created by default
296+
apply_glyph(G, 0, "UM")
297+
298+
# Verify that at least one link was created
299+
# (exact number depends on compatibility calculation)
300+
assert G.number_of_edges() > 0, "UM should create functional links by default"
301+
302+
303+
def test_um_functional_links_can_be_disabled(graph_canon):
304+
"""UM operator respects explicit UM_FUNCTIONAL_LINKS=False (edge case)."""
305+
G = graph_canon()
306+
inject_defaults(G)
307+
# Create nodes with similar phase, EPI, and Si for high compatibility
308+
for i, th in enumerate([0.0, 0.05, 0.1]):
309+
G.add_node(i, **{"theta": th, "EPI": 1.0, "Si": 0.8})
310+
311+
# Explicitly disable functional links (edge case for phase-only sync)
312+
G.graph["UM_FUNCTIONAL_LINKS"] = False
313+
G.graph["UM_COMPAT_THRESHOLD"] = 0.5
314+
315+
# Apply UM with links explicitly disabled
316+
apply_glyph(G, 0, "UM")
317+
318+
# Verify that no links were created
319+
assert G.number_of_edges() == 0, "UM should respect UM_FUNCTIONAL_LINKS=False"
320+
321+
283322
def test_um_candidate_iter_missing_nodenx(monkeypatch):
284323
class DummyNode:
285324
def __init__(self) -> None:

0 commit comments

Comments
 (0)