Oscillators¶
Phase extraction from raw signals via canonical Physical (P), Informational (I), and Symbolic (S) channels, plus named extension channels. The P/I/S decomposition is the default abstraction that makes SPO domain-agnostic, but deployments are not limited to three channels: any signal that exhibits periodic or quasi-periodic behaviour can map onto one or more named channels.
Pipeline position¶
Raw signals ──→ PhysicalExtractor ──→ PhaseState(θ, ω, quality)
Event streams ──→ InformationalExtractor ──→ PhaseState(θ, ω, quality)
Sequences ──→ SymbolicExtractor ──→ PhaseState(θ, ω, quality)
│
↓
PhaseQualityScorer
│
┌─────────┼──────────┐
↓ ↓ ↓
θ array ω array quality mask
│ │ │
↓ ↓ ↓
UPDEEngine.step(phases, omegas, knm * mask, ...)
Oscillators are the input adapters of the SPO pipeline. They convert
raw domain signals into the (θ, ω) vectors that the engine requires.
Quality scores gate which oscillators participate in coupling.
The PIS Model¶
Every domain signal decomposes into one or more oscillator channels. The canonical channels are:
| Channel | Signal type | Extraction method | Example domains |
|---|---|---|---|
| P (Physical) | Continuous waveforms | Hilbert transform | EEG, ECG, vibration, voltage, plasma |
| I (Informational) | Event streams, rates | Inter-event interval | Network traffic, API calls, manufacturing |
| S (Symbolic) | Categorical sequences | Ring mapping | Protocols, language, music, genetics |
Not every domain uses all three canonical channels. A pure physics
domain (tokamak plasma) might use only P. A pure IT domain
(microservices) might use only I. Larger deployments can add named
extension channels such as thermal, market_sentiment, or
operator_intent while preserving the same PhaseState contract. The
binding specification declares which channels are active.
Phase State¶
PhaseState (dataclass)¶
| Field | Type | Range | Description |
|---|---|---|---|
theta |
float |
[0, 2π) | Phase angle |
omega |
float |
R | Instantaneous frequency (rad/s) |
amplitude |
float |
≥ 0 | Signal strength (SNR proxy) |
quality |
float |
[0, 1] | Extraction confidence |
channel |
str |
Identifier | Binding channel (P, I, S, or named extension) |
node_id |
str |
— | Unique oscillator identifier |
Quality scores gate downstream processing: low-quality oscillators are downweighted in coupling and excluded from regime classification.
Extractor Interface¶
All channel extractors implement the PhaseExtractor abstract base class:
from numpy.typing import NDArray
import numpy as np
FloatArray = NDArray[np.float64]
class PhaseExtractor(ABC):
@abstractmethod
def extract(self, signal: FloatArray, sample_rate: float) -> list[PhaseState]: ...
@abstractmethod
def quality_score(self, phase_states: list[PhaseState]) -> float: ...
The extract method receives a raw signal window and sample rate,
and returns one or more PhaseState objects. The quality_score
method computes an aggregate quality for the extraction.
Physical Extraction (P)¶
PhysicalExtractor¶
Uses the analytic signal (Hilbert transform) to decompose a real-valued waveform into instantaneous phase and amplitude:
Quality metric¶
_envelope_quality(signal, analytic) returns quality based on the
coefficient of variation (CV) of the analytic signal envelope:
Clean sinusoids have near-constant envelope (CV ≈ 0, quality ≈ 1.0). Noisy signals have variable envelope (high CV, low quality).
Validation¶
- Rejects empty signals, single-sample signals, and 2-D arrays
with
ValueError("1-D with >= 2 samples") - Returns
channel = "P",node_idfrom constructor
Rust acceleration¶
When spo_kernel is importable, uses spo_kernel.physical_extract()
for the core computation. Python fallback uses scipy Hilbert transform.
Parity verified in tests/test_oscillator_physical.py::test_rust_python_parity.
Performance: extract(1s @ 1kHz) < 5 ms.
physical ¶
Physical-channel phase extraction from continuous numeric waveforms.
PhysicalExtractor validates finite one-dimensional real signals and positive
sample rates, then derives instantaneous phase, angular frequency, amplitude,
and envelope-quality metadata via the Hilbert transform. Optional Rust
acceleration preserves the same PhaseState contract as the NumPy/SciPy path.
Classes¶
PhysicalExtractor ¶
PhysicalExtractor(
node_id: str = "phys_0",
*,
band: tuple[float, float]
| Sequence[float]
| None = None,
filter_order: int = 4,
edge_trim: int | None = None,
)
Bases: PhaseExtractor
Extracts instantaneous phase from continuous waveforms via Hilbert transform.
By default the extractor Hilbert-transforms the raw broadband signal and reports the trailing (endpoint) instantaneous phase — the historical behaviour, retained bit-for-bit so existing bindings and sealed evidence are unchanged. Two optional, opt-in refinements are available for callers who need a cleaner estimate:
band=(low_hz, high_hz)applies a zero-phase Butterworth band-pass (scipy.signal.filtfilt) before the Hilbert transform, isolating the phase of interest instead of mixing all spectral content.edge_trim(or, when a band is set, an automatic filter-transient trim) discards edge samples where the FFT-Hilbert and filtfilt transients are worst, so the reported phase is taken from the last reliable interior sample rather than the artefact-prone endpoint.
Both refinements are applied identically before the NumPy and Rust paths, so the accelerated kernel needs no change and stays bit-parity with the reference.
Source code in src/scpn_phase_orchestrator/oscillators/physical.py
Methods:¶
extract ¶
Extract instantaneous phase from a 1-D waveform via Hilbert transform.
Parameters¶
signal : FloatArray
Input signal, shape (T,).
sample_rate : float
Sampling rate in Hz.
Returns¶
list[PhaseState] Instantaneous phase from a 1-D waveform via Hilbert transform.
Source code in src/scpn_phase_orchestrator/oscillators/physical.py
quality_score ¶
Mean extraction quality across phase states.
Parameters¶
phase_states : list[PhaseState] Extracted per-oscillator phase states.
Returns¶
float Mean extraction quality across phase states.
Source code in src/scpn_phase_orchestrator/oscillators/physical.py
Informational Extraction (I)¶
InformationalExtractor¶
Converts event timestamps into phase oscillators:
- Compute inter-event intervals: τ_k = t_k - t_{k-1}
- Median frequency: f = 1 / median(τ)
- Angular frequency: ω = 2πf
- Phase: θ = (2πf × total_duration) mod 2π
- Amplitude: mean instantaneous frequency
- Quality: 1/(1 + CV(τ)) where CV = std(τ)/mean(τ)
Edge cases¶
| Input | Result |
|---|---|
| Single timestamp | θ=0, ω=0, quality=0 |
| Identical timestamps | θ=0, ω=0, quality=0 |
| Two timestamps | Valid extraction from one interval |
| Regular events | quality ≈ 1.0 |
| Irregular events | quality < 0.9 |
Performance: extract(100 timestamps) < 500 μs.
informational ¶
Informational-channel phase extraction from event timestamp streams.
InformationalExtractor treats sorted event timestamps as a cadence signal,
deriving phase from inter-event intervals and reporting interval-regularity
quality. Non-numeric, boolean, complex, non-finite, or unsorted timestamp
inputs fail before they can seed runtime phase state.
Classes¶
InformationalExtractor ¶
Bases: PhaseExtractor
Extracts phase from event timestamps (spike trains, discrete events).
Converts inter-event intervals to instantaneous frequency, then derives phase via cumulative integral of frequency.
Source code in src/scpn_phase_orchestrator/oscillators/informational.py
Methods:¶
extract ¶
Extract phase states from event timestamps.
Parameters¶
signal : FloatArray 1-D array of event timestamps in seconds (sorted ascending). sample_rate : float not used for timestamps but kept for interface consistency.
Returns¶
list[PhaseState] The result.
Raises¶
ValueError If the inputs are invalid or inconsistent.
Source code in src/scpn_phase_orchestrator/oscillators/informational.py
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | |
quality_score ¶
Mean interval-regularity quality across phase states.
Parameters¶
phase_states : list[PhaseState] Extracted per-oscillator phase states.
Returns¶
float Mean interval-regularity quality across phase states.
Source code in src/scpn_phase_orchestrator/oscillators/informational.py
Symbolic Extraction (S)¶
SymbolicExtractor¶
| Parameter | Type | Description |
|---|---|---|
n_states |
int |
Vocabulary size (≥ 2) |
node_id |
str |
Oscillator identifier |
mode |
str |
"ring" or "graph" |
Ring mode¶
Maps state index s to phase: θ_s = 2πs / N (mod 2π). Equispaced phases with gap = 2π/N.
Graph mode¶
Cumulative transition distances normalised to [0, 2π).
Quality scoring¶
| Transition type | Quality |
|---|---|
| Single step ( | Δs |
| Stalled (Δs = 0) | 0.2 |
| Large jump ( | Δs |
| First state (no prior) | 0.5 |
Omega derivation¶
ω is derived from consecutive phase differences divided by dt (1/sample_rate). For ring mode with single steps: ω = 2π/(N·dt).
Performance: extract(1000 states) < 1 ms.
symbolic ¶
Symbolic-channel phase extraction from discrete state sequences.
SymbolicExtractor maps integer state indices onto ring or graph-walk phases
for semiotic and finite-state systems. It rejects invalid state counts,
non-integer signals, boolean arrays, complex arrays, and invalid sample rates
so symbolic phases remain explicit and deterministic.
Classes¶
SymbolicExtractor ¶
SymbolicExtractor(
n_states: int,
node_id: str = "sym",
mode: str = "ring",
*,
initial_transition_quality: float = SYMBOLIC_INITIAL_TRANSITION_QUALITY_BASELINE,
)
Bases: PhaseExtractor
Phase extraction from discrete symbolic state sequences.
Maps discrete state indices to phases on the unit circle via theta = 2pis/N (ring-phase) or via graph-walk position.
Configure the symbolic oscillator over n_states discrete states.
Parameters¶
n_states : int total number of discrete states N. node_id : str identifier for generated PhaseState objects. mode : str "ring" for ring-phase, "graph" for graph-walk phase. initial_transition_quality : float quality assigned when no transition evidence exists yet (first sample / insufficient history).
Source code in src/scpn_phase_orchestrator/oscillators/symbolic.py
Methods:¶
extract ¶
Map discrete state indices to phases on the unit circle.
Parameters¶
signal : FloatArray | IntArray
Input signal, shape (T,).
sample_rate : float
Sampling rate in Hz.
Returns¶
list[PhaseState] Discrete state indices to phases on the unit circle.
Source code in src/scpn_phase_orchestrator/oscillators/symbolic.py
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 | |
quality_score ¶
Mean transition quality across phase states.
Parameters¶
phase_states : list[PhaseState] Extracted per-oscillator phase states.
Returns¶
float Mean transition quality across phase states.
Source code in src/scpn_phase_orchestrator/oscillators/symbolic.py
Wavelet-ridge extractor (physical channel)¶
WaveletExtractor is a band-adaptive alternative to the Hilbert extractor: it
computes a complex Morlet continuous wavelet transform across a log-spaced
frequency bank, selects the dominant energy ridge over a cone-of-influence-safe
interior region, and reads the analytic phase along that ridge. The terminal
phase is extrapolated from a COI-safe interior sample at the ridge frequency, so
the corrupted signal edge is avoided. The Morlet wavelet is
ψ(t) = π^(−1/4)·exp(i·ω₀·t/s)·exp(−(t/s)²/2)/√s with ω₀ = 6; a pure-NumPy
path is used because SciPy 1.15 removed cwt/morlet2. Prefer it over Hilbert
when a dominant oscillation sits in broadband noise or slow drift.
wavelet ¶
Physical-channel phase extraction via a complex Morlet wavelet ridge.
WaveletExtractor computes a continuous wavelet transform with a bank of complex
Morlet wavelets, selects the dominant scale (the energy ridge over a
cone-of-influence-safe interior region), and reads the analytic phase along that
ridge. Unlike the broadband Hilbert transform, the wavelet ridge is band-adaptive
and therefore robust to broadband noise and slow drift around a dominant
oscillation. The terminal phase is extrapolated from a COI-safe interior sample
using the ridge frequency, avoiding the corrupted signal edge.
The complex Morlet is psi(t) = pi^(-1/4) * exp(i*w0*t/s) * exp(-(t/s)^2/2) /
sqrt(s) with w0 = 6 (the standard admissibility-approximating choice). The
peak frequency of scale s is f = w0 * fs / (2*pi*s). A pure-Python/NumPy
path is used because SciPy 1.15 removed cwt/morlet2 and PyWavelets is not
a required dependency; the extractor preserves the PhaseState contract.
Classes¶
WaveletExtractor ¶
Bases: PhaseExtractor
Extracts instantaneous phase from a waveform via a complex Morlet ridge.
Source code in src/scpn_phase_orchestrator/oscillators/wavelet.py
Methods:¶
extract ¶
Extract phase from a 1-D waveform via the dominant Morlet ridge.
Parameters¶
signal : FloatArray
Input signal, shape (T,).
sample_rate : float
Sampling rate in Hz.
Returns¶
list[PhaseState]
A single PhaseState on channel "P" carrying the terminal phase,
the ridge angular frequency, amplitude, and ridge-regularity quality.
Source code in src/scpn_phase_orchestrator/oscillators/wavelet.py
quality_score ¶
Mean extraction quality across phase states.
Parameters¶
phase_states : list[PhaseState] Extracted per-oscillator phase states.
Returns¶
float Mean extraction quality across phase states.
Source code in src/scpn_phase_orchestrator/oscillators/wavelet.py
Zero-crossing extractor (physical channel)¶
ZeroCrossingExtractor recovers phase from interpolated zero crossings: each
crossing is a half-cycle (π of phase advance), absolute phase is anchored to
the crossing direction (a rising crossing ≡ 0, a falling crossing ≡ π, the
sine convention), and a Schmitt-trigger deadband (a fraction of the RMS)
suppresses spurious noise-induced crossings. Angular frequency comes from the
mean half-period and quality from the regularity of the half-period intervals.
Prefer it for sharply non-sinusoidal periodic signals where a single analytic
phase is ill-defined.
zero_crossing ¶
Physical-channel phase extraction from zero crossings.
ZeroCrossingExtractor recovers instantaneous phase from a real waveform by
locating its zero crossings (with sub-sample linear interpolation), treating each
crossing as a half-cycle (pi of phase advance), and anchoring absolute phase
to the crossing direction (rising crossing ≡ phase 0, falling crossing ≡ phase
pi, matching the sine convention). It is robust to a constant offset (the
mean is removed) and reports an angular frequency from the mean half-period and a
quality from the regularity of the half-period intervals. The extractor produces
the same PhaseState contract as the Hilbert-based PhysicalExtractor but is
preferable for sharply non-sinusoidal periodic signals where a single dominant
analytic phase is ill-defined.
Classes¶
ZeroCrossingExtractor ¶
Bases: PhaseExtractor
Extracts instantaneous phase from a waveform via interpolated zero crossings.
Source code in src/scpn_phase_orchestrator/oscillators/zero_crossing.py
Methods:¶
extract ¶
Extract phase from a 1-D waveform via interpolated zero crossings.
Parameters¶
signal : FloatArray
Input signal, shape (T,).
sample_rate : float
Sampling rate in Hz.
Returns¶
list[PhaseState]
A single PhaseState on channel "P" carrying the terminal phase,
angular frequency, amplitude, and crossing-regularity quality.
Source code in src/scpn_phase_orchestrator/oscillators/zero_crossing.py
quality_score ¶
Mean extraction quality across phase states.
Parameters¶
phase_states : list[PhaseState] Extracted per-oscillator phase states.
Returns¶
float Mean extraction quality across phase states.
Source code in src/scpn_phase_orchestrator/oscillators/zero_crossing.py
Quality Scoring¶
PhaseQualityScorer¶
| Method | Signature | Description |
|---|---|---|
score |
(states) → float |
Amplitude-weighted mean quality |
detect_collapse |
(states, threshold=0.1) → bool |
True if >50% below threshold |
downweight_mask |
(states, min_quality=0.3) → NDArray[np.float64] |
Weight array, zeros below min |
Downweight mask in pipeline¶
The mask is applied to the coupling matrix before engine evaluation:
mask = scorer.downweight_mask(states, min_quality=0.3)
knm_gated = knm * mask[:, None] * mask[None, :]
# Low-quality oscillators decoupled from high-quality ones
This prevents noisy phase estimates from corrupting the synchronisation dynamics. Only oscillators with quality ≥ min_quality participate.
Performance: downweight_mask(100 states) < 50 μs.
quality ¶
Quality aggregation and collapse detection for extracted phase states.
The scorer turns per-oscillator extraction quality into weighted aggregate signals for runtime gating and diagnostics. Empty state sets collapse to safe defaults, low-quality states can be masked, and amplitude weighting prevents near-zero signals from dominating quality summaries.
Classes¶
PhaseQualityScorer ¶
Aggregate quality scoring and collapse detection for phase state arrays.
Source code in src/scpn_phase_orchestrator/oscillators/quality.py
Methods:¶
score ¶
Weighted average quality across all phase states.
Parameters¶
phase_states : list[PhaseState] Extracted per-oscillator phase states.
Returns¶
float Weighted average quality across all phase states.
Source code in src/scpn_phase_orchestrator/oscillators/quality.py
detect_collapse ¶
Return True if quality is below threshold for the majority of states.
Parameters¶
phase_states : list[PhaseState] Extracted per-oscillator phase states. threshold : float Decision threshold.
Returns¶
bool True if quality is below threshold for the majority of states.
Raises¶
ValueError If the inputs are invalid or inconsistent.
Source code in src/scpn_phase_orchestrator/oscillators/quality.py
downweight_mask ¶
Weight array in [0,1], zeros below min_quality.
Parameters¶
phase_states : list[PhaseState] Extracted per-oscillator phase states. min_quality : float Minimum extraction quality.
Returns¶
FloatArray Weight array in [0,1], zeros below min_quality.
Raises¶
ValueError If the inputs are invalid or inconsistent.
Source code in src/scpn_phase_orchestrator/oscillators/quality.py
Base Types¶
base ¶
Shared phase-state contract and extractor interface.
PhaseState is the typed handoff record from channel-specific extractors into
binding, quality scoring, and UPDE initialisation. PhaseExtractor defines the
minimal extraction/quality interface implemented by physical waveform,
informational event, and symbolic state-sequence extractors.
Classes¶
PhaseState
dataclass
¶
PhaseState(
theta: float,
omega: float,
amplitude: float,
quality: float,
channel: str,
node_id: str,
)
Extracted phase, frequency, amplitude, and quality for one oscillator.
PhaseExtractor ¶
Bases: ABC
Abstract base for signal-to-phase extraction algorithms.
Methods:¶
extract
abstractmethod
¶
Extract phase states from a raw signal at the given sample rate.
Parameters¶
signal : FloatArray
Input signal, shape (T,).
sample_rate : float
Sampling rate in Hz.
Returns¶
list[PhaseState] Phase states from a raw signal at the given sample rate.
Source code in src/scpn_phase_orchestrator/oscillators/base.py
quality_score
abstractmethod
¶
Aggregate quality metric (0..1) over a set of extracted phase states.
Parameters¶
phase_states : list[PhaseState] Extracted per-oscillator phase states.
Returns¶
float Aggregate quality metric (0..1) over a set of extracted phase states.
Source code in src/scpn_phase_orchestrator/oscillators/base.py
Phase Initialisation¶
Utilities for deterministic and random initial phase generation used in simulation setup and reproducible experiment seeds.
init_phases ¶
Synthetic binding-aware initial phase generation.
extract_initial_phases uses the binding's oscillator families to generate
small deterministic synthetic signals per P/I/S channel, extract their phases,
and produce a finite initial phase vector for UPDE startup. It validates omega
length, seed, symbolic state counts, and extractor families before falling back
to seeded random phases for unsupported channel families.
Classes¶
Functions:¶
extract_initial_phases ¶
Extract initial phases from channels defined in binding_spec.
For each oscillator, generates a synthetic signal matching the family channel or extractor semantics and extracts the phase. Falls back to random phase if extraction fails.
Returns (n_osc,) array of initial phases in [0, 2*pi).
Parameters¶
spec : BindingSpec
The binding specification.
omegas : FloatArray
Natural frequencies in rad/s, shape (N,).
seed : int
Seed for the deterministic RNG.
Returns¶
FloatArray Initial phases from channels defined in binding_spec.
Source code in src/scpn_phase_orchestrator/oscillators/init_phases.py
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | |
Phase reduction¶
Model-free phase reduction: a dependency-light evaluator of a trained phase
autoencoder (see nn.phase_autoencoder) that recovers the asymptotic phase
Θ(x) and the phase-sensitivity function Z(θ) — the phase response curve —
from frozen NumPy weights, with no JAX on the control path.
phase_reduction ¶
A pure-NumPy evaluator for a trained phase autoencoder.
The phase autoencoder (nn.phase_autoencoder) is trained with JAX, but the
asymptotic phase and the phase-sensitivity function it learns are needed on the
control path, which must stay dependency-light. This module evaluates the trained
encoder/decoder — frozen to plain NumPy weights — without importing JAX.
Given the trained encoder g(x) = (Ỹ₁, Ỹ₂, Ỹ₃) (a ReLU multilayer
perceptron), the asymptotic phase is Θ(x) = atan2(Ỹ₂, Ỹ₁) (the unit-circle
normalisation cancels inside atan2). The phase-sensitivity function — the
gradient of the phase with respect to the state, evaluated on the limit cycle —
is
Z(θ) = ∇ₓ Θ |_{x = decode(cos θ, sin θ, 0)}
= (∂Θ/∂Ỹ₁) ∇ₓ Ỹ₁ + (∂Θ/∂Ỹ₂) ∇ₓ Ỹ₂,
with ∂Θ/∂Ỹ₁ = −Ỹ₂/(Ỹ₁²+Ỹ₂²), ∂Θ/∂Ỹ₂ = Ỹ₁/(Ỹ₁²+Ỹ₂²) and the encoder
Jacobian computed by exact reverse-mode through the ReLU network. This is the
phase response curve (Nakao 2016) recovered model-free from data.
References¶
- Yawata, Fukami, Taira & Nakao 2024, Chaos 34, 063111 — phase autoencoder.
- Nakao 2016, Contemp. Phys. 57, 188 — phase reduction theory.
Classes¶
PhaseReductionWeights
dataclass
¶
PhaseReductionWeights(
encoder_weights: tuple[FloatArray, ...],
encoder_biases: tuple[FloatArray, ...],
decoder_weights: tuple[FloatArray, ...],
decoder_biases: tuple[FloatArray, ...],
omega: float,
decay: float,
state_dim: int,
)
Frozen encoder/decoder weights and (ω, λ) of a phase autoencoder.
Parameters¶
encoder_weights, encoder_biases : tuple[numpy.ndarray, ...]
Per-layer encoder weight matrices (out, in) and bias vectors.
decoder_weights, decoder_biases : tuple[numpy.ndarray, ...]
Per-layer decoder weight matrices and bias vectors.
omega : float
The learned angular frequency ω.
decay : float
The learned amplitude decay λ < 0.
state_dim : int
The oscillator state dimension n.
PhaseReducer
dataclass
¶
A dependency-light evaluator of a trained phase autoencoder.
Parameters¶
weights : PhaseReductionWeights
The frozen encoder/decoder weights and (ω, λ).
Attributes¶
omega
property
¶
decay
property
¶
Methods:¶
asymptotic_phase ¶
Return the asymptotic phase Θ(x) = atan2(Ỹ₂, Ỹ₁) of a state.
Parameters¶
state : numpy.ndarray
The oscillator state x of shape (n,).
Returns¶
float
The asymptotic phase in (−π, π].
Raises¶
ValueError
If state is not a finite vector of length state_dim.
Source code in src/scpn_phase_orchestrator/oscillators/phase_reduction.py
encode_observables ¶
Lift a batch of states to the unnormalised encoder latent (K, 3).
These are the model-free Koopman observables: the learned coordinate in
which the phase autoencoder's dynamics are (approximately) linear, so a
:class:~scpn_phase_orchestrator.monitor.koopman_edmd.KoopmanPredictor
fitted in them captures nonlinear oscillator dynamics that the analytic
dictionaries miss.
Parameters¶
states : numpy.ndarray
A batch of states of shape (K, state_dim).
Returns¶
numpy.ndarray
The unnormalised latent batch of shape (K, 3).
Raises¶
ValueError
If states is not a finite (K, state_dim) array.
Source code in src/scpn_phase_orchestrator/oscillators/phase_reduction.py
reconstruct ¶
Reconstruct the on-cycle state at a phase via the decoder.
Parameters¶
phase : float
The phase θ to reconstruct on the limit cycle.
Returns¶
numpy.ndarray
The decoded state decode(cos θ, sin θ, 0) of shape (n,).
Source code in src/scpn_phase_orchestrator/oscillators/phase_reduction.py
phase_sensitivity ¶
Return the phase-sensitivity function Z(θ) = ∇ₓ Θ on the cycle.
Parameters¶
phase : float
The phase θ on the limit cycle at which to evaluate Z.
Returns¶
numpy.ndarray
The phase response curve value Z(θ) of shape (n,).
Source code in src/scpn_phase_orchestrator/oscillators/phase_reduction.py
Extractor factory¶
build_extractor maps a binding extractor_type — a channel alias
(physical/informational/symbolic) or a canonical algorithm name
(hilbert/wavelet/zero_crossing/event/ring/graph) — to the concrete
PhaseExtractor that implements it. Aliases resolve through
resolve_extractor_type; an unknown type raises ValueError (fail-closed)
rather than silently degrading to a default algorithm.
factory ¶
Construct the phase extractor named by a binding extractor_type.
build_extractor maps a domainpack extractor_type (a channel alias such as
physical/informational/symbolic or a canonical algorithm name such as
hilbert/wavelet/zero_crossing/event/ring/graph) to the
concrete PhaseExtractor that implements it. Aliases are resolved through
resolve_extractor_type; an unknown type raises ValueError (fail-closed)
rather than silently degrading to a default algorithm.
Classes¶
Functions:¶
build_extractor ¶
build_extractor(
extractor_type: str,
*,
node_id: str = "extractor",
n_states: int = 2,
config: Mapping[str, object] | None = None,
) -> PhaseExtractor
Build the PhaseExtractor for a binding extractor_type.
Parameters¶
extractor_type : str
A channel alias (physical/informational/symbolic) or a
canonical algorithm name (hilbert/wavelet/zero_crossing/
event/ring/graph).
node_id : str
Identifier stamped onto the extractor's emitted PhaseState records.
n_states : int
Number of discrete states for symbolic (ring/graph) extractors;
ignored by the continuous and event extractors.
config : Mapping[str, object] | None
Optional oscillator-family config from the binding spec. For the
physical/hilbert extractor its band/filter_order/edge_trim
keys select the opt-in zero-phase band-pass and edge-trim; other extractors
ignore it.
Returns¶
PhaseExtractor
The extractor implementing extractor_type.
Raises¶
ValueError
If extractor_type does not resolve to a known algorithm.
Source code in src/scpn_phase_orchestrator/oscillators/factory.py
Cross-channel composition¶
A domain can use multiple channels simultaneously. The binding spec declares which channels are active and how they map to oscillator indices:
layers:
- name: voltage
channel: P
indices: [0, 1, 2, 3]
- name: event_rate
channel: I
indices: [4, 5]
- name: protocol_state
channel: S
indices: [6, 7]
All channels produce PhaseState with the same fields, so the engine
treats them uniformly. The channel field enables channel-aware
analysis (e.g., computing R separately for P and I oscillators).
Rust FFI acceleration¶
PhysicalExtractor uses spo_kernel.physical_extract() when the
Rust extension is installed. The Rust path computes the Hilbert
transform and phase extraction in a single pass, avoiding Python/NumPy
overhead for large signals.
Parity is verified in tests/test_oscillator_physical.py::test_rust_python_parity
with tolerance atol=1e-10 for phase, rtol=0.01 for frequency.
Performance summary¶
| Operation | Budget | Rust | Notes |
|---|---|---|---|
PhysicalExtractor.extract(1s @ 1kHz) |
< 5 ms | < 1 ms | Hilbert transform |
InformationalExtractor.extract(100 ts) |
< 500 μs | — | numpy operations |
SymbolicExtractor.extract(1000 states) |
< 1 ms | — | ring mapping |
PhaseQualityScorer.downweight_mask(100) |
< 50 μs | — | array comparison |
Domain examples¶
Neuroscience (EEG)¶
# 64-channel EEG → 64 P-channel oscillators
extractor = PhysicalExtractor(node_id="eeg")
for ch in range(64):
states = extractor.extract(eeg_data[ch], fs=256.0)
phases[ch] = states[0].theta
omegas[ch] = states[0].omega
Microservices (queue depths)¶
# 12 services → 12 I-channel oscillators
extractor = InformationalExtractor(node_id="svc")
for svc in services:
timestamps = svc.request_timestamps()
states = extractor.extract(timestamps, sample_rate=0.0)
phases[svc.id] = states[0].theta