Paper 27 Integration — Reviewer Summary¶
Repository: anulum/scpn-control
Branch: main
Commits: 81704be..HEAD
Date: 2026-02-26
Author: Miroslav Šotek — ORCID 0009-0009-3560-0851
Paper 27 source: academia.edu
Related Kuramoto–Sakaguchi finite-size reference: arXiv:2004.06344
PDF export: REVIEWER_PAPER27_INTEGRATION.pdf
Current boundary: this is an example oscillator-model integration, not a reactor feedback loop. Its abstract layers, coefficients, and synthetic SNN signals are not identified from reactor diagnostics and do not command a plant.
1. What Was Requested¶
The reviewer asked for the Kuramoto–Sakaguchi + global field driver from
SCPN Paper 27 ("The Knm Matrix") to be woven into the scpn-control tokamak
control codebase. Specifically:
- The ζ sin(Ψ − θ) "intention as carrier" injection, where Ψ is a Lagrangian pull parameter with no own dynamics (no ˙Ψ equation).
- The full 16-layer Knm coupling matrix with calibration anchors and cross-hierarchy boosts.
- A Rust sub-ms kernel for the hot Kuramoto loop (rayon-parallelised).
- PAC cross-layer SNN sketch showing phase-amplitude coupling gating.
- A demo notebook with visualisations and a markdown export to
docs/.
2. Master Equation¶
dθ_{m,i}/dt = ω_{m,i}
+ K_{mm} · R_m · sin(ψ_m − θ_{m,i} − α_{mm}) [intra-layer]
+ Σ_{n≠m} K_{nm} · R_n · sin(ψ_n − θ_{m,i} − α_{nm}) [inter-layer]
+ ζ_m · sin(Ψ − θ_{m,i}) [global driver]
- K_{mm} (diagonal): intra-layer synchronisation strength
- K_{nm} (off-diagonal): declared inter-layer model coupling
- ζ sin(Ψ − θ): exogenous global field driver — Ψ resolved externally or from mean-field
- α_{nm}: Sakaguchi phase-lag frustration (optional)
- R, ψ: Kuramoto order parameter R·exp(i·ψ) = ⟨exp(i·θ)⟩
Reference: arXiv:2004.06344 (generalized Kuramoto–Sakaguchi finite-size)
3. Files Created / Modified¶
3.1 Python — Phase Dynamics Package¶
| File | Lines | Purpose |
|---|---|---|
src/scpn_control/phase/__init__.py |
36 | Package exports |
src/scpn_control/phase/kuramoto.py |
161 | Kuramoto–Sakaguchi + ζ sin(Ψ−θ) + Lyapunov V/λ, Rust auto-dispatch |
src/scpn_control/phase/knm.py |
101 | Paper 27 Knm matrix builder + OMEGA_N_16 |
src/scpn_control/phase/upde.py |
215 | Multi-layer UPDE engine + run_lyapunov() |
src/scpn_control/phase/lyapunov_guard.py |
130 | Lyapunov stability guardrail (DIRECTOR_AI sync) |
3.2 Rust — Sub-ms Kernel¶
| File | Lines | Purpose |
|---|---|---|
scpn-control-rs/crates/control-math/src/kuramoto.rs |
195 | Rayon-parallelised Kuramoto step + run + 7 unit tests |
scpn-control-rs/crates/control-math/src/lib.rs |
+1 | pub mod kuramoto; |
scpn-control-rs/crates/control-python/src/lib.rs |
+67 | PyO3 bindings: kuramoto_step(), kuramoto_run() |
3.3 FusionKernel Integration¶
| File | Lines | Purpose |
|---|---|---|
src/scpn_control/core/fusion_kernel.py |
+86 | phase_sync_step() + phase_sync_step_lyapunov() |
3.4 Tests¶
| File | Lines | Tests |
|---|---|---|
tests/test_phase_kuramoto.py |
475 | 44 |
kuramoto.rs (inline #[cfg(test)]) |
— | 9 |
3.5 Documentation¶
| File | Purpose |
|---|---|
examples/paper27_phase_dynamics_demo.ipynb |
10-section notebook with plots |
docs/paper27_phase_dynamics.md |
Markdown export of notebook |
4. Architecture — How It Fits¶
4.1 Equation Cross-Reference (Paper 27, Eqs. 12–15)¶
| Paper 27 Eq. | Description | Implementation |
|---|---|---|
| (12) | Mean-field Kuramoto order parameter: R·e^{iψ} = (1/N) Σ e^{iθ_j} | order_parameter() in kuramoto.py:47 / kuramoto.rs:15 |
| (13) | Single-layer Kuramoto–Sakaguchi: dθ_i/dt = ω_i + K·R·sin(ψ−θ_i−α) | kuramoto_sakaguchi_step() in kuramoto.py:87 / kuramoto.rs:53 |
| (14) | Multi-layer UPDE with Knm inter-layer coupling: dθ_{m,i}/dt = ω_{m,i} + K_{mm}·R_m·sin(ψ_m−θ_{m,i}−α_{mm}) + Σ_{n≠m} K_{nm}·R_n·sin(ψ_n−θ_{m,i}−α_{nm}) | UPDESystem.step() in upde.py:45 |
| (15) | Exogenous global field driver: + ζ_m·sin(Ψ−θ_{m,i}), Ψ exogenous (no ˙Ψ) | ζ term in kuramoto.py:126–127, upde.py:115–116; GlobalPsiDriver in kuramoto.py:67 |
4.2 Module Architecture¶
┌───────────────────────────────────────────────────────┐
│ FusionKernel │
│ │
│ solve_equilibrium() ← GS solver (untouched) │
│ compute_stability() ← MHD stability (untouched) │
│ phase_sync_step() ← NEW: Paper 27 Eqs. 12–15 │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ scpn_control.phase │ │
│ │ │ │
│ │ kuramoto_sakaguchi_step() [Eq.13] │──► Rust │
│ │ ├─ order_parameter() [Eq.12] │ fast-path │
│ │ ├─ GlobalPsiDriver [Eq.15] │ (rayon, │
│ │ └─ wrap_phase() │ sub-ms │
│ │ │ N>1000) │
│ │ KnmSpec / build_knm_paper27() │ │
│ │ UPDESystem.step() [Eq.14] │ │
│ └─────────────────────────────────────┘ │
└───────────────────────────────────────────────────────┘
Non-invasive: the GS equilibrium solver, SNN controllers, and all existing
code paths are completely untouched. phase_sync_step() is a new method on
FusionKernel that reads defaults from cfg["phase_sync"].
4.3 Ψ Global Driver Flowchart¶
┌──────────────────────┐
│ Caller / Controller │
└──────────┬───────────┘
│
┌──────────▼───────────┐
│ psi_mode? │
└──┬───────────────┬───┘
│ │
"external" "mean_field"
│ │
┌────────▼──────┐ ┌─────▼──────────────┐
│ Ψ = caller- │ │ Ψ = arg(⟨e^{iθ}⟩) │
│ supplied float │ │ from oscillator │
│ (intention │ │ population │
│ carrier) │ │ (self-organised) │
└────────┬──────┘ └─────┬──────────────┘
│ │
└───────┬───────┘
│
┌──────────▼───────────┐
│ Ψ resolved (scalar) │
│ NO ˙Ψ dynamics │
└──────────┬───────────┘
│
┌────────────────▼────────────────┐
│ For each oscillator i: │
│ dθ_i += ζ · sin(Ψ − θ_i) │
│ │
│ • ζ > 0: pull toward Ψ │
│ • ζ = 0: term vanishes │
│ • gain scales both K and ζ │
└────────────────┬────────────────┘
│
┌────────────────▼────────────────┐
│ Euler step: θ' = θ + dt·dθ │
│ wrap to (−π, π] │
└────────────────┬────────────────┘
│
┌────────────────▼────────────────┐
│ Return: θ', dθ, R, ψ_r, Ψ │
└─────────────────────────────────┘
5. Knm Matrix — Paper 27 Specification¶
# Canonical 16-layer natural frequencies (rad/s)
OMEGA_N_16 = [1.329, 2.610, 0.844, 1.520, 0.710, 3.780, 1.055, 0.625,
2.210, 1.740, 0.480, 3.210, 0.915, 1.410, 2.830, 0.991]
# Base coupling with exponential distance decay
K[i,j] = K_base · exp(−α · |i − j|) # K_base=0.45, α=0.3
# Calibration anchors (Paper 27 Table 2)
K[0,1] = K[1,0] = 0.302
K[1,2] = K[2,1] = 0.201
K[2,3] = K[3,2] = 0.252
K[3,4] = K[4,3] = 0.154
# Cross-hierarchy boosts (Paper 27 §4.3)
K[0,15] = K[15,0] ≥ 0.05 # L1 ↔ L16
K[4,6] = K[6,4] ≥ 0.15 # L5 ↔ L7
6. Rust Kernel — Performance Path¶
The Python solver auto-dispatches to Rust when:
- scpn_control_rs is importable (maturin build)
- wrap=True and alpha=0.0 (common fast-path)
// Hot loop — rayon parallel chunks of 64
theta_out
.par_chunks_mut(64)
.enumerate()
.for_each(|(chunk_idx, chunk)| {
for (local_i, val) in chunk.iter_mut().enumerate() {
let i = base + local_i;
let mut dth = om + kr_sin_base * (psi_r - th - alpha).sin();
if zeta != 0.0 {
dth += zeta * (psi_global - th).sin();
}
*val = wrap_phase(th + dt * dth);
}
});
PyO3 bindings expose kuramoto_step(theta, omega, dt, k, alpha, zeta, psi_external)
and kuramoto_run(...) returning NumPy arrays directly.
6.1 Benchmark: Python NumPy vs Rust Rayon¶
Median wall-time for a single kuramoto_sakaguchi_step() with ζ=0.5, Ψ=0.3.
Python: NumPy vectorised (AMD Ryzen, single-thread).
Rust: Rayon par_chunks_mut(64) + criterion harness.
| N | Python (ms) | Rust (ms) | Speedup |
|---|---|---|---|
| 64 | 0.050 | 0.003 | 17.3× |
| 256 | 0.029 | 0.033 | 0.9× |
| 1 000 | 0.087 | 0.062 | 1.4× |
| 4 096 | 0.328 | 0.180 | 1.8× |
| 16 384 | 1.240 | 0.544 | 2.3× |
N=64: Rust wins on per-element throughput (no NumPy dispatch overhead). N=256: parity — NumPy SIMD matches rayon for this size. N≥1000: Rust rayon parallelism scales; sub-ms for N=16k (0.544 ms).
Benchmark source: benches/bench_kuramoto.rs (criterion, --quick mode).
7. Global Field Driver — ζ sin(Ψ − θ)¶
GlobalPsiDriver resolves Ψ before the integration step:
| Mode | Ψ source | Use case |
|---|---|---|
"external" |
Caller supplies float | Intention-as-carrier injection |
"mean_field" |
arg(⟨exp(iθ)⟩) | Self-organised collective phase |
There is no ˙Ψ equation — Ψ is a Lagrangian pull parameter. When
zeta > 0, all oscillators are pulled toward Ψ with strength proportional to
sin(Ψ − θ_i).
8. PAC Cross-Layer Gating + SNN Sketch¶
8.1 PAC Gate Equation¶
The UPDE engine supports phase-amplitude coupling gating via pac_gamma:
# Inter-layer term with PAC gate
pac_gate = 1.0 + pac_gamma * (1.0 - R_source)
dθ += gain * pac_gate * K[n,m] * R_n * sin(ψ_n - θ - α[n,m])
When a source layer is incoherent (low R), the gate amplifies its coupling, implementing the PAC hypothesis that desynchronised layers drive downstream amplitude modulation.
8.2 SNN PAC Model-Internal Feedback Sketch¶
The example couples spiking neural networks with an abstract Kuramoto oscillator population through a PAC gating mechanism. It closes a numerical model loop only; no reactor observation or actuator interface is present.
┌─────────────────────────────────────────────────────────────────────┐
│ SNN–PAC–Kuramoto Model Feedback │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ spike rate ┌──────────────────┐ │
│ │ LIF Layer A │ ──────────────────► │ Rate Decoder │ │
│ │ (N_a neurons)│ ν_a(t) │ ν → Ψ mapping │ │
│ │ I_syn = f(θ) │ │ Ψ = π(2ν/ν_max │ │
│ └──────┬───────┘ │ − 1) │ │
│ │ synaptic └────────┬─────────┘ │
│ │ input │ │
│ │ │ Ψ (exogenous) │
│ ┌──────▼───────┐ ┌────────▼─────────┐ │
│ │ LIF Layer B │ PAC gate │ Kuramoto │ │
│ │ (N_b neurons)│ ◄─────────────── │ Oscillators │ │
│ │ w_ab modulated│ R_source → │ N oscillators │ │
│ │ by R_source │ gate strength │ dθ/dt = ω + .. │ │
│ └──────┬───────┘ │ + ζ sin(Ψ − θ) │ │
│ │ └────────┬─────────┘ │
│ │ spike output │ │
│ │ │ R, ψ_r │
│ ┌──────▼───────────────────────────────────────▼─────────┐ │
│ │ PAC Feedback Controller │ │
│ │ │ │
│ │ 1. Read R_source from each Kuramoto layer │ │
│ │ 2. Compute PAC gate: g = 1 + γ·(1 − R_n) │ │
│ │ 3. Modulate inter-layer SNN weights: w' = g · w_base │ │
│ │ 4. Inject Kuramoto R into LIF synaptic current: │ │
│ │ I_syn = I_base + β · R · cos(ψ_r − θ_preferred) │ │
│ │ 5. Map spike rate → Ψ for next Kuramoto step │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
├─────────────────────────────────────────────────────────────────────┤
│ Data Flow per Timestep (dt = 1 ms): │
│ │
│ t=0: Kuramoto step → R_m, ψ_m for each layer m │
│ t=1: PAC gate g_m = 1 + γ(1 − R_m) → modulate SNN weights │
│ t=2: LIF neurons integrate I_syn(R, ψ) → spike/no-spike │
│ t=3: Decode spike rate ν → Ψ_next = π(2ν/ν_max − 1) │
│ t=4: Feed Ψ_next back as exogenous driver → next Kuramoto step │
│ │
├─────────────────────────────────────────────────────────────────────┤
│ Key Equations: │
│ │
│ LIF: τ dV/dt = −(V − V_rest) + R_mem · I_syn │
│ if V ≥ V_th: spike, V → V_reset │
│ │
│ PAC gate: g_{n→m} = 1 + γ_PAC · (1 − R_n) │
│ │
│ Synaptic current from Kuramoto: │
│ I_syn,i = Σ_j w_ij · δ(t − t_j^spike) + β · R_m · cos(ψ_m−φ_i)│
│ │
│ Rate→Ψ decoder: Ψ = π · (2·ν_window/ν_max − 1) │
│ ν_window = spike_count / T_window (T_window = 50 ms) │
│ │
│ Model-feedback convergence candidate: │
│ V(t) = (1/N) Σ_i (1 − cos(θ_i − Ψ)) + λ·|ν − ν_target|² │
│ dV/dt ≤ 0 when ζ > 0 and SNN rate tracks target │
└─────────────────────────────────────────────────────────────────────┘
8.3 Cross-Layer PAC Routing (Multi-Layer SNN)¶
Layer L1 (Quantum) Layer L7 (Symbolic) Layer L16 (Director)
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ LIF₁ (64 neu)│ │ LIF₇ (64 neu)│ │ LIF₁₆(64 neu)│
│ ω₁ = 1.329 │ │ ω₇ = 1.055 │ │ ω₁₆ = 0.991 │
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
│ │ │
│ K[0,6]=0.15 │ K[6,15] │ K[15,0]=0.05
│ PAC g₁₇ │ PAC g₇₁₆ │ PAC g₁₆₁
▼ ▼ ▼
┌──────────────────────────────────────────────────────────────┐
│ Kuramoto Phase Bus (16 layers) │
│ │
│ Per-layer R_m, ψ_m → PAC gates g_{n→m} → SNN weight mod │
│ Spike rates ν_m → Ψ decoder → exogenous driver feedback │
│ │
│ Cross-hierarchy fast channels: │
│ L1 ↔ L16: K=0.05 (quantum ↔ director) │
│ L5 ↔ L7: K=0.15 (bio ↔ symbolic) │
└──────────────────────────────────────────────────────────────┘
Demo: notebook §9 (synthetic gain feedback) and §10 (PAC cross-layer example).
9. Lyapunov Stability — λ Hook¶
9.1 Python Functions¶
# Lyapunov candidate V(t) = (1/N) Σ (1 − cos(θ_i − Ψ))
from scpn_control.phase import lyapunov_v, lyapunov_exponent
V = lyapunov_v(theta, psi) # scalar ∈ [0, 2]
lam = lyapunov_exponent(v_hist, dt=1e-3) # T = (len(v_hist)-1)·dt
# λ < 0 ⟹ stable convergence toward Ψ
The exponent helper validates finite non-negative samples, floors only the
initial and final endpoints near perfect synchrony, and matches
control-math/kuramoto.rs::lyapunov_v and kuramoto_run_lyapunov.
9.2 UPDE Lyapunov Tracking¶
UPDESystem.step() returns a completed-tick snapshot: theta1, dtheta,
R_layer, Psi_layer, R_global, Psi_global, V_layer, and V_global.
UPDESystem.run_lyapunov() returns full V histories and per-layer + global λ:
out = sys.run_lyapunov(200, theta_layers, omega_layers, psi_driver=0.5, pac_gamma=1.0)
# out["V_layer_hist"] — (n_steps, L)
# out["lambda_layer"] — (L,) per-layer Lyapunov exponents
# out["lambda_global"] — scalar global λ
9.3 FusionKernel.phase_sync_step_lyapunov()¶
Multi-step Kuramoto with Lyapunov tracking:
out = kernel.phase_sync_step_lyapunov(
theta, omega, n_steps=100, dt=0.01,
zeta=3.0, psi_driver=0.5,
)
# out["lambda"] — Lyapunov exponent
# out["stable"] — True if λ < 0
# out["V_hist"] — (100,) trajectory
# out["R_hist"] — (100,) coherence trajectory
9.4 Lyapunov Exponent vs ζ Strength¶
See docs/bench_lyapunov_vs_zeta.vl.json — Vega-Lite plot showing:
- ζ = 0: λ ≈ 0 (no convergence, drift)
- ζ = 0.5: λ ≈ −0.23 (moderate pull)
- ζ = 3.0: λ ≈ −1.83 (strong convergence)
- ζ = 5.0: λ ≈ −3.35 (rapid sync)
K=2.0 (Kuramoto coupling) amplifies the ζ effect due to cooperative self-organisation.
10. DIRECTOR_AI Guardrail Sync¶
10.1 LyapunovGuard¶
scpn_control.phase.lyapunov_guard.LyapunovGuard monitors V(t) over a sliding
window and flags instability when λ > 0 for K consecutive windows. Interface
mirrors DIRECTOR_AI's CoherenceScorer → CoherenceScore pattern:
from scpn_control.phase import LyapunovGuard
guard = LyapunovGuard(window=50, dt=1e-3, max_violations=3)
# Per-timestep oscillator-model check
verdict = guard.check(theta, psi)
verdict.approved # True if stable
verdict.lambda_exp # current λ
verdict.score # stability score ∈ [0, 1]
verdict.consecutive_violations
# Batch check (post-hoc)
verdict = guard.check_trajectory(v_hist)
10.2 DIRECTOR_AI Integration¶
Export to DIRECTOR_AI AuditLogger format:
d = guard.to_director_ai_dict(verdict)
# {"query": "lyapunov_stability_check",
# "response": "V=0.42, λ=-1.23",
# "approved": True,
# "score": 0.99,
# "h_factual": 0.0,
# "halt_reason": ""}
This enables the DIRECTOR_AI CoherenceAgent to incorporate the model's
Lyapunov metric into its dual-entropy coherence score. When λ > 0 for 3
consecutive windows, the guard refuses the oscillator-model trajectory. This is
not a machine-protection or reactor-safety verdict.
10.3 Data Flow¶
Kuramoto oscillators → θ(t) per timestep
│
▼
LyapunovGuard.check(θ, Ψ) → LyapunovVerdict
│
├─ approved=True → continue model trajectory
├─ approved=False → refuse / clamp model parameters
│
└─ to_director_ai_dict() → DIRECTOR_AI AuditLogger
├─ h_factual = max(0, λ)
└─ halt_reason logged
11. Streaming Model Dashboard Hook¶
11.1 RealtimeMonitor¶
scpn_control.phase.realtime_monitor.RealtimeMonitor wraps UPDESystem +
LyapunovGuard into a tick-by-tick interface for model dashboards:
from scpn_control.phase import RealtimeMonitor
monitor = RealtimeMonitor.from_paper27(psi_driver=0.0)
for _ in model_ticks:
snap = monitor.tick()
if not snap["guard_approved"]:
mark_model_trajectory_refused()
dashboard.push(snap)
Each tick() returns: R_global, R_layer, Psi_global, V_global,
V_layer, lambda_exp, guard_approved, guard_score, latency_us,
and a director_ai dict ready for AuditLogger. Native Rust monitor ticks
also expose the raw dtheta_flat and Psi_layer arrays for parity checks.
Model-tick failures return a refusal snapshot: tick() returns the same fields
with guard_approved=False, guard_score=0.0, and error/error_type
metadata without losing the streaming contract. Nothing in this interface
halts or commands a reactor.
11.2 Interactive Benchmark Visualisation¶
docs/bench_interactive.vl.json — single Vega-Lite
chart with 3 vertically concatenated panels:
- Python vs Rust Speedup (log-log, N=64..65k, legend-click filtering)
- λ vs ζ (K=0 / K=2 configs, stability boundary annotation)
- PAC vs No-PAC Latency (grouped bars with 95% CI error bars)
11.3 CI Benchmark — DIII-D-Sized Oscillator Population¶
CI job python-benchmark runs Kuramoto steps at a population size labelled for
the former DIII-D mock. This is a numerical workload label, not PCS timing,
diagnostic fidelity, or device validation:
- N=1000, N=4096 single-step P50 < 5 ms gate
- RealtimeMonitor tick (16 × 50 oscillators) P50 < 50 ms gate
11.4 Streamlit Dashboard¶
dashboard/control_dashboard.py — 6 tabs:
- Trajectory Viewer — bounded PID/SNN trajectory
- Phase Model Monitor — streamed RealtimeMonitor R/V/λ plots + DIRECTOR_AI export
- Benchmark Plots —
bench_interactive.vl.jsonembedded asst.vega_lite_chart - RMSE Dashboard — validation summary
- Timing Benchmark — PID vs SNN latency
- Shot Replay — disruption shot viewer
11.5 Rust PyO3 UPDE Tick¶
PyRealtimeMonitor in control-python/src/lib.rs wraps the Rust upde_tick()
multi-layer kernel:
import scpn_control_rs as rs
mon = rs.PyRealtimeMonitor(knm_flat, zeta, theta_flat, omega_flat, L, N_per)
snap = mon.tick()
# returns {tick, theta_flat, dtheta_flat, R_global, R_layer,
# Psi_global, Psi_layer, V_global, V_layer}
Rust upde_tick in control-math/src/kuramoto.rs: per-layer Kuramoto +
inter-layer Knm coupling + PAC gate + Lyapunov V tracking. 11 Rust tests
(9 existing + 2 new test_upde_tick_*).
11.6 WebSocket Phase-Model Stream¶
scpn_control.phase.ws_phase_stream.PhaseStreamServer — async WebSocket server
streaming model-tick snapshots as JSON frames. Network hardening does not turn
these frames into reactor diagnostics or actuator commands:
Clients receive {"tick": N, "R_global": ..., "V_global": ..., "lambda_exp": ...}
every tick. Control commands: {"action": "set_psi", "value": 1.0},
{"action": "reset"}, {"action": "stop"}.
The server binds to 127.0.0.1 by default. Binding to a non-loopback address
requires an API key via --api-key or SCPN_PHASE_WS_API_KEY; clients must
provide that key as Authorization: Bearer <key>, X-SCPN-API-Key, or a
token query parameter. Use --tls-cert and --tls-key when exposing the
stream as wss:// behind an operator-approved network boundary. Command
messages are rate-limited per connection.
11.7 HDF5 / NPZ Trajectory Export¶
monitor = RealtimeMonitor.from_paper27()
for _ in range(1000):
monitor.tick()
monitor.save_hdf5("trajectory.h5") # requires h5py
monitor.save_npz("trajectory.npz") # numpy only
Datasets: R_global, R_layer, V_global, V_layer, lambda_exp, guard_approved, latency_us, Psi_global. HDF5 attributes: L, N_per, psi_driver, pac_gamma, n_ticks.
11.8 Mock DIII-D Shot Loader (CI)¶
tests/mock_diiid.py generates synthetic shots matching real DIII-D npz format
(14 fields: time_s, Ip_MA, BT_T, beta_N, q95, ne_1e19, MHD modes, etc.).
CI job e2e-diiid runs end-to-end tests:
- Mock shot generation and round-trip load
- Synthetic-field-driven RealtimeMonitor (Ψ = f(beta_N))
- NPZ and HDF5 trajectory export verification
11.9 Streamlit WebSocket Client¶
examples/streamlit_ws_client.py — live Streamlit dashboard consuming WS ticks:
# Two-terminal mode
python -m scpn_control.phase.ws_phase_stream --host 127.0.0.1 --port 8765 --zeta 0.5 # terminal 1
streamlit run examples/streamlit_ws_client.py # terminal 2
# Embedded mode (server + client in one process)
streamlit run examples/streamlit_ws_client.py -- --embedded
Features: auto-reconnect, R/V/λ time-series plots, per-layer bar chart, guard status, Ψ control slider, raw JSON expander, auto-refresh at 3 Hz.
11.10 Phase Sync Live Video¶

Generated model trajectory from RealtimeMonitor (500 ticks, 16×50 oscillators, ζ=0.5):
- MP4:
docs/phase_sync_live.mp4(418 KB, H.264) - GIF:
docs/phase_sync_live.gif(1.1 MB) - Generator:
tools/generate_phase_video.py --ticks 500 --fps 20
Observed model convergence: R=0.92, V→0, λ=−0.47 (model-local stable), 38 µs/tick in the recorded benchmark context.
11.11 PyPI Publish Script¶
tools/publish.py — local build + publish pipeline:
python tools/publish.py --dry-run # build + twine check
python tools/publish.py --target testpypi # upload to TestPyPI
python tools/publish.py --bump minor --target pypi --confirm # version bump + PyPI
CI workflow .github/workflows/publish-pypi.yml handles tag-triggered trusted
publishing (no tokens needed).
11.12 CLI live Command¶
scpn-control live starts a real-time WebSocket phase sync server directly
from the CLI:
Options: --host, --port, --layers, --n-per, --zeta, --psi,
--tick-interval. Streams JSON tick snapshots at ws://host:port.
11.13 README MP4 Embed¶
README now uses <video> tag for native GitHub MP4 playback (autoplay, loop,
muted) with GIF fallback inside <noscript>.
11.14 Streamlit Cloud Deployment¶
Files added for one-click Streamlit Cloud deployment:
.streamlit/config.toml— dark theme matching docs video palettestreamlit_app.py— root entry point with auto-start embedded server
Deploy: share.streamlit.io > New app > anulum/scpn-control > streamlit_app.py.
Live: scpn-control.streamlit.app
11.15 PyPI Version Bump (v0.2.0)¶
Version bumped from 0.1.0 to 0.2.0 to mark the live CLI feature and full
Paper 27 phase dynamics integration:
pyproject.toml: version = "0.2.0"
__init__.py: __version__ = "0.2.0"
CHANGELOG.md: [0.2.0] — 2026-02-26
Build artifacts (dry-run verified):
- scpn_control-0.2.0-py3-none-any.whl (226 KB)
- scpn_control-0.2.0.tar.gz (272 KB)
- twine check: PASSED
Publish: python tools/publish.py --target testpypi (or --target pypi --confirm).
12. FusionKernel.phase_sync_step() — Single Step¶
kernel = FusionKernel("tokamak_config.json")
# Config-driven defaults from cfg["phase_sync"]
out = kernel.phase_sync_step(
theta=oscillator_phases,
omega=natural_frequencies,
dt=1e-3,
psi_driver=0.0, # exogenous Ψ
)
# Returns: theta1, dtheta, R, Psi_r, Psi
All parameters fall through to cfg["phase_sync"] when not explicitly
provided. The actuation_gain parameter scales both K and ζ uniformly.
13. Test Coverage¶
61 phase-specific tests + 3 Rust parity (675 total in suite, 14 CI jobs, all green):
| Class | Tests | What is verified |
|---|---|---|
TestOrderParameter |
4 | R=1 sync, R≈0 uniform, R∈[0,1], weighted |
TestWrapPhase |
2 | Identity in range, large angle wrapping |
TestGlobalPsiDriver |
3 | External requires value, returns value, mean-field |
TestKuramotoSakaguchiStep |
4 | Sync stability, R increase, ζ pull, α frustration |
TestKnmSpec |
7 | Shape, anchors, boosts, symmetry, zeta, validation |
TestUPDESystem |
6 | Step shape, intra-sync, ζ pull, trajectory, PAC, error |
TestFusionKernelPhaseSync |
3 | Integration smoke, config-driven ζ, Lyapunov multi-step |
TestLyapunovV |
4 | V=0 sync, V=2 anti-sync, empty, range |
TestLyapunovExponent |
3 | λ<0 decreasing, λ>0 increasing, single sample |
TestUPDELyapunov |
3 | step V output, run_lyapunov λ, PAC γ effect |
TestLyapunovGuard |
5 | Stable approved, unstable refused, batch, DIRECTOR_AI dict, reset |
TestRealtimeMonitor |
6 | from_paper27 defaults, tick snapshot, multi-tick, convergence, reset, DIRECTOR_AI export |
TestMockDIIID |
4 | Shot generation, shapes, save/reload, safe shot |
TestE2EPhaseSyncWithShot |
2 | Shot-driven monitor, disruption guard |
TestTrajectoryExport |
4 | NPZ export, HDF5 export, recorder clear, record=False |
TestWebSocketServer |
1 | Server construction |
11 Rust tests (inline, all passing):
| Test | What is verified |
|---|---|
test_order_parameter_synced |
R=1 for identical phases |
test_order_parameter_range |
R∈[0,1] |
test_wrap_phase_identity |
No-op in range |
test_wrap_phase_large |
7π wraps to (−π,π] |
test_step_preserves_count |
Output length matches input |
test_zeta_pulls_toward_psi |
500 steps, spread < 0.1 |
test_run_returns_trajectory |
Correct trajectory length |
test_lyapunov_v_synced_is_zero |
V=0 at perfect sync |
test_lyapunov_exponent_negative_with_zeta |
λ<0 with ζ=3 driver |
test_upde_tick_shape |
Multi-layer tick output dimensions |
test_upde_tick_zeta_convergence |
4-layer ζ=3 convergence to Ψ |
Full suite regression: 582 passed, 94 skipped, 0 failures. Total collected: 675 tests across 41 test files.
14. Demo Notebook Sections¶
examples/paper27_phase_dynamics_demo.ipynb (10 sections + summary):
- Knm Heatmap — 16×16 coupling matrix visualisation
- ζ Comparison — with/without global driver, R convergence
- α Frustration — Sakaguchi phase-lag effect on synchronisation
- 16-Layer UPDE — full multi-layer evolution with R trajectories
- PAC Gating — phase-amplitude coupling modulation demo
- FusionKernel Oscillator Model — compatibility wrapper with config defaults
- Gain Sweep — dimensionless model-gain exploration
- Lyapunov Stability — V(t) = (1/N)Σ(1−cos(θ_i−Ψ)) monotone decrease
- Synthetic SNN Feedback — model-internal spike-rate → Ψ mapping
- PAC Cross-Layer SNN — multi-layer SNN with phase-coupled spike routing
Markdown export: docs/paper27_phase_dynamics.md
15. Commit History¶
4af1c5f fix: silence clippy too_many_arguments / type_complexity on Kuramoto bindings
7453019 style: cargo fmt on kuramoto bindings
ad09c0e feat: Rust Kuramoto kernel, PAC cross-layer SNN, docs export
b11228b docs: add Paper 27 phase dynamics demo notebook
81704be feat: add Paper 27 Knm/UPDE engine + ζ sin(Ψ−θ) global driver
16. What Was NOT Touched¶
- GS equilibrium solver (
solve_equilibrium,gs_step,SOR/multigrid) - SNN controllers (
LIFNeuron,SNNController, spike-rate feedback`) - Chebyshev/IGA spectral methods
- Rust control-math crates (SOR, tridiag, FFT, etc.) — only added
kuramotomodule - All existing tests remain green
17. Paper 27 Reference¶
M. Šotek, "The Knm Matrix: A Simulation Framework for Modelling Multi-Scale Bidirectional Causality in the Self-Consistent Phenomenological Network," SCPN Paper 27, 2026. Available: academia.edu | ORCID 0009-0009-3560-0851
Practical use and scope¶
Use this file only as an integration handoff checklist for external review of Paper 27.
- Keep it aligned with the latest commit and validation outputs before sending for review.
- Use the checklist to map manuscript claims to executed tests, runtime logs, and reproducibility artifacts.
- Do not treat this as deployment guidance; use it to scope review readiness and remaining evidence actions.