Skip to content

Coupling — Spatial Modulator

SpatialCouplingModulator is the reusable distance-coupling primitive for moving oscillator systems. It converts a reviewed base coupling matrix into a position-aware coupling matrix before UPDE, Swarmalator, Doppler, or moving-frame engines consume it.

Mathematical contract

For the default moving-agent and MIF contract:

K'_ij = K_base * K_ij / (1 + ||x_i - x_j||),   K'_ii = 0

The diagonal is always zero because self-coupling is not physical for the standard UPDE contract. Inputs must be finite, real-valued, non-boolean arrays; numeric-string aliases are rejected before float coercion. Base coupling matrices must be square and zero diagonal; distance matrices from custom distance functions must be symmetric, non-negative, and zero diagonal.

Decay forms

decay_form Formula Use case
inverse_plus_one 1 / (1 + d) MIF/FRC kinematic merging, mobile sensors, robots, and moving power assets where coupling decays smoothly without a singularity
exponential exp(-d / ell) Media with characteristic propagation or attenuation length
power_law (1 + d / ell)^(-p) Scale-free or long-range mobile coupling
inverse_distance 1 / sqrt(d^2 + epsilon) Exact compatibility with the existing Swarmalator phase-distance kernel

Pipeline wiring

positions(t) + reviewed K_nm
SpatialCouplingModulator.modulate()
UPDEEngine / Swarmalator / future Doppler and MovingFrame engines
coherence, Lyapunov, entropy-production, and merge-window monitors

The Python reference is paired with Rust, Go, Julia, and Mojo accelerator surfaces. The benchmark gate records each declared backend slot and accepts only when available backends match the Python reference within the documented tolerance.

The public dispatcher and direct accelerator wrappers share the same spatial-modulator output validator. Public positions, base coupling matrices, scalar decay controls, direct accelerator counts/forms/flat buffers, Rust wrapper returns, optional backend outputs, and direct Julia raw returns are validated before dtype coercion, so numeric-string aliases, boolean aliases, complex aliases, non-finite values, wrong cardinality, and non-zero diagonals are rejected instead of being widened into apparently valid float matrices. Public dispatch still returns an (n, n) matrix to callers; backend fallback is reserved for loader or runtime unavailability, not malformed backend physics evidence.

Why spatial modulation is an operations control

  • In moving populations, coupling strength should not be treated as static. Distance-aware modulation makes control decisions sensitive to geometry drift before supervisory actions are emitted.
  • The kernel-level invariant K'_ii = 0 prevents self-loop amplification in both mobile and static modes.
  • Row-wise decay choices are what lets teams keep a single coupling topology and switch physical assumptions per domain.

API

import numpy as np
from scpn_phase_orchestrator.coupling import SpatialCouplingModulator

knm = np.array([[0.0, 1.0], [1.0, 0.0]])
positions = np.array([[0.0], [3.0]])

modulator = SpatialCouplingModulator(K_base=0.5)
modulated = modulator.modulate(knm, positions)
# modulated[0, 1] == 0.5 / (1 + 3)

jacobian_positions(positions) returns the analytical derivative of the modulation matrix with respect to the position array. The shape is (n, n, n, dim), where J[i, j, a, d] = dM[i, j] / dx[a, d].

Benchmark

PYTHONPATH=src python benchmarks/spatial_modulator_benchmark.py --parity-gate --sizes 16 --dim 2 --calls 3

Timing fields are local non-isolated regression evidence unless the benchmark metadata records CPU/core isolation and host-load controls. They are not production throughput claims.

spatial_modulator

Distance-dependent coupling modulation for moving oscillator systems.

SpatialCouplingModulator converts a base coupling matrix K_nm and an absolute position array into a distance-weighted coupling matrix. The default MIF/general moving-agent contract is

K'_ij = K_base * K_ij / (1 + ||x_i - x_j||),   K'_ii = 0.

Additional decay forms support exponential, finite power-law, and the exact regularised inverse-distance kernel used by the existing Swarmalator phase coupling term. Inputs are validated at the public boundary before any optional backend is called.

Classes

SpatialCouplingModulator dataclass

SpatialCouplingModulator(
    K_base: float,
    distance_fn: DistanceFn | None = None,
    decay_form: DecayForm = "inverse_plus_one",
    decay_exponent: float = 1.0,
    decay_length_scale: float = 1.0,
    epsilon: float = 1e-12,
)

Distance-decay modulator for base coupling matrices.

Parameters are immutable so the same instance can be safely reused across simulator steps, benchmarks, and downstream MIF contract tests.

Methods:
__post_init__
__post_init__() -> None

Validate immutable scalar controls and the optional distance kernel.

Source code in src/scpn_phase_orchestrator/coupling/spatial_modulator.py
def __post_init__(self) -> None:
    """Validate immutable scalar controls and the optional distance kernel."""
    _validate_non_negative_scalar(self.K_base, name="K_base")
    _validate_decay_form(self.decay_form)
    _validate_scalar(self.decay_exponent, name="decay_exponent", positive=True)
    _validate_scalar(
        self.decay_length_scale,
        name="decay_length_scale",
        positive=True,
    )
    _validate_scalar(self.epsilon, name="epsilon", positive=True)
    if self.distance_fn is not None and not callable(self.distance_fn):
        raise ValueError("distance_fn must be callable when provided")
distance_matrix
distance_matrix(positions: object) -> FloatArray

Return the validated pairwise distance matrix for positions.

Parameters

positions : object Oscillator positions, shape (N, d).

Returns

FloatArray The validated pairwise distance matrix, shape (N, N).

Source code in src/scpn_phase_orchestrator/coupling/spatial_modulator.py
def distance_matrix(self, positions: object) -> FloatArray:
    """Return the validated pairwise distance matrix for ``positions``.

    Parameters
    ----------
    positions : object
        Oscillator positions, shape ``(N, d)``.

    Returns
    -------
    FloatArray
        The validated pairwise distance matrix, shape ``(N, N)``.
    """
    positions64 = _validate_positions(positions)
    if self.distance_fn is None:
        return _pairwise_euclidean(positions64)
    left = positions64[:, np.newaxis, :]
    right = positions64[np.newaxis, :, :]
    return _validate_distance_matrix(
        self.distance_fn(left, right), n=positions64.shape[0]
    )
modulation_matrix
modulation_matrix(positions: object) -> FloatArray

Return K_base * f(distance) with a zero self-coupling diagonal.

Parameters

positions : object Oscillator positions, shape (N, d).

Returns

FloatArray The distance modulation matrix with a zero diagonal.

Source code in src/scpn_phase_orchestrator/coupling/spatial_modulator.py
def modulation_matrix(self, positions: object) -> FloatArray:
    """Return ``K_base * f(distance)`` with a zero self-coupling diagonal.

    Parameters
    ----------
    positions : object
        Oscillator positions, shape ``(N, d)``.

    Returns
    -------
    FloatArray
        The distance modulation matrix with a zero diagonal.
    """
    positions64 = _validate_positions(positions)
    return _python_modulation_matrix(
        positions64,
        k_base=float(self.K_base),
        decay_form=_validate_decay_form(self.decay_form),
        decay_exponent=float(self.decay_exponent),
        decay_length_scale=float(self.decay_length_scale),
        epsilon=float(self.epsilon),
        distance_fn=self.distance_fn,
    )
modulate
modulate(
    k_nm_base: object, positions: object
) -> FloatArray

Return the distance-modulated coupling matrix.

k_nm_base must be square, finite, real-valued, and zero diagonal. The output preserves that zero self-coupling diagonal.

Parameters

k_nm_base : object Base coupling matrix to modulate, shape (N, N). positions : object Oscillator positions, shape (N, d).

Returns

FloatArray The distance-modulated coupling matrix.

Source code in src/scpn_phase_orchestrator/coupling/spatial_modulator.py
def modulate(self, k_nm_base: object, positions: object) -> FloatArray:
    """Return the distance-modulated coupling matrix.

    ``k_nm_base`` must be square, finite, real-valued, and zero diagonal.
    The output preserves that zero self-coupling diagonal.

    Parameters
    ----------
    k_nm_base : object
        Base coupling matrix to modulate, shape ``(N, N)``.
    positions : object
        Oscillator positions, shape ``(N, d)``.

    Returns
    -------
    FloatArray
        The distance-modulated coupling matrix.
    """
    positions64 = _validate_positions(positions)
    base = _validate_knm_base(k_nm_base, expected_n=positions64.shape[0])
    form = _validate_decay_form(self.decay_form)
    if self.distance_fn is not None:
        out = base * self.modulation_matrix(positions64)
        np.fill_diagonal(out, 0.0)
        return np.ascontiguousarray(out, dtype=np.float64)
    backend_fn = _dispatch()
    if backend_fn is None:
        return _python_spatial_modulate(
            base.ravel(),
            positions64.ravel(),
            positions64.shape[0],
            positions64.shape[1],
            float(self.K_base),
            _DECAY_TO_CODE[form],
            float(self.decay_exponent),
            float(self.decay_length_scale),
            float(self.epsilon),
        ).reshape(positions64.shape[0], positions64.shape[0])
    return _validate_backend_output(
        backend_fn(
            np.ascontiguousarray(base.ravel(), dtype=np.float64),
            np.ascontiguousarray(positions64.ravel(), dtype=np.float64),
            int(positions64.shape[0]),
            int(positions64.shape[1]),
            float(self.K_base),
            int(_DECAY_TO_CODE[form]),
            float(self.decay_exponent),
            float(self.decay_length_scale),
            float(self.epsilon),
        ),
        n=positions64.shape[0],
    )
jacobian_positions
jacobian_positions(positions: object) -> FloatArray

Analytical derivative of modulation_matrix with respect to positions.

Returns an array with shape (n, n, n, dim). Entry J[i, j, a, d] is d M[i, j] / d positions[a, d] where M = modulation_matrix(positions). Custom distance functions are not differentiable through this closed-form path and fail closed.

Parameters

positions : object Oscillator positions, shape (N, d).

Returns

FloatArray The derivative of the modulation matrix with respect to positions.

Raises

ValueError If positions has an invalid shape.

Source code in src/scpn_phase_orchestrator/coupling/spatial_modulator.py
def jacobian_positions(self, positions: object) -> FloatArray:
    """Analytical derivative of ``modulation_matrix`` with respect to positions.

    Returns an array with shape ``(n, n, n, dim)``. Entry
    ``J[i, j, a, d]`` is ``d M[i, j] / d positions[a, d]`` where
    ``M = modulation_matrix(positions)``. Custom distance functions are not
    differentiable through this closed-form path and fail closed.

    Parameters
    ----------
    positions : object
        Oscillator positions, shape ``(N, d)``.

    Returns
    -------
    FloatArray
        The derivative of the modulation matrix with respect to positions.

    Raises
    ------
    ValueError
        If ``positions`` has an invalid shape.
    """
    if self.distance_fn is not None:
        raise ValueError(
            "jacobian_positions requires the default Euclidean distance"
        )
    positions64 = _validate_positions(positions)
    form = _validate_decay_form(self.decay_form)
    n, dim = positions64.shape
    jac = np.zeros((n, n, n, dim), dtype=np.float64)
    for i in range(n):
        for j in range(n):
            if i == j:
                continue
            delta = positions64[i] - positions64[j]
            distance = float(np.linalg.norm(delta))
            if form == "inverse_distance":
                denom = (distance * distance + float(self.epsilon)) ** 1.5
                grad_i = -float(self.K_base) * delta / denom
            elif distance <= float(self.epsilon):
                grad_i = np.zeros(dim, dtype=np.float64)
            elif form == "inverse_plus_one":
                grad_i = (
                    -float(self.K_base) * delta / (distance * (1.0 + distance) ** 2)
                )
            elif form == "exponential":
                weight = np.exp(-distance / float(self.decay_length_scale))
                grad_i = (
                    -float(self.K_base)
                    * weight
                    * delta
                    / (float(self.decay_length_scale) * distance)
                )
            else:
                scaled = 1.0 + distance / float(self.decay_length_scale)
                grad_i = (
                    -float(self.K_base)
                    * float(self.decay_exponent)
                    * scaled ** (-float(self.decay_exponent) - 1.0)
                    * delta
                    / (float(self.decay_length_scale) * distance)
                )
            jac[i, j, i, :] = grad_i
            jac[i, j, j, :] = -grad_i
    return jac

Functions:

spatial_modulate

spatial_modulate(
    k_nm_base: object,
    positions: object,
    *,
    K_base: float = 1.0,
    decay_form: DecayForm = "inverse_plus_one",
    decay_exponent: float = 1.0,
    decay_length_scale: float = 1.0,
    epsilon: float = 1e-12,
) -> FloatArray

Functional wrapper around :class:SpatialCouplingModulator.

Parameters

k_nm_base : object Base coupling matrix to modulate, shape (N, N). positions : object Oscillator positions, shape (N, d). K_base : float Base coupling strength before spatial modulation. decay_form : DecayForm Spatial decay law (e.g. exponential or power). decay_exponent : float Exponent of the spatial decay law. decay_length_scale : float Characteristic length scale of the spatial decay. epsilon : float Numerical floor guarding the decay denominator.

Returns

FloatArray The distance-modulated coupling matrix.

Source code in src/scpn_phase_orchestrator/coupling/spatial_modulator.py
def spatial_modulate(
    k_nm_base: object,
    positions: object,
    *,
    K_base: float = 1.0,
    decay_form: DecayForm = "inverse_plus_one",
    decay_exponent: float = 1.0,
    decay_length_scale: float = 1.0,
    epsilon: float = 1.0e-12,
) -> FloatArray:
    """Functional wrapper around :class:`SpatialCouplingModulator`.

    Parameters
    ----------
    k_nm_base : object
        Base coupling matrix to modulate, shape ``(N, N)``.
    positions : object
        Oscillator positions, shape ``(N, d)``.
    K_base : float
        Base coupling strength before spatial modulation.
    decay_form : DecayForm
        Spatial decay law (e.g. ``exponential`` or ``power``).
    decay_exponent : float
        Exponent of the spatial decay law.
    decay_length_scale : float
        Characteristic length scale of the spatial decay.
    epsilon : float
        Numerical floor guarding the decay denominator.

    Returns
    -------
    FloatArray
        The distance-modulated coupling matrix.
    """
    return SpatialCouplingModulator(
        K_base=K_base,
        decay_form=decay_form,
        decay_exponent=decay_exponent,
        decay_length_scale=decay_length_scale,
        epsilon=epsilon,
    ).modulate(k_nm_base, positions)