Coupling¶
The coupling subsystem builds, adapts, and analyses the inter-oscillator coupling matrix K_nm — the central object in Kuramoto dynamics. K_ij determines how strongly oscillator j pulls oscillator i toward synchrony.
The subsystem spans 27 source files: public API modules for construction (knm), geometry constraints, phase lag estimation, template management, Hodge decomposition, spectral analysis, plasticity, transfer-entropy adaptation, causal inference, connectome generation, E/I balance, attention residuals, spatial modulation, and a universal Bayesian prior, plus validated backend bridge files.
Pipeline position¶
CouplingBuilder.build() ──→ K_nm, α ──→ UPDEEngine.step()
↑ │
UniversalPrior ↓
LagModel.estimate ────→ α compute_order_parameter()
connectome loader ─────→ K_nm │
auto-coupling-estimation ← raw phase time series
plasticity/TE ←────────────────── phase history
CouplingBuilder is the entry point of the SPO pipeline. Every engine
variant consumes (phases, omegas, knm, zeta, psi, alpha), so the
coupling matrix and phase-lag matrix are required for any simulation.
For data-first onboarding, auto_coupling_estimation() infers an initial
directed coupling graph from phase time series before review, projection, or
engine execution.
The inference boundary requires finite real phase samples and enforces the
transfer-entropy invariant that directed scores are non-negative with no
self-edge diagonal.
Across the coupling public boundary, boolean aliases mean Python bool,
NumPy boolean scalars, and object arrays containing either form; those inputs
are rejected before any float coercion.
K_nm Construction¶
CouplingBuilder¶
Builds coupling matrices from parameters.
Methods:
| Method | Signature | Description |
|---|---|---|
build |
(n_layers, base_strength, decay_alpha) → CouplingState |
Exponential-decay K_nm |
build_scpn_physics |
(k_base=0.45, alpha_decay=0.3) → CouplingState |
16-layer SCPN physics |
build_with_amplitude |
(n, base, decay, amp_str, amp_dec) → CouplingState |
Phase + amplitude K |
apply_handshakes |
(state, path) → CouplingState |
Overlay from JSON spec |
switch_template |
(state, name, templates) → CouplingState |
Runtime topology switch |
apply_handshakes() parses the JSON specification fail-closed: non-finite
constants, duplicate object keys, non-list matrix payloads, self-coupled
entries, and out-of-range layer indices are rejected before any K_nm entries
are modified.
CouplingState (frozen dataclass)¶
| Field | Type | Description |
|---|---|---|
knm |
NDArray |
Phase coupling matrix K_ij |
alpha |
NDArray |
Phase-lag matrix α_ij |
active_template |
str |
Name of active template |
knm_r |
NDArray \| None |
Amplitude coupling (Stuart-Landau) |
Coupling equation¶
For the standard Kuramoto model, the coupling enters as:
K_ij is the (i,j) entry of the coupling matrix. The matrix must satisfy:
- Square: K ∈ R^{N×N}
- Symmetric: K_ij = K_ji (undirected coupling; directed via asymmetric K)
- Non-negative: K_ij ≥ 0
- Zero diagonal: K_ii = 0 (no self-coupling)
Exponential-decay construction¶
CouplingBuilder.build(n, base_strength, decay_alpha) produces:
This generates nearest-neighbour-dominant coupling with exponential fall-off — appropriate for layered systems where adjacent layers interact more strongly than distant ones.
SCPN physics construction¶
build_scpn_physics(k_base=0.45, alpha_decay=0.3) produces a 16×16 matrix
using three coupling mechanisms:
- Adjacent layers (|i-j| = 1): timescale matching via
SCPN_LAYER_TIMESCALES(Quantum: 1e-15s to Social: 3.15e7s) - Near-neighbour (|i-j| ≤ 3): geometric mean of adjacent couplings
- Distant (|i-j| > 3): exponential decay from k_base
The 16 SCPN layers span 22 orders of magnitude in timescale:
| Layer | Name | Timescale |
|---|---|---|
| L1 | Quantum | 1e-15 s |
| L2 | Sub-nuclear | 1e-12 s |
| L3 | Atomic | 1e-10 s |
| L4 | Molecular | 1e-9 s |
| L5 | Cellular | 1e-3 s |
| L6 | Neural | 1e-2 s |
| L7 | Synaptic | 1e-1 s |
| L8 | Circuit | 1 s |
| L9 | Regional | 10 s |
| L10 | Behavioural | 60 s |
| L11 | Cognitive | 600 s |
| L12 | Social | 3600 s |
| L13 | Cultural | 86400 s |
| L14 | Evolutionary | 3.15e6 s |
| L15 | Cosmological | 3.15e7 s |
| L16 | Director (meta) | — |
Performance: build(100) < 10 ms, build_scpn_physics() < 5 ms.
knm ¶
Coupling-matrix builders for generic and SCPN-layer topologies.
CouplingBuilder constructs deterministic K_nm, alpha, and optional
amplitude-coupling snapshots from validated scalar parameters. The Rust-backed
path and NumPy fallback share the same public contract: finite non-boolean
inputs, positive layer counts, zero diagonals, and explicit template labels for
runtime/audit reporting.
Classes¶
CouplingState
dataclass
¶
CouplingState(
knm: FloatArray,
alpha: FloatArray,
active_template: str,
knm_r: FloatArray | None = None,
)
Immutable snapshot of phase/amplitude coupling matrices and template.
CouplingBuilder ¶
Builds Knm coupling matrices.
Methods:¶
build ¶
Build an exponentially decayed phase-coupling matrix.
Parameters¶
n_layers
Number of hierarchy layers or oscillators represented in the
square coupling matrix.
base_strength
Coupling strength before distance decay is applied.
decay_alpha
Non-negative exponential decay coefficient in
exp(-decay_alpha * |i - j|).
Returns¶
CouplingState
Coupling snapshot with knm and alpha matrices of shape
(n_layers, n_layers). The diagonal of knm is zero and
alpha is initialised to zeros.
Notes¶
When the Rust extension is available, construction dispatches to
spo_kernel.PyCouplingBuilder and preserves the same output
contract as the NumPy fallback.
Raises¶
ValueError If the layer count or coupling parameters are invalid.
Source code in src/scpn_phase_orchestrator/coupling/knm.py
build_scpn_physics ¶
Build 16×16 K_nm using SCPN layer physics.
Three coupling mechanisms (Paper 0, HolonomicAtlas v2.4.0): - Adjacent: timescale matching with calibration anchors - Near-neighbor (|n-m|=2): geometric mean of intermediate path - Distant (|n-m|>=3): exponential decay with cross-hierarchy boosts
Returns CouplingState with 16×16 matrix.
Parameters¶
k_base : float Base coupling strength. alpha_decay : float Exponential decay rate of the coupling.
Returns¶
CouplingState The 16×16 coupling state from SCPN layer physics.
Source code in src/scpn_phase_orchestrator/coupling/knm.py
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 | |
apply_handshakes ¶
Overlay documented inter-layer couplings from JSON spec.
Reads the KNM_MATRIX_COMPLETE_SPECIFICATION.json format: each entry has from_layer, to_layer, coupling_strength. Negative values are preserved (inhibitory coupling).
Parameters¶
state : CouplingState The coupling state to transform. handshakes_path : str | Path Path to the JSON inter-layer handshake spec.
Returns¶
CouplingState The coupling state with the documented inter-layer couplings overlaid.
Raises¶
ValueError If the handshake spec is malformed or out of range.
Source code in src/scpn_phase_orchestrator/coupling/knm.py
build_with_amplitude ¶
build_with_amplitude(
n_layers: int,
base_strength: float,
decay_alpha: float,
amp_strength: float,
amp_decay: float,
) -> CouplingState
Build phase + amplitude coupling matrices together.
Parameters¶
n_layers : int Number of SCPN hierarchy layers. base_strength : float Base coupling strength at zero layer separation. decay_alpha : float Exponential decay rate of the coupling across layer separation. amp_strength : float Base amplitude-coupling strength. amp_decay : float Exponential decay rate of the amplitude coupling.
Returns¶
CouplingState The coupling state with both phase and amplitude coupling matrices.
Source code in src/scpn_phase_orchestrator/coupling/knm.py
switch_template ¶
switch_template(
state: CouplingState,
template_name: str,
templates: dict[str, FloatArray],
) -> CouplingState
Replace the active K_nm with a named template matrix.
Parameters¶
state : CouplingState The coupling state to transform. template_name : str Name of the template to activate. templates : dict[str, FloatArray] Mapping of template name to coupling matrix.
Returns¶
CouplingState
The coupling state with the named template installed as K_nm.
Raises¶
KeyError
If template_name is not in templates.
ValueError
If the template matrix is invalid.
Source code in src/scpn_phase_orchestrator/coupling/knm.py
Geometry Constraints¶
Enforces structural invariants on K_nm.
Constraint classes¶
| Class | project(knm) behaviour |
|---|---|
SymmetryConstraint |
Returns (K + K^T) / 2 |
NonNegativeConstraint |
Clamps negative entries to 0 |
Validation¶
validate_knm(knm, atol=1e-12) accepts only finite real square matrices and
checks all four invariants: symmetric, non-negative, zero diagonal, and
boolean/complex aliases rejected before numeric projection. Raises
ValueError on violation.
project_knm(knm, constraints) applies constraints sequentially, then
zeros the diagonal. Built-in and custom constraints are fail-closed: each
constraint must be a GeometryConstraint, preserve the matrix shape, and
return finite real square K_nm values before the next projection step.
geometry_constraints ¶
Projection and validation helpers for coupling-matrix geometry.
Geometry constraints project candidate K_nm matrices onto simple feasible
sets such as symmetry and non-negativity. validate_knm enforces the runtime
matrix contract used by domainpacks and UPDE handoff: square, symmetric,
non-negative, and zero diagonal within tolerance.
Classes¶
GeometryConstraint ¶
Bases: ABC
Base class for K_nm matrix geometry constraints.
Methods:¶
project
abstractmethod
¶
Project knm onto the feasible set defined by this constraint.
Parameters¶
knm : FloatArray
Coupling matrix K_nm, shape (N, N).
Returns¶
FloatArray The projection of knm onto the constraint's feasible set.
Source code in src/scpn_phase_orchestrator/coupling/geometry_constraints.py
SymmetryConstraint ¶
Bases: GeometryConstraint
Enforce K_nm symmetry: K -> (K + K^T) / 2.
Methods:¶
project ¶
Return the symmetric part of knm.
Parameters¶
knm : FloatArray
Coupling matrix K_nm, shape (N, N).
Returns¶
FloatArray The symmetric part of knm.
Source code in src/scpn_phase_orchestrator/coupling/geometry_constraints.py
NonNegativeConstraint ¶
Bases: GeometryConstraint
Clamp negative entries to zero.
Methods:¶
project ¶
Return knm with all negative entries replaced by 0.
Parameters¶
knm : FloatArray
Coupling matrix K_nm, shape (N, N).
Returns¶
FloatArray knm with negative entries clipped to zero.
Source code in src/scpn_phase_orchestrator/coupling/geometry_constraints.py
Functions:¶
validate_knm ¶
Check that a coupling matrix is square, symmetric, non-negative, zero-diagonal.
Raises ValueError with a specific message on the first violation found.
Parameters¶
knm : FloatArray
Coupling matrix K_nm, shape (N, N).
atol : float
Absolute tolerance for the validity checks.
Raises¶
ValueError
If knm is not square, symmetric, non-negative, and zero-diagonal.
Source code in src/scpn_phase_orchestrator/coupling/geometry_constraints.py
project_knm ¶
Apply all geometry constraints sequentially, then zero the diagonal.
Parameters¶
knm : FloatArray
Coupling matrix K_nm, shape (N, N).
constraints : list[GeometryConstraint]
Geometry constraints applied in sequence.
Returns¶
FloatArray The coupling matrix after applying every constraint and zeroing the diagonal.
Raises¶
ValueError If a constraint produces an invalid coupling matrix.
Source code in src/scpn_phase_orchestrator/coupling/geometry_constraints.py
Phase Lag Estimation¶
Estimates inter-oscillator phase lags α_ij from observed time series or known physical distances.
From distances¶
LagModel.estimate_from_distances(distances, speed) computes:
Inputs must be a finite real square physical-distance matrix with
non-negative entries, a zero diagonal, and symmetric pair distances, plus a
finite positive propagation speed. Boolean aliases and complex/object-complex
distance payloads are rejected before numeric coercion because transport
delays are ordered real quantities. Returns an antisymmetric matrix:
α_ij = -α_ji. This encodes the fact that if signal from i reaches j with
positive lag, then j reaches i with negative lag. Directed or asymmetric
empirical delays belong in build_alpha_matrix, not in the physical-distance
constructor.
From cross-correlation¶
LagModel().estimate_lag(signal_a, signal_b, sample_rate) finds the
cross-correlation peak lag in seconds between two signals. Signals must be
finite real one-dimensional arrays with equal non-zero length and non-zero
variance. The sample-rate must be a finite positive real value. Constant,
boolean, complex/object-complex, non-finite, or length-mismatched signals are
rejected before cross-correlation because they do not define a reliable
phase-lag estimate.
Matrix construction¶
build_alpha_matrix(lag_estimates, n_layers, carrier_freq_hz=1.0) converts
pairwise lag estimates (in seconds) to a phase-offset matrix (in radians):
Performance: estimate_from_distances(64×64) < 5 ms.
lags ¶
Phase-lag estimation and alpha-matrix construction.
LagModel converts physical distances or observed signal offsets into
antisymmetric phase-lag matrices consumed by the UPDE engine. The module is
kept dependency-light and deterministic while rejecting non-physical distance,
sample, carrier, and speed inputs before the resulting lags enter closed-loop
runs.
Classes¶
LagModel ¶
Phase-lag estimation and alpha matrix construction.
Methods:¶
estimate_from_distances
staticmethod
¶
Build antisymmetric alpha matrix from pairwise distances and speed.
alpha[i,j] = 2*pi * distances[i,j] / speed.
Matches the Rust LagModel::estimate_from_distances algorithm.
Parameters¶
distances : FloatArray
Pairwise distance matrix, shape (N, N).
speed : float
Signal propagation speed.
Returns¶
FloatArray
The antisymmetric phase-lag matrix, shape (N, N).
Source code in src/scpn_phase_orchestrator/coupling/lags.py
estimate_lag ¶
Cross-correlation peak lag in seconds between two signals.
Parameters¶
signal_a : FloatArray
First signal, shape (T,).
signal_b : FloatArray
Second signal, shape (T,).
sample_rate : float
Sampling rate in Hz.
Returns¶
float The cross-correlation peak lag in seconds.
Raises¶
ValueError If the signals differ in length or the sample rate is invalid.
Source code in src/scpn_phase_orchestrator/coupling/lags.py
build_alpha_matrix ¶
build_alpha_matrix(
lag_estimates: dict[tuple[int, int], float],
n_layers: int,
carrier_freq_hz: float = 1.0,
) -> FloatArray
Pairwise lag estimates (seconds) to phase-offset matrix (radians).
alpha[i,j] = 2picarrier_freq_hz*lag[i,j], antisymmetric.
carrier_freq_hz defaults to 1.0 for backward compatibility.
Parameters¶
lag_estimates : dict[tuple[int, int], float] Mapping of oscillator pair to estimated lag in seconds. n_layers : int Number of SCPN hierarchy layers. carrier_freq_hz : float Carrier frequency in Hz used to convert lag to phase.
Returns¶
FloatArray
The phase-offset matrix in radians, shape (N, N).
Source code in src/scpn_phase_orchestrator/coupling/lags.py
Coupling Templates¶
Pre-configured coupling topologies for regime-dependent switching.
KnmTemplate (frozen dataclass)¶
| Field | Type | Description |
|---|---|---|
name |
str |
Template identifier |
knm |
NDArray |
Coupling matrix |
alpha |
NDArray |
Phase-lag matrix |
description |
str |
Human-readable description |
KnmTemplateSet¶
Registry for named templates:
add(template)— register (overwrites existing with same name)get(name) → KnmTemplate— retrieve (raisesKeyErrorif missing, error message lists available names)list_names() → list[str]— all registered names
Usage: The supervisor can switch coupling topology at runtime by calling
CouplingBuilder.switch_template(state, name, templates) when a regime
transition occurs (e.g., switching from all-to-all to nearest-neighbour
when entering DEGRADED regime).
templates ¶
Named coupling-template registry for runtime K/alpha switching.
Templates bundle a phase-coupling matrix, phase-lag matrix, description, and stable name. The registry is intentionally in-memory and deterministic: retrieval fails explicitly with available names when a requested template is not registered, leaving persistence and validation to the binding/template owner.
Classes¶
KnmTemplate
dataclass
¶
Named K_nm coupling matrix with associated phase-lag matrix.
KnmTemplateSet ¶
Registry of named K_nm templates for runtime switching.
Source code in src/scpn_phase_orchestrator/coupling/templates.py
Methods:¶
add ¶
Register a template, overwriting any existing one with the same name.
Parameters¶
template : KnmTemplate
The K_nm template to register.
Raises¶
TypeError
If template is not a KnmTemplate.
ValueError
If the template is invalid.
Source code in src/scpn_phase_orchestrator/coupling/templates.py
get ¶
Retrieve a template by name. Raises KeyError if not found.
Parameters¶
name : str Name to look up.
Returns¶
KnmTemplate The registered template with the given name.
Raises¶
KeyError If no template with that name is registered.
Source code in src/scpn_phase_orchestrator/coupling/templates.py
Combinatorial Hodge Decomposition¶
Decomposes the Kuramoto coupling current into three L²-orthogonal edge-flow components via combinatorial Hodge theory (Jiang, Lim, Yao & Ye 2011, Statistical ranking and combinatorial Hodge theory, Math. Program. 127 (1):203–244):
The oscillator network is treated as a simplicial complex (V, E, T):
vertices are oscillators, edges are the pairs {i, j} with non-zero
symmetric coupling, and triangles are the 3-cliques of that graph (or an
explicit user-supplied set). The decomposed object is the alternating
edge flow
— the canonical coupling current, built from the symmetric coupling part
so it satisfies f_ji = −f_ij. With node–edge incidence B1 and
edge–triangle incidence B2:
gradient = B1ᵀ · L0⁺ · (B1 f) # curl-free conservative flow
curl = B2 · L2⁺ · (B2ᵀ f) # divergence-free rotational flow
harmonic = f − gradient − curl # ker of the Hodge 1-Laplacian
where L0 = B1 B1ᵀ and L2 = B2ᵀ B2. Because B1 B2 = 0, the three
components are mutually L²-orthogonal.
HodgeResult (dataclass)¶
| Field | Type | Physical meaning |
|---|---|---|
gradient |
NDArray (N, N) |
Conservative (curl-free) flow grad(s) |
curl |
NDArray (N, N) |
Rotational (divergence-free) flow bounded by triangles |
harmonic |
NDArray (N, N) |
Topological residual in ker(L1) (non-zero only on cycles not filled by triangles) |
flow |
NDArray (N, N) |
The input alternating coupling current |
potential |
NDArray (N,) |
Minimum-norm node potential s with gradient = grad(s) |
betti_one |
int |
First Betti number β₁ — dimension of the harmonic subspace |
Each flow matrix is antisymmetric (M[i, j] is the flow on the oriented
edge i → j, M[j, i] = −M[i, j]).
Interpretation¶
- Gradient-dominated: the current is a node-potential difference and the system relaxes towards a fixed phase configuration.
- Curl-dominated: circulation around filled triangles — local cyclic frustration with no global potential.
- Harmonic component: flows around topological cycles that no
triangle bounds; its dimension equals the first Betti number
β₁. On a triangle-free graph carrying a cycle (for example, a 4-cycle), a circulating current is purely harmonic — the topological content that a plain symmetric/antisymmetric matrix split cannot represent. In the SCPN identity-coherence model this is the identity invariant that persists across regime changes.
hodge_decomposition(knm, phases, triangles=None) computes all three
components; pass an explicit triangles list of node triples to override
the default 3-clique fill.
Because the decomposition relies on two least-squares pseudoinverse
solves, exact cross-language parity is not attainable; the dispatcher
validates each accelerated backend against the NumPy reference within
rtol = 1e-10 / atol = 1e-12 (matching the spectral solver) and falls
back to NumPy only after the backend has returned a valid Hodge payload.
Direct accelerator boundary contract: the public Python dispatcher, public Rust
wrapper, and the Go, Julia, and Mojo Hodge adapters reject numeric-string
aliases before Python, NumPy, shared-library, Julia, or subprocess coercion.
The public surface applies the boundary to knm, phases, and explicit
triangle nodes; the direct adapters apply it to counts, flattened coupling,
phase, edge, triangle, backend-output, and Julia raw-return payloads. The
shared typed float64 path also rejects boolean aliases, complex or non-finite
payloads, malformed flattened n*n coupling buffers, phase vectors whose
length does not match n, and invalid oscillator counts before optional runtime
loading. After backend execution, the same output validator checks that
gradient, curl, and harmonic are finite real non-boolean (N, N) or
flattened N*N antisymmetric matrices before publication or parity fallback.
Malformed backend outputs raise immediately; fallback is reserved for validated
numerical parity mismatches. Empty Hodge systems return empty components
without requiring optional runtimes, matching the public Python special case.
hodge ¶
Combinatorial Hodge (Helmholtz–Hodge) decomposition of the Kuramoto current.
Exposes a 5-backend fallback chain.
Model¶
The oscillator network is treated as a simplicial complex
(V, E, T): vertices V are the oscillators, edges E are the
unordered pairs {i, j} (i < j) carrying non-zero symmetric
coupling, and triangles T are the 2-simplices (3-cliques of the
coupling graph, or an explicit user-supplied set).
The decomposed object is the alternating edge flow — the Kuramoto
coupling current on the reference orientation i → j (i < j):
f_{ij} = K^{sym}_{ij} · sin(θ_j − θ_i), K^{sym} = ½(K + Kᵀ)
which satisfies f_{ji} = −f_{ij} exactly, the defining property of a
1-cochain. Using the symmetric part of K keeps the current
alternating even when K encodes directed coupling; for the standard
symmetric Kuramoto model K^{sym} = K.
Boundary operators¶
B1(|V| × |E|) is the node–edge incidence∂₁: for edgee = (i, j)withi < j,B1[i, e] = −1andB1[j, e] = +1. The discrete gradient isgrad(s) = B1ᵀ swith(B1ᵀ s)_{ij} = s_j − s_i; the divergence is its adjointB1.B2(|E| × |T|) is the edge–triangle incidence∂₂: for trianglet = {i, j, k}withi < j < kthe simplicial boundary∂[i, j, k] = [j, k] − [i, k] + [i, j]givesB2[(i,j), t] = +1,B2[(j,k), t] = +1,B2[(i,k), t] = −1. The discrete curl iscurl(f) = B2ᵀ f.
Because ∂₁ ∂₂ = B1 B2 = 0 (boundary of a boundary is empty), the
gradient image im(B1ᵀ) and the curl image im(B2) are
L²-orthogonal.
Decomposition¶
With graph Laplacian L0 = B1 B1ᵀ and triangle Laplacian
L2 = B2ᵀ B2:
f_grad = B1ᵀ · L0⁺ · (B1 f) (curl-free, conservative)
f_curl = B2 · L2⁺ · (B2ᵀ f) (divergence-free, rotational)
f_harm = f − f_grad − f_curl (harmonic: ker of the Hodge
1-Laplacian L1 = B1ᵀB1 + B2 B2ᵀ)
The three components are mutually L²-orthogonal (Jiang, Lim, Yao & Ye 2011, Theorem 2.4). The harmonic part is both divergence-free and curl-free; its dimension equals the first Betti number
β₁ = |E| − rank(B1) − rank(B2)
i.e. the number of independent cycles not bounded by triangles. On a triangle-free graph with a cycle (e.g. a square) a circulating current is purely harmonic — the topological content that a plain symmetric/antisymmetric matrix split cannot represent.
Output¶
:class:HodgeResult returns the three components and the input current
as antisymmetric (N, N) flow matrices (M[i, j] is the flow on
edge i → j), the minimum-norm node potential s such that
f_grad = grad(s), and the integer betti_one (β₁).
Numerics¶
The decomposition needs two least-squares solves (L0⁺, L2⁺), so
exact cross-language parity is not attainable; the dispatcher validates
each accelerated backend against the NumPy reference within
rtol = 1e-10 / atol = 1e-12 (matching the spectral solver) and
falls back to NumPy on any valid numerical mismatch. Malformed backend
payloads fail closed before parity fallback.
Reference¶
Jiang, Lim, Yao & Ye 2011, Statistical ranking and combinatorial Hodge theory, Math. Program. 127 (1):203–244.
Classes¶
HodgeResult
dataclass
¶
HodgeResult(
gradient: FloatArray,
curl: FloatArray,
harmonic: FloatArray,
flow: FloatArray,
potential: FloatArray,
betti_one: int,
)
Decompose the Kuramoto coupling current into three L²-orthogonal flows.
Each flow matrix is antisymmetric: M[i, j] is the flow on the
oriented edge i → j and M[j, i] = −M[i, j].
Attributes¶
gradient: Conservative (curl-free) component ``grad(s)``.
curl: Rotational (divergence-free) component bounded by triangles.
harmonic: Topological residual in ``ker(L1)``; non-zero exactly
when the graph carries cycles not filled by triangles.
flow: The input alternating coupling current
``K^{sym}_{ij} · sin(θ_j − θ_i)``.
potential: Minimum-norm node potential ``s`` with
``gradient = grad(s)``.
betti_one: First Betti number ``β₁`` — the dimension of the
harmonic subspace.
Functions:¶
hodge_decomposition ¶
hodge_decomposition(
knm: FloatArray,
phases: FloatArray,
triangles: Sequence[Sequence[int]] | None = None,
) -> HodgeResult
Decompose the Kuramoto coupling current into orthogonal edge flows.
Parameters¶
knm : FloatArray
Square (N, N) coupling matrix; the symmetric part defines the edge support
and the current magnitude.
phases : FloatArray
(N,) oscillator phases.
triangles : Sequence[Sequence[int]] | None
Optional explicit 2-simplices as node triples; each must reference existing
edges. When omitted, all 3-cliques of the coupling graph are used.
Returns¶
HodgeResult
:class:HodgeResult with the three flow components as antisymmetric (N, N)
matrices, the input current, the node potential, and the first Betti number.
Source code in src/scpn_phase_orchestrator/coupling/hodge.py
573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 | |
Spectral Analysis¶
Algebraic graph-theoretic properties of the coupling network.
Functions¶
| Function | Returns | Description |
|---|---|---|
graph_laplacian(knm) |
NDArray |
L = D - W (combinatorial Laplacian) |
fiedler_value(knm) |
float |
λ₂(L) — algebraic connectivity |
fiedler_vector(knm) |
NDArray |
Eigenvector of λ₂ |
critical_coupling(omegas, knm) |
float |
K_c = max|Δω| / λ₂ |
fiedler_partition(knm) |
(list, list) |
Network bisection via Fiedler sign |
spectral_gap(knm) |
float |
λ₃ - λ₂ (cluster clarity) |
sync_convergence_rate(knm, omegas, γ_max) |
float |
μ = K·λ₂·cos(γ)/N |
Critical coupling estimate¶
The Dörfler-Bullo bound gives the minimum coupling strength for synchronisation:
where λ₂ is the Fiedler eigenvalue (algebraic connectivity). Networks with higher λ₂ synchronise more easily.
Direct accelerator boundary contract: Go, Julia, and Mojo spectral adapters use
one shared typed float64 validation path before loading shared-library, Julia,
or subprocess runtimes. The contract rejects boolean aliases, numeric-string
aliases, complex or non-finite flattened coupling payloads, non-vector inputs,
malformed n*n buffer lengths, and invalid oscillator counts. Empty spectral
problems return empty eigenvalue and Fiedler vectors without optional runtime
loading.
After backend execution, the same shared output validator is replayed for the
direct Go, Julia, and Mojo adapters and for the public optional primitive path:
returned eigenvalues and the Fiedler vector must be finite real non-boolean,
non-numeric-string vectors of length N, eigenvalues must be non-negative and
sorted ascending, and the Fiedler vector must be non-zero for N > 1.
Malformed backend physics payloads raise immediately; fallback remains reserved
for loader or runtime unavailability.
Public spectral helpers enforce the same real-valued boundary on coupling
matrices, frequency vectors, gamma_max, optional primitive eigensystem
outputs, and Rust fast-path scalar/vector returns. Boolean aliases are not
coerced into weights or frequencies, and complex-valued aliases are rejected
before NumPy can discard imaginary components. Numeric-string aliases are
rejected before Python, NumPy, Rust, Julia, Go, or Mojo can widen them into
ordinary floating-point weights, frequencies, scalar controls, or eigensystem
payloads.
spectral ¶
Symmetric eigendecomposition of the combinatorial graph Laplacian L = D − A.
Exposes a 5-backend fallback chain.
For asymmetric measured coupling, the undirected adjacency is the
reciprocal magnitude average A = (|W| + |Wᵀ|) / 2 with zeroed
diagonal. Degrees are then computed from A. This preserves the
combinatorial Laplacian contract: symmetric positive-semidefinite
L, zero row sums, and 1 ∈ ker L.
Primitive¶
spectral_eig(W_flat, n) → (eigvals, fiedler) — eigenvalues
ascending + Fiedler eigenvector (column 1 of the sorted
decomposition).
Backend chain¶
- Rust: pre-existing
fiedler_value_rust,fiedler_vector_rust,spectral_gap_rust,critical_coupling_rust,sync_convergence_rate_rustFFI fast paths are wired individually (each exposes a direct entry, no round-trip through the primitive). - Julia:
LinearAlgebra.eigen(Symmetric(L))— LAPACKdsyevunderneath, same numerics as NumPy. - Go:
gonum.org/v1/gonum/mat :: EigenSym— pure-Go symmetric solver, sub-1e-12drift vs LAPACK on well-conditioned Laplacians. - Mojo: LAPACK
dsyev_via thestd.ffi.OwnedDLHandlepattern (same as_lapack_test.mojo). - Python:
np.linalg.eigh— LAPACK-backed reference.
Derived functions (fiedler_value, fiedler_vector,
spectral_gap) route through the primitive on non-Rust
backends. critical_coupling and sync_convergence_rate
are composites that reuse fiedler_value.
References: Dörfler & Bullo 2014, Automatica 50(6):1539-1564; Dörfler & Bullo 2013, IEEE Proc. 102(10):1539-1564.
Functions:¶
graph_laplacian ¶
Combinatorial graph Laplacian L = D − A.
A is the reciprocal undirected magnitude adjacency
(|W| + |Wᵀ|) / 2 with zero diagonal, so asymmetric measured
couplings produce one symmetric edge weight before node degrees
are computed.
Parameters¶
knm : FloatArray
Coupling matrix K_nm, shape (N, N).
Returns¶
FloatArray
The combinatorial graph Laplacian L = D − A.
Source code in src/scpn_phase_orchestrator/coupling/spectral.py
spectral_eig ¶
Symmetric eigendecomposition of L = D − |W|.
Returns (eigvals ascending, fiedler vector). Thin wrapper
over the dispatched backend primitive; python reference
is a direct np.linalg.eigh.
Parameters¶
knm : FloatArray
Coupling matrix K_nm, shape (N, N).
Returns¶
tuple[FloatArray, FloatArray] The eigenvalues and eigenvectors of the symmetric Laplacian.
Source code in src/scpn_phase_orchestrator/coupling/spectral.py
fiedler_value ¶
Return the algebraic connectivity λ₂(L) (Dörfler-Bullo 2014).
Parameters¶
knm : FloatArray
Coupling matrix K_nm, shape (N, N).
Returns¶
float
The algebraic connectivity λ₂(L).
Source code in src/scpn_phase_orchestrator/coupling/spectral.py
fiedler_vector ¶
Return the λ₂ eigenvector partitioning the graph into clusters.
Parameters¶
knm : FloatArray
Coupling matrix K_nm, shape (N, N).
Returns¶
FloatArray
The λ₂ eigenvector partitioning the graph.
Source code in src/scpn_phase_orchestrator/coupling/spectral.py
critical_coupling ¶
Dörfler-Bullo critical coupling K_c = Δω / λ₂.
Returns +inf if the graph is disconnected
(λ₂ ≈ 0).
Parameters¶
omegas : FloatArray
Natural frequencies in rad/s, shape (N,).
knm : FloatArray
Coupling matrix K_nm, shape (N, N).
Returns¶
float
The Dörfler-Bullo critical coupling K_c.
Source code in src/scpn_phase_orchestrator/coupling/spectral.py
fiedler_partition ¶
Bisect the network using sign(v₂).
Returns (group_positive, group_negative) — indices
of oscillators in each partition.
Parameters¶
knm : FloatArray
Coupling matrix K_nm, shape (N, N).
Returns¶
tuple[list[int], list[int]]
The two index lists of the sign(v₂) bisection.
Source code in src/scpn_phase_orchestrator/coupling/spectral.py
spectral_gap ¶
Return the gap between λ₂ and λ₃ (two-cluster cleanliness).
Parameters¶
knm : FloatArray
Coupling matrix K_nm, shape (N, N).
Returns¶
float
The gap between λ₂ and λ₃.
Source code in src/scpn_phase_orchestrator/coupling/spectral.py
sync_convergence_rate ¶
Estimate the convergence rate from λ₂ (Dörfler-Bullo 2014 §III.B).
Parameters¶
knm : FloatArray
Coupling matrix K_nm, shape (N, N).
omegas : FloatArray
Natural frequencies in rad/s, shape (N,).
gamma_max : float
Maximum phase-lag γ across edges.
Returns¶
float The estimated synchronisation convergence rate.
Source code in src/scpn_phase_orchestrator/coupling/spectral.py
Three-Factor Hebbian Plasticity¶
Coupling adaptation rule inspired by biological synaptic plasticity:
Functions¶
-
compute_eligibility(phases) → NDArray(n,n): pairwise Hebbian tracecos(θ_j - θ_i)with zero diagonal. In-phase pairs → +1 (strengthen), anti-phase → -1 (weaken). -
three_factor_update(knm, eligibility, modulator, phase_gate, lr=0.01) → NDArray: applies the three-factor rule. Only modifies K when all three factors are active. The boundary enforces the same physical K_nm contract consumed by the UPDE engines:knmmust be finite, real, non-negative, square, and zero-diagonal;eligibilitymust be finite, real, square, zero-diagonal, and bounded in[-1, 1]. Negative modulation can depress coupling but is clamped at zero, and the result always keeps a zero self-coupling diagonal.
Three factors¶
- Eligibility (local): cos(Δθ) — pairwise Hebbian trace
- Modulator (global): scalar from L16 director layer (dopamine analog)
- Phase gate (global): Boolean from topological-integration gate
Reference: Friston 2005 on free energy and synaptic plasticity.
plasticity ¶
Validated three-factor plasticity updates for coupling matrices.
The module computes pairwise phase eligibility traces and applies a
modulator-gated Hebbian update to K_nm. Public functions reject boolean,
non-numeric, non-finite, non-vector, non-square, and shape-mismatched inputs so
plasticity cannot corrupt coupling state silently. The update preserves the
Kuramoto coupling contract by requiring non-negative zero-diagonal K_nm,
bounded zero-diagonal eligibility traces, and finite real scalar controls.
Functions:¶
compute_eligibility ¶
Pairwise Hebbian eligibility trace: cos(theta_j - theta_i).
Returns shape (n, n) with zero diagonal.
Parameters¶
phases : FloatArray
Oscillator phases in radians, shape (N,).
Returns¶
FloatArray
The pairwise Hebbian eligibility trace cos(θ_j − θ_i).
Source code in src/scpn_phase_orchestrator/coupling/plasticity.py
three_factor_update ¶
three_factor_update(
knm: FloatArray,
eligibility: FloatArray,
modulator: float,
phase_gate: bool,
lr: float = 0.01,
) -> FloatArray
Three-factor plasticity rule: K_ij += lr * eligibility_ij * M * gate.
Factors
- eligibility — pairwise phase correlation (Hebbian trace)
- modulator — scalar reward/error signal from L16 director
- phase_gate — boolean from the topological-integration gate
Friston 2005, Philos. Trans. R. Soc. B 360:815-836 (free energy & synaptic plasticity).
Parameters¶
knm : FloatArray current coupling matrix, shape (n, n). eligibility : FloatArray Hebbian trace, shape (n, n). modulator : float scalar neuromodulatory signal. phase_gate : bool if False, no update occurs (integration gate below threshold). lr : float learning rate.
Returns¶
FloatArray Updated coupling matrix (new array, does not mutate input).
Raises¶
TypeError If an argument has the wrong type. ValueError If the eligibility or coupling shapes mismatch.
Source code in src/scpn_phase_orchestrator/coupling/plasticity.py
Transfer Entropy Adaptive Coupling¶
Directed causal adaptation that breaks symmetry:
te_adapt_coupling(knm, phase_history, lr=0.01, decay=0.0, n_bins=8):
- Computes transfer entropy TE(i→j) for all pairs from phase history
- Updates coupling: pairs with causal influence get stronger
- Applies decay to forget old coupling structure
- Clamps K ≥ 0 and zeros diagonal
- Rejects boolean aliases in both
knmandphase_historybefore numeric coercion
Unlike Hebbian plasticity (symmetric), TE captures directed information flow — oscillator i can influence j without j influencing i.
Reference: Lizier 2012, "Local Information Transfer as Spatiotemporal Filter." Detailed documentation: TE Adaptive — detailed reference
te_adaptive ¶
Transfer-entropy-guided coupling adaptation for offline matrix updates.
te_adapt_coupling derives a directed transfer-entropy matrix from phase
history and combines it with the current coupling matrix under learning-rate
and decay parameters. The Python fallback clamps the returned coupling to
non-negative values and clears self-coupling; the optional Rust path preserves
the same dense N x N output contract. The helper returns a new matrix and
does not mutate live solver state or apply actuation.
Functions:¶
te_adapt_coupling ¶
te_adapt_coupling(
knm: FloatArray,
phase_history: FloatArray,
lr: float = 0.01,
decay: float = 0.0,
n_bins: int = 8,
) -> FloatArray
Adapt coupling matrix using transfer entropy as learning signal.
K_ij(t+1) = (1-decay) * K_ij(t) + lr * TE(i→j)
Strengthens coupling along causal information flow channels. Weakens where there is no causal influence.
Lizier 2012, "Local Information Transfer as a Spatiotemporal Filter for Complex Systems," Physical Review E 77(2):026110.
Parameters¶
knm : FloatArray current (n, n) coupling matrix. phase_history : FloatArray (n, T) recent phase trajectories. lr : float learning rate for TE-based update. decay : float coupling decay rate per update (0 = no decay). n_bins : int histogram bins for TE estimation.
Returns¶
FloatArray FloatArray The coupling matrix adapted by the transfer-entropy learning signal.
Raises¶
RuntimeError If the transfer-entropy backend fails.
Source code in src/scpn_phase_orchestrator/coupling/te_adaptive.py
E/I Balance¶
Computes and adjusts excitatory/inhibitory coupling balance. The aggregate
ratio summarises overall balance, while the four directed interaction-type
means resolve it into the source→target block strengths that Kuroki &
Mizuseki 2025 (Neural Computation 37 (7):1353–1372) identify as the
control parameters of the EI-Kuramoto synchronised / bistable /
desynchronised regimes.
EIBalance (dataclass)¶
| Field | Type | Description |
|---|---|---|
ratio |
float |
E/I balance ratio (excitatory_strength / inhibitory_strength) |
excitatory_strength |
float |
Mean coupling from excitatory sources over all targets |
inhibitory_strength |
float |
Mean coupling from inhibitory sources over all targets |
is_balanced |
bool |
True if 0.8 ≤ ratio ≤ 1.2 |
e_to_e |
float |
Mean E→E interaction-type coupling |
e_to_i |
float |
Mean E→I interaction-type coupling |
i_to_e |
float |
Mean I→E interaction-type coupling |
i_to_i |
float |
Mean I→I interaction-type coupling |
Each aggregate strength is the count-weighted blend of its two outgoing
interaction-type blocks (e.g. excitatory_strength blends e_to_e and
e_to_i over the target-group sizes).
Functions¶
compute_ei_balance(knm, excitatory_indices, inhibitory_indices) → EIBalanceadjust_ei_ratio(knm, excitatory_indices, inhibitory_indices, target_ratio=1.0) → NDArray— scales inhibitory coupling to achieve target ratio
Both helpers reject boolean aliases in knm before computing row means or
scaling inhibitory rows.
ei_balance ¶
Excitatory/inhibitory balance summaries and adjustment helpers.
The module measures mean outgoing coupling from caller-specified excitatory and inhibitory index sets, then optionally rescales inhibitory rows toward a target ratio. Rust acceleration is used when available; the NumPy fallback preserves the same shape and summary contract for examples and deterministic tests.
Classes¶
EIBalance
dataclass
¶
EIBalance(
ratio: float,
excitatory_strength: float,
inhibitory_strength: float,
is_balanced: bool,
e_to_e: float,
e_to_i: float,
i_to_e: float,
i_to_i: float,
)
Summary of excitatory and inhibitory coupling balance.
excitatory_strength / inhibitory_strength aggregate the mean
coupling from each source group over all targets, and ratio is their
quotient. The four *_to_* block means resolve this into the directed
interaction-type strengths (source group → target group) that Kuroki &
Mizuseki 2025 identify as the control parameters of the synchronised,
bistable, and desynchronised regimes of the EI-Kuramoto model.
Functions:¶
compute_ei_balance ¶
compute_ei_balance(
knm: FloatArray,
excitatory_indices: list[int],
inhibitory_indices: list[int],
) -> EIBalance
Compute E/I balance from coupling matrix and layer typing.
Kuroki & Mizuseki 2025, Neural Computation — E/I balance is the critical parameter for synchronization, not K or D.
ratio > 1: excitation-dominated (hypersynchrony risk) ratio < 1: inhibition-dominated (desynchronization risk) ratio ≈ 1: balanced (optimal for metastability)
Parameters¶
knm : FloatArray
Coupling matrix K_nm, shape (N, N).
excitatory_indices : list[int]
Indices of the excitatory oscillators.
inhibitory_indices : list[int]
Indices of the inhibitory oscillators.
Returns¶
EIBalance The E/I balance summary derived from the coupling typing.
Source code in src/scpn_phase_orchestrator/coupling/ei_balance.py
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 | |
adjust_ei_ratio ¶
adjust_ei_ratio(
knm: FloatArray,
excitatory_indices: list[int],
inhibitory_indices: list[int],
target_ratio: float = 1.0,
) -> FloatArray
Scale inhibitory coupling to achieve target E/I ratio.
Returns modified knm with inhibitory rows scaled so that E_strength / I_strength ≈ target_ratio.
Parameters¶
knm : FloatArray
Coupling matrix K_nm, shape (N, N).
excitatory_indices : list[int]
Indices of the excitatory oscillators.
inhibitory_indices : list[int]
Indices of the inhibitory oscillators.
target_ratio : float
Target excitatory/inhibitory coupling ratio.
Returns¶
FloatArray The coupling matrix with inhibitory weights scaled to the target ratio.
Source code in src/scpn_phase_orchestrator/coupling/ei_balance.py
Universal Bayesian Prior¶
Gaussian prior over coupling parameters, calibrated from the SCPN experimental programme.
CouplingPrior (dataclass)¶
| Field | Type | Default |
|---|---|---|
K_base |
float |
0.47 |
decay_alpha |
float |
0.25 |
K_c_estimate |
float |
0.0 |
UniversalPrior¶
default() → CouplingPrior— MAP estimate (K_base=0.47, α=0.25)sample(rng=None, seed=None) → CouplingPrior— random draw from prior;seedmust be an integer in the unsigned 64-bit range when providedestimate_Kc(omegas, n_layers) → CouplingPrior— combines prior with Dörfler-Bullo K_c for a finite one-dimensional frequency vectorlog_probability(K_base, decay_alpha) → float— unnormalised log-probability under Gaussian prior
estimate_Kc rejects boolean aliases in omegas, including NumPy boolean
scalars carried inside object arrays, before constructing the prior graph.
Detailed documentation: Universal Prior — detailed reference
prior ¶
Empirical domain-agnostic prior for coupling hyperparameters.
UniversalPrior provides default/sample/log-probability helpers for K_base
and decay_alpha, plus a Dörfler-Bullo-style critical-coupling estimate over
the spectral module. When the optional Rust kernel is importable the
log-probability path dispatches there; otherwise the NumPy scalar fallback
preserves the same Gaussian-prior contract.
Classes¶
CouplingPrior
dataclass
¶
Coupling configuration: base strength, decay, and K_c estimate.
UniversalPrior ¶
UniversalPrior(
K_base_mean: float = _K_BASE_MEAN,
K_base_std: float = _K_BASE_STD,
decay_alpha_mean: float = _DECAY_ALPHA_MEAN,
decay_alpha_std: float = _DECAY_ALPHA_STD,
)
Domain-agnostic coupling prior from 25-domainpack empirical distribution.
K_base ~ N(0.47, 0.09), decay_alpha ~ N(0.25, 0.07). Any new domain starts from this prior. Combined with Dörfler-Bullo K_c, collapses auto-tune from 5D optimization to 2D.
Source: R4-A3 cross-domain transfer analysis (Stankovski 2017, Rev. Mod. Phys.).
Source code in src/scpn_phase_orchestrator/coupling/prior.py
Methods:¶
sample ¶
Draw a random coupling configuration from the prior.
Pass rng for an explicit generator, or seed to create a
seeded one. If neither is given, a fresh unseeded generator is used
(NOT reproducible across sessions).
Parameters¶
rng : np.random.Generator | None
NumPy random generator, or None to seed from seed.
seed : int | None
Seed for the deterministic RNG.
Returns¶
CouplingPrior A coupling configuration sampled from the prior.
Source code in src/scpn_phase_orchestrator/coupling/prior.py
default ¶
Return the MAP (maximum a posteriori) estimate = the means.
Returns¶
CouplingPrior Return the MAP (maximum a posteriori) estimate = the means.
Source code in src/scpn_phase_orchestrator/coupling/prior.py
estimate_Kc ¶
Combine prior with Dörfler-Bullo K_c for given omegas.
K_c = max|ω_i - ω_j| / λ₂(L) where L is built from the prior's decay_alpha on a chain graph of n_layers.
Parameters¶
omegas : FloatArray
Natural frequencies in rad/s, shape (N,).
n_layers : int
Number of SCPN hierarchy layers.
Returns¶
CouplingPrior
The prior combined with the Dörfler-Bullo K_c for the given frequencies.
Raises¶
TypeError
If an argument has the wrong type.
ValueError
If omegas or the layer count is invalid.
Source code in src/scpn_phase_orchestrator/coupling/prior.py
log_probability ¶
Log-probability under the Gaussian prior (unnormalised).
Parameters¶
K_base : float Base coupling strength before spatial modulation. decay_alpha : float Exponential decay rate of the coupling across layer separation.
Returns¶
float The unnormalised log-probability of the configuration under the prior.
Raises¶
TypeError
If an argument has the wrong type.
ValueError
If K_base or decay_alpha is out of range.
Source code in src/scpn_phase_orchestrator/coupling/prior.py
HCP Connectome Generator¶
Neuroscience-realistic coupling matrices.
Synthetic generator¶
load_hcp_connectome(n_regions, seed=42) generates a matrix with:
- Intra-hemispheric: exponential distance decay
- Inter-hemispheric: corpus callosum pattern (homotopic connections)
- Default Mode Network: hub structure with elevated coupling
Real data bridge¶
load_neurolib_hcp(n_regions=80) loads real HCP structural connectivity
from the neurolib library. Supports n_regions from 2 to 80.
Both loaders validate optional-backend matrices before publication. Boolean,
complex/object-complex, and numeric-string aliases are rejected before
float64 conversion; finite real numeric-object matrices remain compatible.
Shape, finiteness, non-negativity, symmetry, and zero-diagonal constraints are
then replayed at the public Python boundary.
Performance: load_hcp_connectome(80) < 10 ms (Python), ~48 µs (Rust, 17.6x speedup).
Detailed documentation: HCP Connectome — detailed reference
connectome ¶
Synthetic and optional neurolib HCP coupling loaders.
load_hcp_connectome generates a deterministic HCP-inspired synthetic matrix
with explicit non-real-data provenance. load_neurolib_hcp is the optional real
HCP path and fails with an import error when neurolib is unavailable. Both
paths return non-negative zero-diagonal structural coupling matrices suitable
for examples, validation, and explicit downstream review.
Functions:¶
load_neurolib_hcp ¶
Load real HCP structural connectivity from neurolib.
Parameters¶
n_regions : int number of regions to return (max 80). If < 80, returns the top-left (n_regions, n_regions) submatrix.
Returns¶
FloatArray Symmetric non-negative coupling matrix, shape (n_regions, n_regions).
Raises¶
ImportError If neurolib is not installed. ValueError If n_regions < 2 or > 80.
Source code in src/scpn_phase_orchestrator/coupling/connectome.py
load_hcp_connectome ¶
Generate a synthetic HCP-inspired coupling matrix.
Parameters¶
n_regions : int number of cortical regions (must be >= 2, even recommended).
Returns¶
FloatArray Symmetric coupling matrix, shape (n_regions, n_regions), zero diagonal.
Source code in src/scpn_phase_orchestrator/coupling/connectome.py
Rust FFI acceleration¶
spo_kernel.PyCouplingBuilder provides Rust-accelerated K_nm
construction. The Python implementation is the reference; the Rust path
is selected automatically when spo_kernel is importable. Parity is
verified in tests/test_rust_python_parity_performance.py.
Rust builder returns are inspected before numeric conversion: boolean,
complex/object-complex, and numeric-string K_nm or alpha aliases are rejected
and trigger the documented NumPy fallback. Finite real numeric-object matrices
remain compatible; shape, finiteness, non-negativity, symmetry, and zero-
diagonal checks still run before publication.
Performance summary¶
| Operation | Budget | Measured |
|---|---|---|
CouplingBuilder.build(100) |
< 10 ms | ~2 ms |
build_scpn_physics() |
< 5 ms | ~1 ms |
estimate_from_distances(64) |
< 5 ms | ~0.5 ms |
load_hcp_connectome(80) |
< 10 ms | ~3 ms |
validate_knm(64) |
< 1 ms | ~0.1 ms |
graph_laplacian(64) |
< 1 ms | ~0.007 ms |
fiedler_value(64) |
< 1 ms | ~0.12 ms |
Spatial coupling modulation¶
SpatialCouplingModulator is the public PHA-C.1 coupling surface for systems where the effective phase coupling must depend on moving geometry instead of static oscillator labels. It turns a zero-diagonal base K_nm matrix and a position matrix into a physically constrained modulated coupling matrix.
Use it when spatial proximity, mobile agents, tissue geometry, sensor placement, or edge-node distance changes the strength of phase transfer. The default kernel is 1 / (1 + distance), which is bounded, finite at zero separation, symmetric for Euclidean positions, and preserves the zero self-coupling diagonal required by the oscillator engines.
The module also exposes exponential, power-law, and inverse-distance kernels. The inverse-distance form is reserved for Swarmalator compatibility and uses an epsilon-regularised denominator so the historical kernel remains bit-true without introducing singularities.
The reference implementation is NumPy. Rust, Go, Julia, and Mojo adapters are validated as optional accelerators and must reproduce the same invariants before their output is accepted: finite real-valued matrices, exact shape or flat cardinality, non-boolean and non-complex values, non-negative entries, zero diagonal, and symmetry preservation for symmetric inputs. Public positions, base coupling matrices, scalar decay controls, direct accelerator counts/forms/flat buffers, optional backend outputs, and raw Julia returns reject numeric-string aliases before float coercion. The public dispatcher preserves matrix-shaped output for callers after replaying the shared direct output validator; optional backend fallback remains limited to loader or runtime unavailability.
See Coupling - Spatial Modulator for examples, backend notes, and the benchmark contract.