Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 161 additions & 3 deletions docs/THOL_ENCAPSULATION_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,164 @@ Ensure the added operator makes **semantic sense** for your application:

---

## Phase Compatibility in Propagation

**CRITICAL**: THOL propagation respects **AGENTS.md Invariant #5**: "No coupling is valid without explicit phase verification."

### Physical Basis

Sub-EPI propagation follows resonance physics, not arbitrary connectivity. Antiphase nodes (Δθ ≈ π) create **destructive interference**, preventing coherent propagation regardless of network topology.

### Implementation

From `src/tnfr/operators/metabolism.py::propagate_subepi_to_network()`:

```python
# Compute coupling strength (phase alignment)
phase_diff = abs(angle_diff(neighbor_theta, parent_theta))
coupling_strength = 1.0 - (phase_diff / math.pi)

# Propagate only if sufficiently coupled
if coupling_strength >= min_coupling_strength:
# Attenuate and inject sub-EPI
attenuated_epi = sub_epi_magnitude * attenuation * coupling_strength
# ...
```

**Formula**:
```
coupling_strength = 1.0 - (|Δθ| / π)
```

Where:
- `Δθ` = phase difference between parent and neighbor (radians)
- `coupling_strength` ∈ [0, 1]
- Propagation occurs only if `coupling_strength ≥ threshold` (default: 0.5)

### Phase Compatibility Table

| Phase Difference (Δθ) | Coupling Strength | Propagates? (threshold=0.5) | Physics |
|----------------------|-------------------|---------------------------|---------|
| 0 (in-phase) | 1.0 | ✅ Yes | Perfect resonance |
| π/4 (45°) | 0.75 | ✅ Yes | Strong coupling |
| π/2 (90°) | 0.5 | ✅ Yes (at threshold) | Moderate coupling |
| 3π/4 (135°) | 0.25 | ❌ No | Weak coupling |
| π (antiphase) | 0.0 | ❌ No | Destructive interference |

### Canonical Constraints

**From AGENTS.md Invariant #5**:

Sub-EPIs propagate **ONLY** to neighbors with:
1. **Phase compatibility**: `|Δθ| ≤ Δθ_max` (typically π/2)
2. **Coupling threshold**: `coupling_strength ≥ threshold` (default: 0.5)
3. **Explicit verification**: Phase difference computed before propagation

**Why This Matters**:

- **TNFR Physics**: Resonance requires phase alignment, not just network edges
- **Prevents Chaos**: Antiphase propagation would create destructive interference
- **Canonical Compliance**: Same phase verification as UM (Coupling) and RA (Resonance)

### Example: Phase Barrier Blocking Cascade

```python
import math
import networkx as nx
from tnfr.operators.definitions import SelfOrganization
from tnfr.constants import EPI_PRIMARY, THETA_PRIMARY, VF_PRIMARY, DNFR_PRIMARY

# Create network with phase barrier
G = nx.Graph()

# Cluster A: coherent phases
G.add_node(0, **{EPI_PRIMARY: 0.50, THETA_PRIMARY: 0.1, VF_PRIMARY: 1.0, DNFR_PRIMARY: 0.10})
G.add_node(1, **{EPI_PRIMARY: 0.45, THETA_PRIMARY: 0.12, VF_PRIMARY: 1.0, DNFR_PRIMARY: 0.10})

# Node 2: phase barrier (antiphase)
G.add_node(2, **{EPI_PRIMARY: 0.45, THETA_PRIMARY: math.pi, VF_PRIMARY: 1.0, DNFR_PRIMARY: 0.10})

# Cluster B: isolated by barrier
G.add_node(3, **{EPI_PRIMARY: 0.45, THETA_PRIMARY: math.pi + 0.1, VF_PRIMARY: 1.0, DNFR_PRIMARY: 0.10})

# Connect: 0-1-2-3
G.add_edges_from([(0, 1), (1, 2), (2, 3)])

# Enable propagation
G.graph["THOL_PROPAGATION_ENABLED"] = True
G.nodes[0]["epi_history"] = [0.05, 0.33, 0.50] # Bifurcation conditions

# Trigger cascade from node 0
SelfOrganization()(G, 0)

# Result: Propagation reaches node 1, stops at phase barrier (node 2)
# Node 3 is NOT affected due to phase incompatibility
```

### Testing Phase Verification

From `tests/integration/test_thol_propagation.py`:

```python
def test_thol_rejects_antiphase_propagation():
"""THOL must reject propagation to antiphase neighbors (Invariant #5)."""
import math

G = nx.Graph()
G.add_node(0, epi=0.50, vf=1.0, theta=0.0, delta_nfr=0.15)
G.add_node(1, epi=0.50, vf=1.0, theta=math.pi, delta_nfr=0.05) # Antiphase
Comment on lines +685 to +686
Copy link

Copilot AI Nov 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation example uses invalid attribute aliases that will not work with the TNFR alias system:

  • epi should be EPI (or valid aliases: psi, PSI, value)
  • vf should be νf (or valid aliases: nu_f, nu-f, nu, freq, frequency)

The aliases theta and delta_nfr are valid.

According to src/tnfr/config/tnfr_config.py, the ALIASES dictionary defines:

  • "EPI": ("EPI", "psi", "PSI", "value") - lowercase epi is not included
  • "VF": ("νf", "nu_f", "nu-f", "nu", "freq", "frequency") - lowercase vf is not included

The actual test in tests/integration/test_thol_propagation.py correctly uses the constants EPI_PRIMARY, VF_PRIMARY, THETA_PRIMARY, and DNFR_PRIMARY.

Suggested change
G.add_node(0, epi=0.50, vf=1.0, theta=0.0, delta_nfr=0.15)
G.add_node(1, epi=0.50, vf=1.0, theta=math.pi, delta_nfr=0.05) # Antiphase
G.add_node(0, EPI=0.50, νf=1.0, theta=0.0, delta_nfr=0.15)
G.add_node(1, EPI=0.50, νf=1.0, theta=math.pi, delta_nfr=0.05) # Antiphase

Copilot uses AI. Check for mistakes.
G.add_edge(0, 1)

G.graph["THOL_PROPAGATION_ENABLED"] = True
G.nodes[0]["epi_history"] = [0.05, 0.33, 0.50]

epi_1_before = G.nodes[1]["epi"]
SelfOrganization()(G, 0)
epi_1_after = G.nodes[1]["epi"]

# Antiphase neighbor should be rejected
assert epi_1_after == epi_1_before, "Invariant #5 violation"
```

### Configuration

Control phase verification via graph parameters:

```python
# Strict phase compatibility (default)
G.graph["THOL_MIN_COUPLING_FOR_PROPAGATION"] = 0.5 # Δθ ≤ π/2

# Relaxed (allow weaker coupling)
G.graph["THOL_MIN_COUPLING_FOR_PROPAGATION"] = 0.3 # Δθ ≤ ~2.2 rad

# Very strict (only near-in-phase)
G.graph["THOL_MIN_COUPLING_FOR_PROPAGATION"] = 0.8 # Δθ ≤ ~0.6 rad
```

**Canonical Default**: 0.5 (π/2 maximum phase difference)

### Cross-Operator Consistency

THOL phase verification is **consistent** with other TNFR operators:

| Operator | Phase Verification | Formula | Use Case |
|----------|-------------------|---------|----------|
| **UM** (Coupling) | Consensus phase | `arctan2(Σsin(θ), Σcos(θ))` | Bidirectional synchronization |
| **RA** (Resonance) | Circular mean | `\|⟨e^(iθ)⟩\|` (Kuramoto) | Network-wide propagation |
| **THOL** (Propagation) | Direct difference | `1.0 - (\|Δθ\| / π)` | Unidirectional sub-EPI transfer |

All three enforce phase compatibility before structural modifications, ensuring resonance physics integrity.

### References

- **AGENTS.md**: Invariant #5 (Phase Verification)
- **UNIFIED_GRAMMAR_RULES.md**: U3 (Resonant Coupling)
- **src/tnfr/operators/metabolism.py**: Lines 284-331 (propagate_subepi_to_network)
Copy link

Copilot AI Nov 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The line reference for propagate_subepi_to_network is inaccurate. The documentation states "Lines 284-331" but:

  • The complete function definition spans lines 229-340
  • Line 284 is within the docstring examples section, not the implementation
  • The phase verification logic being referenced is at lines 306-319

Consider updating the reference to either:

  • "Lines 229-340 (propagate_subepi_to_network)" for the complete function
  • "Lines 306-319 (phase verification in propagate_subepi_to_network)" for the specific phase coupling logic
Suggested change
- **src/tnfr/operators/metabolism.py**: Lines 284-331 (propagate_subepi_to_network)
- **src/tnfr/operators/metabolism.py**: Lines 306-319 (phase verification in propagate_subepi_to_network)

Copilot uses AI. Check for mistakes.
- **src/tnfr/utils/numeric.py**: Lines 72-75 (angle_diff utility)
- **tests/integration/test_thol_propagation.py**: Lines 220-290 (test_thol_rejects_antiphase_propagation)
Copy link

Copilot AI Nov 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The line reference for the test is inaccurate. The documentation states "Lines 220-290" but the actual test test_thol_rejects_antiphase_propagation spans lines 218-283.

The test definition starts at line 218 and the last assertion ends at line 283. Line 284 is blank, and line 285 starts the next class.

Consider updating to: "Lines 218-283 (test_thol_rejects_antiphase_propagation)"

Suggested change
- **tests/integration/test_thol_propagation.py**: Lines 220-290 (test_thol_rejects_antiphase_propagation)
- **tests/integration/test_thol_propagation.py**: Lines 218-283 (test_thol_rejects_antiphase_propagation)

Copilot uses AI. Check for mistakes.

---

## Additional Resources

- **[THOL_CONFIGURATION_REFERENCE.md](THOL_CONFIGURATION_REFERENCE.md)**: Complete THOL parameter reference with canonical constraints
Expand All @@ -589,6 +747,6 @@ Ensure the added operator makes **semantic sense** for your application:

---

*Last updated: 2025-11-08*
*Version: 1.0.0 (Initial release with Grammar 2.0)*
*Related PR: #[PR_NUMBER] - Recursive THOL validation with encapsulation*
*Last updated: 2025-11-09*
*Version: 1.1.0 (Added Phase Compatibility section)*
*Related PR: Verify phase validation in THOL sub-EPI propagation (Invariant #5)*
17 changes: 13 additions & 4 deletions src/tnfr/operators/metabolism.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,12 @@ def propagate_subepi_to_network(
TNFR Principle: "Sub-EPIs propagate to coupled neighbors, triggering their
own bifurcations when ∂²EPI/∂t² > τ" (canonical THOL dynamics).

**AGENTS.md Invariant #5**: No coupling is valid without explicit phase
verification. This function enforces phase compatibility before propagation:
- Computes coupling_strength = 1.0 - (|Δθ| / π) using angle_diff()
- Rejects neighbors with coupling_strength < threshold (antiphase blocked)
- Ensures resonance physics: only phase-aligned nodes receive sub-EPIs

Propagation mechanism:
1. Select neighbors with sufficient coupling (phase alignment)
2. Compute attenuation based on coupling strength
Expand Down Expand Up @@ -298,11 +304,14 @@ def propagate_subepi_to_network(
for neighbor in neighbors:
neighbor_theta = float(get_attr(G.nodes[neighbor], ALIAS_THETA, 0.0))

# Compute coupling strength using canonical phase compatibility formula
# (unified across UM, RA, THOL operators - see phase_compatibility module)
coupling_strength = compute_phase_coupling_strength(parent_theta, neighbor_theta)
# INVARIANT #5: Phase verification before coupling
# Compute coupling strength based on phase alignment
# coupling_strength ∈ [0, 1]: 1 = in-phase, 0 = antiphase
phase_diff = abs(angle_diff(neighbor_theta, parent_theta))
coupling_strength = 1.0 - (phase_diff / math.pi)

# Propagate only if sufficiently coupled
# Propagate only if sufficiently coupled (phase-aligned)
# Antiphase neighbors (Δθ ≈ π) have coupling_strength ≈ 0, blocked by threshold
if coupling_strength >= min_coupling_strength:
# Attenuate sub-EPI based on distance and coupling
attenuated_epi = sub_epi_magnitude * attenuation_factor * coupling_strength
Expand Down
66 changes: 66 additions & 0 deletions tests/integration/test_thol_propagation.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,72 @@ def test_thol_propagation_can_be_disabled(self):
propagations = G.graph.get("thol_propagations", [])
assert len(propagations) == 0, "No propagations should be recorded"

def test_thol_rejects_antiphase_propagation(self):
"""THOL must reject propagation to neighbors with antiphase (Invariant #5).

This test explicitly validates AGENTS.md Invariant #5:
"No coupling is valid without explicit phase verification."

THOL propagation uses phase-based coupling strength:
coupling_strength = 1.0 - (|Δθ| / π)

For antiphase nodes (Δθ = π), coupling_strength = 0, which is below
the minimum threshold, thus blocking propagation as required by physics.
"""
import math

G = nx.Graph()
# Node 0: phase = 0.0
G.add_node(
0,
**{
EPI_PRIMARY: 0.50,
VF_PRIMARY: 1.0,
THETA_PRIMARY: 0.0,
DNFR_PRIMARY: 0.15,
}
)
# Node 1: phase = π (antiphase - destructive interference)
G.add_node(
1,
**{
EPI_PRIMARY: 0.50,
VF_PRIMARY: 1.0,
THETA_PRIMARY: math.pi,
DNFR_PRIMARY: 0.05,
}
)
G.add_edge(0, 1)

# Enable propagation with standard threshold
G.graph["THOL_PROPAGATION_ENABLED"] = True
G.graph["THOL_MIN_COUPLING_FOR_PROPAGATION"] = 0.5

# Build EPI history for bifurcation
G.nodes[0]["epi_history"] = [0.05, 0.33, 0.50]

epi_1_before = G.nodes[1][EPI_PRIMARY]

# Apply THOL
SelfOrganization()(G, 0)

epi_1_after = G.nodes[1][EPI_PRIMARY]

# Verify: propagation should NOT have occurred to antiphase neighbor
assert (
epi_1_after == epi_1_before
), "Antiphase neighbor must be rejected (Invariant #5)"

# Verify telemetry: no propagation to node 1
propagations = G.graph.get("thol_propagations", [])
if propagations:
for prop in propagations:
if prop["source_node"] == 0:
affected_neighbors = [n for n, _ in prop["propagations"]]
assert (
1 not in affected_neighbors
), "Antiphase neighbor should not appear in propagation list"


class TestTHOLCascades:
"""Test cascade triggering across network chains."""
Expand Down
Loading