Repository navigation
Expand file tree
/
Copy pathtest_phase_contrast_coordination_scope.py
More file actions
203 lines (179 loc) · 8.72 KB
/
Copy pathtest_phase_contrast_coordination_scope.py
File metadata and controls
203 lines (179 loc) · 8.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
"""Scope of phase coordination and the separately supplied sine phase law.
Ideal algebra, finite coordinator invocations and the separately configured
free-advance proposal have distinct claims. These controls neither select a
primitive phase law nor certify a repeated full-runtime trajectory.
"""
import math
import networkx as nx
import pytest
from tests.joint_phase_helpers import configure
from tests.physics._internal_mode_fixture import NODES, _graph
from tnfr.alias import get_attr, get_theta_attr
from tnfr.constants.aliases import ALIAS_DNFR
from tnfr.dynamics.coordination import coordinate_global_local_phase
from tnfr.dynamics.dnfr import default_compute_delta_nfr
from tnfr.dynamics.phase_evolution import propose_u3_gated_phase_step
def _repeated_triple():
"""A nonzero phasor-mean correction inside a chart centered on pi."""
graph = _graph()
eta = (math.pi / 12, math.pi / 12, -math.pi / 6)
for node in NODES:
graph.nodes[node]["theta"] = math.pi + eta[node[1]]
# Every fine node sees exactly one representative of each phase.
assert sorted(other[1] for other in graph.neighbors(node)) == [0, 1, 2]
# Independent closed radical expression for Arg(2 exp(i*pi/12)+exp(-i*pi/6)).
delta = math.atan(
(math.sqrt(6) - math.sqrt(2) - 1) / (math.sqrt(6) + math.sqrt(2) + math.sqrt(3))
)
assert 0 < delta < math.pi / 12
return graph, eta, delta
def test_repeated_mean_relaxation_has_only_a_scalar_contrast_map():
s = pytest.importorskip("sympy")
a, b, beta, delta = s.symbols("a b beta delta", real=True)
eta = s.Matrix([a + b, -a + b, -2 * b])
ones = s.ones(3, 1)
theta = beta * ones + eta
# The repeated triple gives identical local/global phasor targets beta+delta.
# delta=pi*c may be nonzero: keeping beta fixed would lose the mean update.
for gain in (s.Rational(1, 2), s.Integer(1), s.Rational(3, 2)):
proposal = theta + gain * ((beta + delta) * ones - theta)
mean = s.simplify(sum(proposal) / 3)
contrast = s.simplify(proposal - mean * ones)
assert mean == beta + gain * delta
assert contrast == (1 - gain) * eta
assert s.simplify(eta.cross(contrast)) == s.zeros(3, 1)
# Changing scalar gains through history does not add a transverse direction,
# conditional on this same repeated regular chart at each invocation.
gain_1, gain_2 = s.symbols("gain_1 gain_2", real=True)
twice = (1 - gain_2) * ((1 - gain_1) * eta)
assert s.simplify(twice - (1 - gain_1) * (1 - gain_2) * eta) == s.zeros(3, 1)
assert s.simplify(eta.cross(twice)) == s.zeros(3, 1)
@pytest.mark.parametrize(
("global_gain", "local_gain"),
((0.5, 0.0), (0.0, 0.5), (0.25, 0.25), (0.5, 0.5), (0.75, 0.75)),
)
def test_actual_coordinator_retains_the_mean_and_scalar_contrast_with_rounding(
global_gain, local_gain
):
graph, eta, delta = _repeated_triple()
held = tuple(
(graph.nodes[node]["EPI"], graph.nodes[node]["nu_f"]) for node in NODES
)
gain = global_gain + local_gain
assert (
coordinate_global_local_phase(
graph, global_force=global_gain, local_force=local_gain, n_jobs=1
)
is None
)
actual = tuple(get_theta_attr(graph.nodes[node]) for node in NODES)
expected_mean = math.pi + gain * delta
expected = tuple(expected_mean + (1 - gain) * eta[node[1]] for node in NODES)
# Local/global trig reductions and phase writes are separately rounded.
assert actual == pytest.approx(expected, abs=4e-15, rel=0)
for fiber in (actual[:3], actual[3:]):
mean = math.fsum(fiber) / 3
assert mean == pytest.approx(expected_mean, abs=4e-15, rel=0)
assert mean > math.pi
assert tuple(value - mean for value in fiber) == pytest.approx(
tuple((1 - gain) * value for value in eta), abs=4e-15, rel=0
)
assert (
tuple((graph.nodes[node]["EPI"], graph.nodes[node]["nu_f"]) for node in NODES)
== held
)
def test_uniform_free_advance_moves_the_mean_while_sine_coupling_dissipates_contrast():
s = pytest.importorskip("sympy")
eta_exact = (s.pi / 12, s.pi / 12, -s.pi / 6)
strength = s.Rational(1, 4)
rate = tuple(
s.simplify(strength * sum(s.sin(other - value) for other in eta_exact) / 3)
for value in eta_exact
)
assert sum(rate) == 0
variance_rate = s.simplify(2 * sum(x * y for x, y in zip(eta_exact, rate)))
pair_budget = s.simplify(
-2
* strength
/ 3
* sum(
(eta_exact[i] - eta_exact[j]) * s.sin(eta_exact[i] - eta_exact[j])
for i in range(3)
for j in range(i + 1, 3)
)
)
assert variance_rate == pair_budget == -s.sqrt(2) * s.pi / 24
# For a regular triple every pair term is nonnegative. This is the
# continuous sine-model comparison, not a claim for arbitrary Euler dt.
graph, _, _ = _repeated_triple()
phases = tuple(get_theta_attr(graph.nodes[node]) for node in NODES)
dt = 0.125
without_free = propose_u3_gated_phase_step(
graph, NODES, phases, (0.0,) * 6, dt=dt, coupling_strength=float(strength)
)
with_free = propose_u3_gated_phase_step(
graph, NODES, phases, (1.0,) * 6, dt=dt, coupling_strength=float(strength)
)
expected_without = tuple(
phases[index] + dt * float(rate[node[1]]) for index, node in enumerate(NODES)
)
assert tuple(without_free) == pytest.approx(expected_without, abs=2e-15, rel=0)
assert tuple(with_free - without_free) == pytest.approx((dt,) * 6, abs=2e-15, rel=0)
for proposed in (without_free, with_free):
mean = math.fsum(proposed[:3]) / 3
contrast = tuple(value - mean for value in proposed[:3])
expected_contrast = tuple(
float(x) + dt * float(y) for x, y in zip(eta_exact, rate)
)
assert contrast == pytest.approx(expected_contrast, abs=2e-15, rel=0)
# The proposal reads declared frequency vectors; it does not mutate a graph
# or derive this angular-frequency identification from the EPI equation.
assert tuple(get_theta_attr(graph.nodes[node]) for node in NODES) == phases
def _phase_pressure_pair(gap):
"""Keep the shared equal EPI/phase mixture on supplied unit P2 support."""
graph = nx.path_graph(2)
configure(graph)
graph.edges[0, 1].update(weight=1.0, length=1.0)
for node, phase in enumerate((0.0, gap)):
graph.nodes[node].update(EPI=0.0, nu_f=1.0, theta=phase)
default_compute_delta_nfr(graph)
return graph
def test_sine_phase_proposal_does_not_receive_the_changed_form_pressure():
graph = _phase_pressure_pair(math.pi / 3)
nodes = tuple(graph)
phases = tuple(get_theta_attr(graph.nodes[node]) for node in nodes)
pressure_before = tuple(get_attr(graph.nodes[node], ALIAS_DNFR) for node in nodes)
before = propose_u3_gated_phase_step(
graph, nodes, phases, (1.0, 1.0), dt=1 / 8, coupling_strength=1 / 4
)
# The independent P2 mixture is (1/2)*((x_j-x_i) +/- 1/3).
# Refresh actual production pressure after changing only the form state.
graph.nodes[0]["EPI"], graph.nodes[1]["EPI"] = 1 / 4, -1 / 4
default_compute_delta_nfr(graph)
pressure_after = tuple(get_attr(graph.nodes[node], ALIAS_DNFR) for node in nodes)
after = propose_u3_gated_phase_step(
graph, nodes, phases, (1.0, 1.0), dt=1 / 8, coupling_strength=1 / 4
)
assert pressure_before == pytest.approx((1 / 6, -1 / 6), abs=2e-15, rel=0)
assert pressure_after == pytest.approx((-1 / 12, 1 / 12), abs=2e-15, rel=0)
# sin(pi/3)=sqrt(3)/2 supplies an independent active-coupling oracle.
expected = (1 / 8 + math.sqrt(3) / 64, math.pi / 3 + 1 / 8 - math.sqrt(3) / 64)
assert tuple(before) == pytest.approx(expected, abs=2e-15, rel=0)
assert tuple(after) == tuple(before)
assert tuple(get_theta_attr(graph.nodes[node]) for node in nodes) == phases
def test_u3_exclusion_removes_phase_interaction_but_not_full_support_pressure():
graph = _phase_pressure_pair(3 * math.pi / 4)
nodes = tuple(graph)
phases = tuple(get_theta_attr(graph.nodes[node]) for node in nodes)
pressure = tuple(get_attr(graph.nodes[node], ALIAS_DNFR) for node in nodes)
proposal = propose_u3_gated_phase_step(
graph, nodes, phases, (1.0, 1.0), dt=1 / 8, coupling_strength=1 / 4
)
# Arg of the sole neighbor gives +/-3/4; the equal mixture halves it.
# The nonzero nodal pressure survives, although the phase law uses no edge.
assert pressure == pytest.approx((3 / 8, -3 / 8), abs=2e-15, rel=0)
assert tuple(proposal) == pytest.approx(
(1 / 8, 3 * math.pi / 4 + 1 / 8), abs=2e-15, rel=0
)
assert proposal[1] - proposal[0] == pytest.approx(phases[1], abs=2e-15, rel=0)
assert tuple(get_theta_attr(graph.nodes[node]) for node in nodes) == phases