Network

experimental.network_dynamics.Network(
    dynamics,
    coupling,
    graph,
    noise=None,
    history=None,
    external_input=None,
)

Unified network class for all equation types (ODE/DDE/SDE/SDDE).

Network behavior is determined by composition: - Coupling type: InstantaneousCoupling (ODE/SDE) vs DelayedCoupling (DDE/SDDE) - Noise presence: None (deterministic) vs AbstractNoise (stochastic)

Supports multiple named coupling inputs and external inputs that are automatically mapped to dynamics model’s COUPLING_INPUTS and EXTERNAL_INPUTS specifications. Each coupling-input name maps to at most one coupling; ordinary Network couplings are not accumulated. Duplicate dictionary keys are resolved by Python before construction and therefore cannot be detected here.

Args: dynamics: Dynamics model (must be AbstractDynamics) coupling: Single coupling, dict {name: coupling}, or list [coupling1, …] graph: Network connectivity (AbstractGraph or DelayGraph) noise: Optional noise model for stochastic systems history: Optional history for warm-starting simulations external_input: Optional external inputs (single, dict, or list)

Example: # Single coupling (ODE) network = Network(Lorenz(), LinearCoupling(…), graph)

# Multiple couplings (dict)
network = Network(
    FlexibleLorenz(),
    {'structural': LinearCoupling(...), 'modulatory': SigmoidCoupling(...)},
    graph
)

# Multiple couplings (list - auto-mapped to COUPLING_INPUTS order)
network = Network(
    FlexibleLorenz(),
    [LinearCoupling(...), SigmoidCoupling(...)],
    graph
)

# With noise (SDE)
network = Network(Lorenz(), LinearCoupling(...), graph, noise=AdditiveNoise(...))

# With delays (DDE)
network = Network(Lorenz(), DelayedLinearCoupling(...), delay_graph)

# With external input
network = Network(
    WongWang(),
    LinearCoupling(...),
    graph,
    external_input={'I_ext': DataInput(...)}
)

Attributes

Name Description
initial_state Initial state for all nodes.
params Aggregate parameters from all network components.

Methods

Name Description
compute_coupling_inputs Compute all coupling inputs and return as Bunch.
compute_external_inputs Compute all external inputs and return as Bunch.
get_history Get history buffer for delayed coupling networks.
prepare Prepare all couplings for simulation.
prepare_external Prepare all external inputs for simulation.
update_coupling_states Update all coupling internal states after integration step.
update_external_states Update all external input internal states after integration step.
update_history Update internal history from a simulation result.

compute_coupling_inputs

experimental.network_dynamics.Network.compute_coupling_inputs(
    t,
    state,
    coupling_data_dict,
    coupling_state_dict,
)

Compute all coupling inputs and return as Bunch.

Args: t: Current time state: Current network state [n_states, n_nodes] coupling_data_dict: Coupling data from prepare() coupling_state_dict: Current coupling internal states

Returns: coupling_inputs: Bunch {name: array[n_dims, n_nodes]} Named coupling inputs ready for dynamics.dynamics() Missing couplings automatically filled with zeros

Note: Runs precompute() per call, as solve.py does once per forward pass. compute() must never see raw prepare() output: DelayedCoupling only resolves its delay read indices in precompute().

compute_external_inputs

experimental.network_dynamics.Network.compute_external_inputs(
    t,
    state,
    external_data_dict,
    external_state_dict,
)

Compute all external inputs and return as Bunch.

Args: t: Current time state: Current network state [n_states, n_nodes] external_data_dict: Precomputed external data from prepare_external() external_state_dict: Current external internal states

Returns: external_inputs: Bunch {name: array[n_dims, n_nodes]} Named external inputs ready for dynamics.dynamics() Missing inputs automatically filled with zeros

get_history

experimental.network_dynamics.Network.get_history(dt)

Get history buffer for delayed coupling networks.

If no custom history is set, returns initial state-based history. If custom history is set, extracts appropriate window from stored solution.

Args: dt: Integration timestep

Returns: None if no delays (max_delay == 0.0) Otherwise history buffer [n_steps, n_states, n_nodes] where n_steps = ceil(max_delay / dt)

prepare

experimental.network_dynamics.Network.prepare(
    dt,
    t0,
    t1,
    *,
    stage_time_centroid=0.0,
    recompute_coupling_per_stage=False,
    _prepared_topology=None,
)

Prepare all couplings for simulation.

Calls prepare() on each coupling to get coupling_data and coupling_state.

Args: dt: Integration timestep t0: Simulation start time t1: Simulation end time stage_time_centroid: The solver’s sum_i b_i * c_i (see AbstractSolver). DelayedCoupling.precompute() reads it to undo the delay bias introduced by freezing the coupling across solver stages. The default 0.0 means “step-start evaluation”, i.e. no shift. recompute_coupling_per_stage: The solver’s flag of the same name. DelayedCoupling.precompute() needs it to decide whether the stage-time shift is safe to apply (it is not, on its own, for a coupling that also reads a frozen local state).

Both are written into every coupling_data after coupling.prepare() returns, rather than passed down into it, so that couplings overriding prepare() with the historical (network, dt, t0, t1) signature keep working unchanged.

Returns: coupling_data_dict: Bunch {name: coupling_data_bunch} Precomputed data for each coupling (stored outside scan carry) coupling_state_dict: Bunch {name: coupling_state_bunch} Internal state for each coupling (stored in scan carry)

prepare_external

experimental.network_dynamics.Network.prepare_external(dt)

Prepare all external inputs for simulation.

Calls prepare() on each external input to get external_data and external_state.

Args: dt: Integration timestep

Returns: external_data_dict: Bunch {name: external_data_bunch} Precomputed data for each external input (stored outside scan carry) external_state_dict: Bunch {name: external_state_bunch} Internal state for each external input (stored in scan carry)

update_coupling_states

experimental.network_dynamics.Network.update_coupling_states(
    coupling_data_dict,
    coupling_state_dict,
    new_state,
)

Update all coupling internal states after integration step.

Args: coupling_data_dict: Precomputed coupling data from prepare() coupling_state_dict: Current coupling internal states new_state: New network state after integration [n_states, n_nodes]

Returns: new_coupling_state_dict: Bunch {name: updated_coupling_state_bunch}

update_external_states

experimental.network_dynamics.Network.update_external_states(
    external_data_dict,
    external_state_dict,
    new_state,
)

Update all external input internal states after integration step.

Args: external_data_dict: Precomputed external data from prepare_external() external_state_dict: Current external internal states new_state: New network state after integration [n_states, n_nodes]

Returns: new_external_state_dict: Bunch {name: updated_external_state_bunch}

update_history

experimental.network_dynamics.Network.update_history(solution)

Update internal history from a simulation result.

Slices solution.ys down to the integrated state variables (by name, using solution.variable_names) so delay lookups and initial_state see [n_time, len(STATE_NAMES), n_nodes] regardless of the dynamics’ VARIABLES_OF_INTEREST. Also warns if time coverage < max_delay.

Args: solution: Solution from a previous simulation, or None to clear history. Must carry variable_names — all solve() paths populate this.

Raises: ValueError: If the solution shape is not 3D, lacks variable_names, is missing any entry of dynamics.STATE_NAMES, or has an n_nodes mismatch with the graph.