Skip to content

Commit f4187c5

Browse files
Copilotfermga
andcommitted
Add test_um_sequences.py integration tests
Co-authored-by: fermga <203334638+fermga@users.noreply.github.com>
1 parent 702cbb2 commit f4187c5

File tree

1 file changed

+356
-0
lines changed

1 file changed

+356
-0
lines changed
Lines changed: 356 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,356 @@
1+
"""Integration tests for UM (Coupling) operator sequences.
2+
3+
Tests canonical sequences involving UM from TNFR theory:
4+
- UM → RA: Coupling followed by resonance propagation
5+
- AL → UM: Emission followed by coupling
6+
- UM → IL: Coupling stabilized into coherence
7+
- Network_sync: Complete sequence with UM
8+
- UM in network formation cycles
9+
10+
These tests validate that UM correctly:
11+
1. Synchronizes phases (θᵢ ≈ θⱼ)
12+
2. Preserves EPI identity
13+
3. Enables network-level coherence
14+
4. Works in combination with other operators
15+
"""
16+
17+
import math
18+
import pytest
19+
from tnfr.sdk import TNFRNetwork, NetworkConfig
20+
from tnfr.structural import create_nfr, run_sequence
21+
from tnfr.operators import apply_glyph
22+
from tnfr.operators.definitions import (
23+
Coupling, Resonance, Emission, Coherence, Reception
24+
)
25+
26+
27+
class TestCanonicalUMSequences:
28+
"""Test canonical UM sequences from TNFR theory."""
29+
30+
def test_um_ra_coupling_propagation(self):
31+
"""Test UM → RA sequence (coupling + propagation)."""
32+
# Create small network
33+
net = TNFRNetwork("um_ra_test", NetworkConfig(random_seed=42))
34+
net.add_nodes(8).connect_nodes(0.4, "random")
35+
36+
# Set varied phases
37+
for i, node in enumerate(net.graph.nodes()):
38+
net.graph.nodes[node]['theta'] = (i % 3) * (2 * math.pi / 3)
39+
40+
# Measure initial coherence
41+
results_before = net.measure()
42+
C_before = results_before.coherence
43+
44+
# Apply UM → RA sequence via network_sync
45+
net.apply_sequence("network_sync", repeat=1)
46+
47+
results_after = net.measure()
48+
C_after = results_after.coherence
49+
50+
# RA after UM should maintain or increase coherence
51+
# (coupling creates synchronized substrate, resonance propagates it)
52+
assert C_after >= C_before * 0.8, "UM → RA should maintain reasonable coherence"
53+
54+
def test_al_um_emission_coupling(self):
55+
"""Test AL → UM sequence (emission + coupling)."""
56+
# Create network
57+
G, node = create_nfr("test_node", vf=1.0, theta=0.0, epi=0.6)
58+
G.add_node("node2", theta=math.pi/4, EPI=0.6, vf=1.0, dnfr=0.3, Si=0.5)
59+
G.add_node("node3", theta=math.pi/2, EPI=0.6, vf=1.0, dnfr=0.3, Si=0.5)
60+
G.add_edge(node, "node2")
61+
G.add_edge(node, "node3")
62+
63+
# Record initial phase and EPI
64+
theta_before = G.nodes[node]['theta']
65+
epi_before = G.nodes[node].get('EPI', G.nodes[node].get('epi'))
66+
67+
# Apply AL → UM (via network_sync which includes AL, EN, IL, UM)
68+
apply_glyph(G, node, "AL") # Emission
69+
epi_after_emission = G.nodes[node].get('EPI', G.nodes[node].get('epi'))
70+
71+
apply_glyph(G, node, "UM") # Coupling
72+
73+
theta_after = G.nodes[node]['theta']
74+
epi_after_coupling = G.nodes[node].get('EPI', G.nodes[node].get('epi'))
75+
76+
# Phase should change (synchronization effect)
77+
# Emission may modify EPI, but Coupling should preserve it
78+
# Check that EPI didn't change significantly during coupling
79+
if isinstance(epi_after_emission, (int, float)) and isinstance(epi_after_coupling, (int, float)):
80+
assert epi_after_coupling == pytest.approx(epi_after_emission, abs=0.15), \
81+
"Coupling must preserve EPI (emission may change it)"
82+
83+
def test_um_il_coupling_stabilization(self):
84+
"""Test UM → IL sequence (coupling + stabilization)."""
85+
# Create ring network
86+
net = TNFRNetwork("um_il_test", NetworkConfig(random_seed=42))
87+
net.add_nodes(6).connect_nodes(0.5, "ring")
88+
89+
# Set moderate phase variation
90+
for i, node in enumerate(net.graph.nodes()):
91+
net.graph.nodes[node]['theta'] = i * math.pi / 6
92+
93+
results_before = net.measure()
94+
95+
# Apply sequence with UM (network_sync includes UM)
96+
net.apply_sequence("network_sync")
97+
results_after_um = net.measure()
98+
99+
# Apply stabilization sequence (includes coherence)
100+
net.apply_sequence("stabilization")
101+
results_after_stabilization = net.measure()
102+
103+
# Stabilization should maintain or improve coherence after network_sync
104+
assert results_after_stabilization.coherence >= results_after_um.coherence * 0.8, \
105+
"Stabilization should maintain coupling effects"
106+
107+
def test_network_sync_includes_um(self):
108+
"""Test that network_sync sequence properly includes and uses UM."""
109+
net = TNFRNetwork("network_sync_test", NetworkConfig(random_seed=42))
110+
net.add_nodes(10).connect_nodes(0.3, "random")
111+
112+
# Set distinct phase groups
113+
for i, node in enumerate(net.graph.nodes()):
114+
net.graph.nodes[node]['theta'] = (i % 4) * math.pi / 2
115+
116+
phases_before = [net.graph.nodes[n]['theta'] for n in net.graph.nodes()]
117+
phase_spread_before = max(phases_before) - min(phases_before)
118+
119+
# Apply network_sync (AL → EN → IL → UM → RA → NAV)
120+
net.apply_sequence("network_sync", repeat=2)
121+
122+
phases_after = [net.graph.nodes[n]['theta'] for n in net.graph.nodes()]
123+
phase_spread_after = max(phases_after) - min(phases_after)
124+
125+
# Phase spread should reduce (synchronization effect)
126+
assert phase_spread_after < phase_spread_before, \
127+
"network_sync should reduce phase spread via UM"
128+
129+
130+
class TestUMNetworkFormation:
131+
"""Test UM role in network formation from isolated or loosely connected nodes."""
132+
133+
def test_um_forms_coherent_network(self):
134+
"""Test that UM contributes to network formation."""
135+
# Start with isolated nodes
136+
net = TNFRNetwork("formation_test", NetworkConfig(random_seed=42))
137+
net.add_nodes(12)
138+
139+
# Varied initial phases
140+
for i, node in enumerate(net.graph.nodes()):
141+
net.graph.nodes[node]['theta'] = (i % 5) * (2 * math.pi / 5)
142+
143+
results_isolated = net.measure()
144+
145+
# Add connections
146+
net.connect_nodes(0.25, "random")
147+
148+
# Apply formation sequence with UM
149+
net.apply_sequence("network_sync", repeat=3)
150+
151+
results_formed = net.measure()
152+
153+
# Network should achieve reasonable coherence
154+
assert results_formed.coherence > 0.5, \
155+
"UM should contribute to network formation"
156+
157+
# Average sense index should be reasonable
158+
avg_si = sum(results_formed.sense_indices.values()) / len(results_formed.sense_indices)
159+
assert avg_si > 0.3, "Formed network should have reasonable stability"
160+
161+
def test_um_bridges_phase_groups(self):
162+
"""Test that UM can bridge incompatible phase groups."""
163+
net = TNFRNetwork("phase_bridging", NetworkConfig(random_seed=42))
164+
net.add_nodes(8)
165+
166+
# Create two opposing phase groups
167+
for i, node in enumerate(net.graph.nodes()):
168+
if i < 4:
169+
net.graph.nodes[node]['theta'] = 0.0
170+
else:
171+
net.graph.nodes[node]['theta'] = math.pi
172+
173+
# Connect the groups
174+
net.connect_nodes(0.3, "random")
175+
176+
phases_before = [net.graph.nodes[n]['theta'] for n in net.graph.nodes()]
177+
phase_spread_before = max(phases_before) - min(phases_before)
178+
179+
# Apply multiple rounds of network_sync (includes UM)
180+
for _ in range(3):
181+
net.apply_sequence("network_sync")
182+
183+
phases_after = [net.graph.nodes[n]['theta'] for n in net.graph.nodes()]
184+
phase_spread_after = max(phases_after) - min(phases_after)
185+
186+
# Phase spread should significantly reduce
187+
assert phase_spread_after < phase_spread_before * 0.5, \
188+
"UM should bridge opposing phase groups"
189+
190+
191+
class TestUMStructuralInvariants:
192+
"""Test that UM preserves TNFR structural invariants."""
193+
194+
def test_um_preserves_epi_identity(self):
195+
"""Test that UM synchronizes phase without modifying EPI."""
196+
G, node = create_nfr("test_node", vf=1.0, theta=0.5, epi=0.6)
197+
G.add_node("neighbor", theta=0.1, EPI=0.5, vf=1.0, dnfr=0.3, Si=0.5)
198+
G.add_edge(node, "neighbor")
199+
200+
epi_before = G.nodes[node].get('EPI', G.nodes[node].get('epi'))
201+
202+
# Apply UM multiple times
203+
for _ in range(3):
204+
apply_glyph(G, node, "UM")
205+
206+
epi_after = G.nodes[node].get('EPI', G.nodes[node].get('epi'))
207+
208+
# EPI should not be directly modified by UM
209+
# (small changes may occur due to natural evolution via nodal equation)
210+
assert epi_after == pytest.approx(epi_before, abs=0.15), \
211+
"UM must preserve EPI identity (critical invariant)"
212+
213+
def test_um_modifies_phase(self):
214+
"""Test that UM actually modifies phase (its primary function)."""
215+
G, node = create_nfr("test_node", vf=1.0, theta=0.8, epi=0.5)
216+
G.add_node("neighbor1", theta=0.1, EPI=0.5, vf=1.0, dnfr=0.3, Si=0.5)
217+
G.add_node("neighbor2", theta=0.15, EPI=0.5, vf=1.0, dnfr=0.3, Si=0.5)
218+
G.add_edge(node, "neighbor1")
219+
G.add_edge(node, "neighbor2")
220+
221+
theta_before = G.nodes[node]['theta']
222+
223+
# Apply UM
224+
apply_glyph(G, node, "UM")
225+
226+
theta_after = G.nodes[node]['theta']
227+
228+
# Phase should move towards neighbors (synchronization)
229+
assert abs(theta_after - theta_before) > 0.01, \
230+
"UM should synchronize phase"
231+
232+
def test_um_can_reduce_dnfr(self):
233+
"""Test that UM can reduce ΔNFR through synchronization."""
234+
G, node = create_nfr("test_node", vf=1.0, theta=0.5, epi=0.5)
235+
G.add_node("neighbor", theta=0.1, EPI=0.5, vf=1.0, dnfr=0.3, Si=0.5)
236+
G.add_edge(node, "neighbor")
237+
238+
# Set high initial ΔNFR
239+
G.nodes[node]["dnfr"] = 0.8
240+
G.graph["UM_STABILIZE_DNFR"] = True # Enable ΔNFR stabilization
241+
242+
dnfr_before = G.nodes[node]["dnfr"]
243+
244+
# Apply UM
245+
apply_glyph(G, node, "UM")
246+
247+
dnfr_after = G.nodes[node]["dnfr"]
248+
249+
# ΔNFR should reduce (stabilization effect)
250+
assert dnfr_after < dnfr_before, \
251+
"UM with stabilization should reduce ΔNFR"
252+
253+
254+
class TestUMTopologyEffects:
255+
"""Test UM behavior across different network topologies."""
256+
257+
@pytest.mark.parametrize("topology,edge_prob", [
258+
("random", 0.3),
259+
("ring", 0.5),
260+
("random", 0.5),
261+
])
262+
def test_um_across_topologies(self, topology, edge_prob):
263+
"""Test UM effectiveness across different topologies."""
264+
net = TNFRNetwork(f"topology_{topology}", NetworkConfig(random_seed=42))
265+
net.add_nodes(10).connect_nodes(edge_prob, topology)
266+
267+
# Varied initial phases
268+
for i, node in enumerate(net.graph.nodes()):
269+
net.graph.nodes[node]['theta'] = (i % 3) * (2 * math.pi / 3)
270+
271+
results_before = net.measure()
272+
273+
# Apply network_sync (includes UM)
274+
net.apply_sequence("network_sync", repeat=2)
275+
276+
results_after = net.measure()
277+
278+
# Should achieve reasonable coherence in any topology
279+
assert results_after.coherence > 0.5, \
280+
f"UM should work in {topology} topology"
281+
282+
def test_um_with_disconnected_components(self):
283+
"""Test UM behavior with disconnected network components."""
284+
net = TNFRNetwork("disconnected", NetworkConfig(random_seed=42))
285+
net.add_nodes(12)
286+
287+
# Create two disconnected components
288+
nodes = list(net.graph.nodes())
289+
for i in range(5):
290+
net.graph.add_edge(nodes[i], nodes[(i+1) % 6])
291+
for i in range(6, 11):
292+
net.graph.add_edge(nodes[i], nodes[6 + (i-6+1) % 6])
293+
294+
# Different phases in each component
295+
for i, node in enumerate(net.graph.nodes()):
296+
if i < 6:
297+
net.graph.nodes[node]['theta'] = 0.0
298+
else:
299+
net.graph.nodes[node]['theta'] = math.pi
300+
301+
# Apply network_sync
302+
net.apply_sequence("network_sync", repeat=2)
303+
304+
results = net.measure()
305+
306+
# Each component should synchronize internally
307+
# (even if components remain desynchronized from each other)
308+
assert results.coherence > 0.4, \
309+
"UM should synchronize within connected components"
310+
311+
312+
class TestUMMetricsAndTracking:
313+
"""Test that UM effects are properly captured in metrics."""
314+
315+
def test_um_phase_convergence_tracked(self):
316+
"""Test that phase convergence from UM is observable."""
317+
net = TNFRNetwork("metrics_test", NetworkConfig(random_seed=42))
318+
net.add_nodes(8).connect_nodes(0.4, "random")
319+
320+
# Set varied phases
321+
for i, node in enumerate(net.graph.nodes()):
322+
net.graph.nodes[node]['theta'] = (i % 4) * math.pi / 2
323+
324+
# Track phase spread over multiple applications
325+
phase_spreads = []
326+
327+
for i in range(4):
328+
phases = [net.graph.nodes[n]['theta'] for n in net.graph.nodes()]
329+
phase_spreads.append(max(phases) - min(phases))
330+
net.apply_sequence("network_sync")
331+
332+
# Phase spread should show convergence trend
333+
# (may not be strictly monotonic due to other operators)
334+
assert phase_spreads[-1] < phase_spreads[0] * 0.8, \
335+
"Phase spread should reduce over time"
336+
337+
def test_um_coherence_contribution(self):
338+
"""Test that UM contributes to coherence increase."""
339+
net = TNFRNetwork("coherence_test", NetworkConfig(random_seed=42))
340+
net.add_nodes(10).connect_nodes(0.3, "random")
341+
342+
# Moderate initial coherence
343+
results_before = net.measure()
344+
345+
# Apply sequence with UM
346+
net.apply_sequence("network_sync", repeat=3)
347+
348+
results_after = net.measure()
349+
350+
# Coherence should improve or remain stable
351+
assert results_after.coherence >= results_before.coherence * 0.7, \
352+
"UM should contribute to coherence stability"
353+
354+
355+
if __name__ == "__main__":
356+
pytest.main([__file__, "-v"])

0 commit comments

Comments
 (0)