Skip to content

Commit a8eb972

Browse files
authored
Merge pull request #2843 from fermga/copilot/enrich-telemetry-metrics
[VAL] Enhanced telemetry metrics with canonical structural indicators
2 parents bf3595d + 7724d78 commit a8eb972

File tree

4 files changed

+863
-92
lines changed

4 files changed

+863
-92
lines changed
Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
"""Demo of enhanced VAL (Expansion) metrics (Issue #2724).
2+
3+
This script demonstrates the comprehensive telemetry metrics for the VAL
4+
operator including bifurcation risk, coherence preservation, fractality
5+
indicators, network impact, and structural stability.
6+
"""
7+
8+
from tnfr.alias import set_attr
9+
from tnfr.constants.aliases import ALIAS_DNFR, ALIAS_D2EPI, ALIAS_THETA
10+
from tnfr.operators.definitions import Expansion
11+
from tnfr.structural import create_nfr
12+
13+
14+
def print_metrics_section(title: str, metrics: dict, keys: list[str]) -> None:
15+
"""Print a section of metrics."""
16+
print(f"\n{title}")
17+
print("=" * len(title))
18+
for key in keys:
19+
if key in metrics:
20+
value = metrics[key]
21+
if isinstance(value, float):
22+
print(f" {key}: {value:.4f}")
23+
elif isinstance(value, bool):
24+
print(f" {key}: {'✓' if value else '✗'}")
25+
else:
26+
print(f" {key}: {value}")
27+
28+
29+
def demo_healthy_expansion():
30+
"""Demonstrate healthy expansion with all indicators positive."""
31+
print("\n" + "=" * 60)
32+
print("SCENARIO 1: Healthy Expansion")
33+
print("=" * 60)
34+
35+
G, node = create_nfr("healthy_node", epi=0.4, vf=1.0)
36+
37+
# Add network context
38+
for i in range(3):
39+
neighbor = f"neighbor_{i}"
40+
G.add_node(neighbor)
41+
set_attr(G.nodes[neighbor], ALIAS_THETA, 0.3 + i * 0.1)
42+
G.add_edge(node, neighbor)
43+
44+
# Set healthy conditions
45+
G.graph["COLLECT_OPERATOR_METRICS"] = True
46+
set_attr(G.nodes[node], ALIAS_DNFR, 0.2) # Positive, stable
47+
set_attr(G.nodes[node], ALIAS_D2EPI, 0.1) # Below threshold
48+
set_attr(G.nodes[node], ALIAS_THETA, 0.35) # Aligned with neighbors
49+
50+
# Apply expansion
51+
Expansion()(G, node)
52+
53+
# Get metrics
54+
metrics = G.graph["operator_metrics"][-1]
55+
56+
# Display metrics
57+
print_metrics_section(
58+
"Core Metrics",
59+
metrics,
60+
["vf_increase", "delta_epi", "expansion_factor"],
61+
)
62+
63+
print_metrics_section(
64+
"Structural Stability",
65+
metrics,
66+
["dnfr_final", "dnfr_positive", "dnfr_stable"],
67+
)
68+
69+
print_metrics_section(
70+
"Bifurcation Risk",
71+
metrics,
72+
["d2epi", "bifurcation_risk", "bifurcation_magnitude"],
73+
)
74+
75+
print_metrics_section(
76+
"Coherence",
77+
metrics,
78+
["coherence_local", "coherence_preserved"],
79+
)
80+
81+
print_metrics_section(
82+
"Fractality",
83+
metrics,
84+
["epi_growth_rate", "vf_growth_rate", "growth_ratio", "fractal_preserved"],
85+
)
86+
87+
print_metrics_section(
88+
"Network Impact",
89+
metrics,
90+
["neighbor_count", "phase_coherence_neighbors", "network_coupled"],
91+
)
92+
93+
print_metrics_section(
94+
"Overall Health",
95+
metrics,
96+
["expansion_healthy"],
97+
)
98+
99+
100+
def demo_bifurcation_risk():
101+
"""Demonstrate expansion at bifurcation threshold."""
102+
print("\n" + "=" * 60)
103+
print("SCENARIO 2: Expansion at Bifurcation Threshold")
104+
print("=" * 60)
105+
106+
G, node = create_nfr("bifurcating_node", epi=0.4, vf=1.0)
107+
108+
# Set conditions for bifurcation risk
109+
G.graph["COLLECT_OPERATOR_METRICS"] = True
110+
G.graph["VAL_BIFURCATION_THRESHOLD"] = 0.3
111+
set_attr(G.nodes[node], ALIAS_DNFR, 0.2)
112+
set_attr(G.nodes[node], ALIAS_D2EPI, 0.5) # HIGH - above threshold
113+
114+
# Apply expansion
115+
Expansion()(G, node)
116+
117+
# Get metrics
118+
metrics = G.graph["operator_metrics"][-1]
119+
120+
print_metrics_section(
121+
"Bifurcation Indicators",
122+
metrics,
123+
[
124+
"d2epi",
125+
"bifurcation_threshold",
126+
"bifurcation_risk",
127+
"bifurcation_magnitude",
128+
],
129+
)
130+
131+
print_metrics_section(
132+
"Overall Health",
133+
metrics,
134+
["expansion_healthy"],
135+
)
136+
137+
if metrics["bifurcation_risk"]:
138+
print("\n⚠️ WARNING: Bifurcation risk detected!")
139+
print(
140+
f" d²EPI/dt² = {metrics['d2epi']:.3f} > threshold = {metrics['bifurcation_threshold']:.3f}"
141+
)
142+
print(" Consider applying IL (Coherence) or THOL (Self-organization)")
143+
144+
145+
def demo_coherence_degradation():
146+
"""Demonstrate expansion with coherence concerns."""
147+
print("\n" + "=" * 60)
148+
print("SCENARIO 3: Expansion with Low Coherence")
149+
print("=" * 60)
150+
151+
G, node = create_nfr("low_coherence_node", epi=0.3, vf=1.0)
152+
153+
# Set conditions
154+
G.graph["COLLECT_OPERATOR_METRICS"] = True
155+
G.graph["VAL_MIN_COHERENCE"] = 0.5
156+
set_attr(G.nodes[node], ALIAS_DNFR, 0.2)
157+
set_attr(G.nodes[node], ALIAS_D2EPI, 0.1)
158+
159+
# Apply expansion
160+
Expansion()(G, node)
161+
162+
# Get metrics
163+
metrics = G.graph["operator_metrics"][-1]
164+
165+
print_metrics_section(
166+
"Coherence Metrics",
167+
metrics,
168+
["coherence_local", "coherence_preserved"],
169+
)
170+
171+
print_metrics_section(
172+
"Overall Health",
173+
metrics,
174+
["expansion_healthy"],
175+
)
176+
177+
if not metrics.get("coherence_preserved", True):
178+
print("\n⚠️ WARNING: Coherence below threshold!")
179+
print(f" C_local = {metrics['coherence_local']:.3f}")
180+
print(" Consider applying IL (Coherence) to stabilize")
181+
182+
183+
def demo_network_impact():
184+
"""Demonstrate network coupling analysis."""
185+
print("\n" + "=" * 60)
186+
print("SCENARIO 4: Network Coupling Analysis")
187+
print("=" * 60)
188+
189+
G, node = create_nfr("network_node", epi=0.4, vf=1.0)
190+
191+
# Add neighbors with varying phase alignment
192+
neighbors_info = [
193+
("aligned_1", 0.50),
194+
("aligned_2", 0.52),
195+
("misaligned", 2.0), # Far from node's phase
196+
]
197+
198+
for neighbor, phase in neighbors_info:
199+
G.add_node(neighbor)
200+
set_attr(G.nodes[neighbor], ALIAS_THETA, phase)
201+
G.add_edge(node, neighbor)
202+
203+
# Set conditions
204+
G.graph["COLLECT_OPERATOR_METRICS"] = True
205+
set_attr(G.nodes[node], ALIAS_DNFR, 0.2)
206+
set_attr(G.nodes[node], ALIAS_D2EPI, 0.1)
207+
set_attr(G.nodes[node], ALIAS_THETA, 0.51) # Mostly aligned
208+
209+
# Apply expansion
210+
Expansion()(G, node)
211+
212+
# Get metrics
213+
metrics = G.graph["operator_metrics"][-1]
214+
215+
print_metrics_section(
216+
"Network Metrics",
217+
metrics,
218+
[
219+
"neighbor_count",
220+
"phase_coherence_neighbors",
221+
"network_coupled",
222+
"theta_final",
223+
],
224+
)
225+
226+
print("\nNetwork Analysis:")
227+
if metrics["network_coupled"]:
228+
print(" ✓ Node is well-coupled to network")
229+
else:
230+
print(" ✗ Node has weak network coupling")
231+
232+
print(
233+
f" Phase coherence: {metrics['phase_coherence_neighbors']:.2%} "
234+
f"({metrics['neighbor_count']} neighbors)"
235+
)
236+
237+
238+
def main():
239+
"""Run all demonstration scenarios."""
240+
print("\n" + "=" * 60)
241+
print("Enhanced VAL (Expansion) Metrics Demo")
242+
print("Issue #2724: Canonical Structural Indicators")
243+
print("=" * 60)
244+
245+
demo_healthy_expansion()
246+
demo_bifurcation_risk()
247+
demo_coherence_degradation()
248+
demo_network_impact()
249+
250+
print("\n" + "=" * 60)
251+
print("Demo Complete!")
252+
print("=" * 60)
253+
print(
254+
"\nThese metrics enable:\n"
255+
" 1. Early detection of bifurcation events\n"
256+
" 2. Validation of coherence preservation\n"
257+
" 3. Analysis of fractal self-similarity\n"
258+
" 4. Monitoring of network coupling\n"
259+
" 5. Overall structural health assessment"
260+
)
261+
262+
263+
if __name__ == "__main__":
264+
main()

src/tnfr/config/defaults_core.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,12 @@ class CoreDefaults:
161161
VAL_CHECK_NETWORK_CAPACITY: bool = False # Optional network capacity validation
162162
VAL_MAX_NETWORK_SIZE: int = 1000 # Maximum network size if capacity checking enabled
163163

164+
# VAL (Expansion) metric thresholds (Issue #2724)
165+
VAL_BIFURCATION_THRESHOLD: float = 0.3 # Threshold for |∂²EPI/∂t²| bifurcation detection
166+
VAL_MIN_COHERENCE: float = 0.5 # Minimum local coherence for healthy expansion
167+
VAL_FRACTAL_RATIO_MIN: float = 0.5 # Minimum vf_growth/epi_growth ratio for fractality
168+
VAL_FRACTAL_RATIO_MAX: float = 2.0 # Maximum vf_growth/epi_growth ratio for fractality
169+
164170

165171
@dataclass(frozen=True, slots=True)
166172
class RemeshDefaults:

0 commit comments

Comments
 (0)