# linear_response { #tvbo.analysis.linear_response }

`analysis.linear_response`

Linear response.

Symbolic linear-response machinery derived entirely from a model's declarative metadata — the network Jacobian ``A`` at an operating point, and the noise input matrix ``Q`` — from which fixed-point observables follow (stationary covariance via the Lyapunov equation, power spectra, Fisher information; Deco 2014 Figs 5/6).

Everything model-specific is **symbolic** and backend-independent: :func:`jacobian_terms` differentiates the dfun metadata with ``sympy`` (derived-variable chain unfolded) and returns the symbolic per-node Jacobians, which the **code generator renders to any backend** through ``render_expression``. The network assembly (block-diagonal local Jacobian + connectome-scattered coupling Jacobian) is likewise **emitted by codegen per backend** — a ``vmap``/scatter on JAX, a loop on Julia — exactly as the network RHS is emitted (one metadata source, every backend); ideally through the backend-abstracted ``arrayops`` structural primitives so the assembly, too, is one handler.

:func:`network_jacobian` below is a **NumPy reference oracle only** — it assembles ``A`` numerically so the symbolic terms can be verified against a finite-difference Jacobian in tests. It is NOT the runtime path (the runtime path is the codegen described above); do not call it from generated code.

The full network Jacobian is

    A[(k,i),(l,j)] = δ_ij · ∂f_k/∂x_l                       (local block, per node)
                   + Σ_c (∂f_k/∂c) · (∂c_i/∂x_{l,j})         (coupling block)

where for an instantaneous coupling input ``c`` whose source state variable is ``s`` (``c_i = Σ_j W_ij s_j``), ``∂c_i/∂x_{l,j} = W_ij`` when ``l == s`` else 0.
Both ``∂f_k/∂x_l`` (``Jloc``) and ``∂f_k/∂c`` (``Jcpl``) are symbolic per-node Jacobians of the metadata dfun.

## Functions

| Name | Description |
| --- | --- |
| [constraint_expr](#tvbo.analysis.linear_response.constraint_expr) | Unfolded symbolic expression of a derived variable (e.g. the FIC constraint variable ``I_E``), in state variables, network-coupling inputs and parameters — same unfolding as :func:`_dfun_symbols` uses for the RHS (derived-variable chain inlined, local coupling zeroed), so it prints against the same symbol set (``ctx['syms']``). Used to emit the constraint residual of a constraint-defined operating point (Deco FIC: ``I_E = target``, with ``J_i`` the free parameter), solved deterministically alongside the fixed point. |
| [jacobian_terms](#tvbo.analysis.linear_response.jacobian_terms) | Symbolic per-node Jacobian terms of the metadata dfun. |
| [linear_response_context](#tvbo.analysis.linear_response.linear_response_context) | Resolution for the linear-response codegen: symbolic terms + layout, NO code. |
| [network_jacobian](#tvbo.analysis.linear_response.network_jacobian) | NumPy **reference oracle** — assemble ``A`` numerically for verification only. |
| [noise_terms](#tvbo.analysis.linear_response.noise_terms) | Per-state-variable noise standard deviations declared on the model, or ``None``. |
| [observable_terms](#tvbo.analysis.linear_response.observable_terms) | Symbolic observation row ``∂y/∂x`` of a declared observable ``y``. |

### constraint_expr { #tvbo.analysis.linear_response.constraint_expr }

```python
analysis.linear_response.constraint_expr(model, var_name)
```

Unfolded symbolic expression of a derived variable (e.g. the FIC constraint variable ``I_E``), in state variables, network-coupling inputs and parameters — same unfolding as :func:`_dfun_symbols` uses for the RHS (derived-variable chain inlined, local coupling zeroed), so it prints against the same symbol set (``ctx['syms']``). Used to emit the constraint residual of a constraint-defined operating point (Deco FIC: ``I_E = target``, with ``J_i`` the free parameter), solved deterministically alongside the fixed point.

### jacobian_terms { #tvbo.analysis.linear_response.jacobian_terms }

```python
analysis.linear_response.jacobian_terms(model)
```

Symbolic per-node Jacobian terms of the metadata dfun.

Returns a dict with the symbolic ``Jloc`` (∂f/∂state, ``n_sv × n_sv``) and ``Jcpl`` (∂f/∂net-coupling, ``n_sv × n_cpl``) sympy matrices plus the symbol ordering needed to lower them (state vars, network coupling names, the coupling source variable). Backend-independent — a printer turns these into code.

### linear_response_context { #tvbo.analysis.linear_response.linear_response_context }

```python
analysis.linear_response.linear_response_context(model)
```

Resolution for the linear-response codegen: symbolic terms + layout, NO code.

Keeps *resolution* in Python and *code structure* in the template, per the codegen convention. The Mako ``<%def>`` partials in ``_linear_response.py.mako`` consume this and emit the vector-field / Jacobian / covariance structure, rendering each symbolic entry with ``render_expression`` (so any backend prints it) — no Python string-emit.

Returns the state / coupling / parameter layout — including which parameters are per-node (``pernode``: heterogeneous, gathered by node index) and the symbol set the printer must treat as plain symbols (``syms``) — plus the symbolic per-node RHS (``rhs``), local Jacobian (``Jloc``), coupling Jacobian (``Jcpl``) and the declared per-state noise amplitudes (``noise``, or ``None`` when the model declares none).

### network_jacobian { #tvbo.analysis.linear_response.network_jacobian }

```python
analysis.linear_response.network_jacobian(model, weights, state, params)
```

NumPy **reference oracle** — assemble ``A`` numerically for verification only.

Used by tests to check the symbolic :func:`jacobian_terms` against a finite-difference Jacobian. The runtime path renders those symbolic terms to the target backend and assembles ``A`` in codegen (``vmap``/scatter on JAX, loop on Julia); this function is deliberately NumPy and must not be called from generated code.



#### Parameters {.doc-section .doc-section-parameters}

model : Dynamics
    The model (source of the symbolic dfun).
weights : array (n_nodes, n_nodes)
    connectome ``W`` (``c_i = Σ_j W_ij s_j`` for the coupling source ``s``).
state : array (n_sv, n_nodes)
    The operating point (e.g. the deterministic fixed point), per state
    variable and node.
params : dict
    Scalar parameter values by name.



#### Returns: {.doc-section .doc-section-returns}

A : array (n_sv·n_nodes, n_sv·n_nodes)
    The Jacobian in block layout (state-variable block ``k`` spans rows/cols
    ``k·N .. (k+1)·N``), matching the network state layout used elsewhere.

### noise_terms { #tvbo.analysis.linear_response.noise_terms }

```python
analysis.linear_response.noise_terms(model)
```

Per-state-variable noise standard deviations declared on the model, or ``None``.

The Lyapunov equation's input matrix ``Q`` is ``diag(σ_k²)`` over the state blocks, which is only ``σ² I`` when every state variable is driven. A model whose noise enters two of six equations — two synaptic gating variables and a four-state haemodynamic cascade that is driven, not forced — needs the declared per-state amplitudes, and a uniform ``Q`` would put noise into the haemodynamics.

Returns ``None`` when no state variable declares noise at all, which is the signal to fall back to a uniform amplitude supplied by the analysis observation.

### observable_terms { #tvbo.analysis.linear_response.observable_terms }

```python
analysis.linear_response.observable_terms(model, name)
```

Symbolic observation row ``∂y/∂x`` of a declared observable ``y``.

``y`` is either a state variable (the row is a selector) or a derived variable — a BOLD signal, a firing rate, any declared readout — unfolded through the same derived-variable chain the RHS uses, so the linear response can be carried through whatever cascade the model declares rather than stopping at the state vector.

Returns the per-node Jacobian of ``y`` with respect to the state variables (``Hloc``, ``1 × n_sv``) and with respect to the network coupling inputs (``Hcpl``, ``1 × n_cpl``); the latter scatters through the connectome exactly as ``Jcpl`` does, so an observable reading a coupling term stays correct.