Simulation¶
Use the simulation namespace for open-loop LTI responses, closed-loop trajectories, fixed-horizon nonlinear rollouts, and sampled continuous models.
The namespace includes:
lsim()for open-loop discrete or sampled continuous LTI simulationsimulate()for closed-loop discrete and continuous simulationrollout()for fixed-horizon nonlinear transitions without a system objectsample_system()for solver-backed continuous-to-discrete nonlinear model constructionfoh_inputs()for first-order-hold endpoint pairing on sampled inputsstep_response(),impulse_response(), andinitial_response()for standard response viewsas_ode_term()as a lower-level bridge into direct Diffrax use
Minimal Example¶
import jax
jax.config.update("jax_enable_x64", True)
import jax.numpy as jnp
import contrax as cx
sys = cx.dss(
jnp.array([[1.0, 0.05], [0.0, 1.0]]),
jnp.array([[0.0], [0.05]]),
jnp.eye(2),
jnp.zeros((2, 1)),
dt=0.05,
)
K = cx.lqr(sys, jnp.eye(2), jnp.array([[1.0]])).K
trajectory = cx.simulate(
sys,
jnp.array([1.0, 0.0]),
lambda t, x: -K @ x,
num_steps=80,
)
ts, xs, ys = trajectory.ts, trajectory.xs, trajectory.ys
Horizon Conventions¶
simulate() is explicit about the horizon type:
- use
num_steps=...for discrete systems - use
duration=...for continuous systems
LTI response functions and simulate return a
SimResult with .ts, .xs, .ys, .status,
.successful, and .num_steps fields.
For returned trajectories:
Use plot(result.state_times, result.xs) for states and
plot(result.output_times, result.ys) for outputs. Discrete simulations retain
T outputs and T+1 states: .state_times includes the terminal time even when
T is zero. .ts aliases .output_times. Continuous results
use the same sample vector for states and outputs. .final_state returns the
last state and raises ValueError when the trajectory is empty.
- discrete
simulate()returnsxsincludingx0 - continuous
simulate()returns samples on a fixed save grid lsim()returns aSimResultfor an open-loop input sequence- response helpers (
step_response,impulse_response,initial_response) also returnSimResultwith.ts,.xs,.ys
Discrete simulation and responses accept zero steps: the initial state remains
in .xs and the time/output arrays are empty. Continuous simulate(duration=0)
returns one initial-state/output sample and performs no integration.
Continuous Open-Loop Inputs¶
Continuous LTI lsim takes an explicit sample-time vector and one input per
sample. The initial state belongs to ts[0]; sample times may be nonuniform.
sys_c = cx.ss([[-1.0]], [[1.0]], [[1.0]], [[0.2]])
times = jnp.array([0.0, 0.1, 0.4, 1.0])
inputs = times[:, None]
response = cx.lsim(sys_c, inputs, ts=times, input_interpolation="foh")
assert response.successful
zoh holds the left input over each interval. foh interpolates linearly
between adjacent inputs. Block matrix exponentials evaluate both exactly up to
floating-point error, including for singular or marginal state matrices. The
continuous result has N states and outputs for N time/input samples; direct
feedthrough uses the input at each sample. The last input contributes to the
final output and to the last FOH interval. Empty sample vectors return empty
continuous states and outputs; one sample returns the initial state/output.
Discrete lsim retains T inputs/outputs and T+1 states. It uses the model's
sample period and accepts neither ts= nor continuous FOH interpolation.
Both paths support jit, vmap, JVP and VJP for fixed sample counts.
Continuous grids whose intervals agree within a small roundoff budget reuse one ZOH/FOH transition exponential. Other grids retain interval-by-interval exponentials. Forward, reverse and higher differentiation use the general-grid computation, preserving derivatives with respect to every independent interval. The reuse reduces repeated exponentials in primal rollouts. Derivative evaluation continues to use the general-grid computation.
Use rollout() when there is no system object and you simply want to scan a
transition function over a fixed input sequence.
It accepts state and input pytrees, including scalar state leaves, and returns
the state structure with a leading time axis on every leaf. Empty inputs retain
one initial-state sample per leaf.
Use sample_system() when the underlying plant model is continuous-time but
the estimation or control workflow lives on a discrete grid. The returned
object is a discrete NonlinearSystem, so
it plugs directly into ekf(), ukf(), simulate(..., num_steps=...), and
other discrete-time workflows.
With the default input_interpolation="zoh", each step input has shape (m,)
and is held constant across the interval. With
input_interpolation="foh", each step input has shape (2, m) and is treated
as the endpoint pair (u_k, u_{k+1}); foh_inputs() builds that paired
sequence from ordinary sampled inputs.
For sampled models, model.step(t, x, u) returns
SampleResult with terminal .x, .status,
.successful and integration .num_steps. Close over the model in jax.jit,
or apply eqx.filter_jit to its bound step method. model.dynamics(t, x, u)
returns the state alone and produces a nonfinite state when integration fails.
Sampled numerical status is retained by discrete simulate as well.
For a separable Hamiltonian, sample_system(sys, dt, method="sv") uses
Störmer–Verlet for the conservative flow and second-order explicit midpoint
for the dissipative/input half-steps. It requires SeparablePHSSystem with
the default J=None; custom structure maps are rejected. Under FOH the two
external half-steps sample their own midpoint inputs. The full method is
second order for smooth data and supports jit, vmap, JVP and VJP.
Conservative Verlet preserves the symplectic structure; dissipative/input
steps require a resolved step size and do not guarantee energy inequalities
at arbitrary step sizes. Increase n_substeps to check discretization error.
Transform Behavior¶
The main transform contracts are:
- discrete
lsim(),simulate(), androllout()are built around fixed-shape JAX scans - continuous
simulate()uses Diffrax internally but keeps the public output on a fixed save grid rollout()composes withjit,vmap, andgradfor fixed-shape inputs
That makes simulation suitable for both ordinary control scripts and compiled design loops.
Numerical Notes¶
simulate and sample_system(method="diffrax") accept native Diffrax solver
and stepsize_controller objects, an initial step dt0 and max_steps.
For example, use diffrax.PIDController(rtol=1e-8, atol=1e-10) to set
tolerances and solver=diffrax.Kvaerno3() for a validated implicit integration
route. Tsit5 is the default explicit solver. Response helpers forward the same
continuous integration controls. Incompatible discrete/fixed-step options
raise immediately.
throw=False returns numerical failure diagnostics. throw=True enables
checked failures in eager and compiled execution, including when only a state
or output field is consumed. Iteration limits report max_steps_reached even
when some saved values are nonfinite; nonfinite output after a successful
backend reports numerical_failure.
The default continuous simulation adjoint is RecursiveCheckpointAdjoint for
reverse-mode trajectory losses. Sampled models default to DirectAdjoint to
support forward state Jacobians as required by EKF. Replacing an adjoint changes
the supported differentiation modes; in particular, the checkpoint adjoint does
not provide forward-mode JVP. Both explicit Tsit5 and implicit Kvaerno3 have
sampled-Jacobian and reverse-loss coverage. Precision is determined by input
arrays; JAX global precision settings and device selection remain under caller
control.
step_response() and impulse_response() represent discontinuous inputs with
fixed event times. Use a smooth input parameterization when optimizing event
timing.
Related Pages¶
- Systems for model construction
- Control for controller design before simulation
- JAX transform contract for scan and
solver behavior under
jit,vmap, andgrad - Linearize, LQR, simulate for the main discrete control workflow
contrax.simulation
¶
Simulation and response-analysis functions.
as_ode_term(sys: ContLTI | NonlinearSystem | PHSSystem, control_fn: Callable[[float, Array], Array]) -> Any
¶
Wrap a continuous system as a Diffrax ODETerm.
Returns an ODE term for a caller-configured Diffrax solve. The downstream solver and adjoint configuration determine transform behavior.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sys
|
ContLTI | NonlinearSystem | PHSSystem
|
Continuous-time system. |
required |
control_fn
|
Callable[[float, Array], Array]
|
Control function mapping |
required |
Returns:
| Type | Description |
|---|---|
Any
|
diffrax.ODETerm: An ODE term representing |
Raises:
| Type | Description |
|---|---|
ImportError
|
If Diffrax is not installed. |
Source code in contrax/sim.py
foh_inputs(us: Array) -> Array
¶
Build first-order-hold endpoint pairs from a sample sequence.
Given inputs u_k, returns a sequence with shape (T, 2, m) where each
item contains (u_k, u_{k+1}), using u_{T-1} for the final right
endpoint.
Source code in contrax/_rollout.py
impulse_response(sys: DiscLTI | ContLTI, *, num_steps: int | None = None, duration: float | None = None, input_index: int = 0, x0: Array | None = None, dt: float | None = None, solver: Any = None, adjoint: Any = None, dt0: float | None = None, stepsize_controller: Any = None, max_steps: int = 100000, throw: bool = False) -> SimResult
¶
Compute the impulse response of an LTI system.
For discrete systems this applies a unit pulse at k=0. For continuous
systems this applies the equivalent state jump x(0+) = x0 + B[:, i] and
then simulates with zero input. That means the returned sampled trajectory
omits any singular direct-feedthrough D delta(t) spike at t=0.
The continuous-time convention represents the impulse as a state jump;
sampled outputs omit the singular direct-feedthrough term at t=0.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sys
|
DiscLTI | ContLTI
|
Continuous or discrete-time system. |
required |
num_steps
|
int | None
|
Discrete-only number of time steps. |
None
|
duration
|
float | None
|
Continuous-only response duration. |
None
|
input_index
|
int
|
Input channel receiving the impulse. |
0
|
x0
|
Array | None
|
Optional initial state. Defaults to zeros. |
None
|
dt
|
float | None
|
Continuous-only output sample spacing hint. |
None
|
solver
|
Any
|
Continuous-only Diffrax solver override. |
None
|
adjoint
|
Any
|
Continuous-only Diffrax adjoint override. |
None
|
dt0
|
float | None
|
Continuous-only initial solver step size hint. |
None
|
stepsize_controller
|
Any
|
Native Diffrax step-size controller. |
None
|
max_steps
|
int
|
Continuous integration step limit. |
100000
|
throw
|
bool
|
Raise on numerical failure. |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
SimResult |
SimResult
|
Simulation result with output times, states, and outputs. |
Source code in contrax/_responses.py
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 166 167 168 169 170 171 172 173 174 175 176 | |
initial_response(sys: DiscLTI | ContLTI, x0: Array, *, num_steps: int | None = None, duration: float | None = None, dt: float | None = None, solver: Any = None, adjoint: Any = None, dt0: float | None = None, stepsize_controller: Any = None, max_steps: int = 100000, throw: bool = False) -> SimResult
¶
Compute the zero-input response from a nonzero initial state.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sys
|
DiscLTI | ContLTI
|
Continuous or discrete-time system. |
required |
x0
|
Array
|
Initial state. Shape: |
required |
num_steps
|
int | None
|
Discrete-only number of time steps. |
None
|
duration
|
float | None
|
Continuous-only response duration. |
None
|
dt
|
float | None
|
Continuous-only output sample spacing hint. |
None
|
solver
|
Any
|
Continuous-only Diffrax solver override. |
None
|
adjoint
|
Any
|
Continuous-only Diffrax adjoint override. |
None
|
dt0
|
float | None
|
Continuous-only initial solver step size hint. |
None
|
stepsize_controller
|
Any
|
Native Diffrax step-size controller. |
None
|
max_steps
|
int
|
Continuous integration step limit. |
100000
|
throw
|
bool
|
Raise on numerical failure. |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
SimResult |
SimResult
|
Simulation result with output times, states, and outputs. |
Source code in contrax/_responses.py
lsim(sys: DiscLTI | ContLTI, us: Array, x0: Array | None = None, *, ts: Array | None = None, input_interpolation: str = 'zoh', throw: bool = False) -> SimResult
¶
Simulate a discrete or continuous LTI system from sampled open-loop inputs.
Discrete indexing returns T inputs/outputs and T+1 states. Continuous systems require N sample times and inputs and return N sampled states and outputs, with x0 at ts[0]. Exact block exponentials implement ZOH or FOH inputs on each interval; nonuniform grids are supported.
Uniform grids (within time-differencing roundoff) reuse one transition exponential. Forward/reverse and higher derivatives use the general-grid computation, retaining derivatives of every independent interval. This optimization targets primal trajectories, not differentiation throughput.
The input sequence has a fixed shape and each interval uses the selected zero-order or first-order hold convention.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sys
|
DiscLTI | ContLTI
|
Continuous or discrete LTI system. |
required |
us
|
Array
|
Input sequence. Shape: |
required |
x0
|
Array | None
|
Initial state. Shape: |
None
|
ts
|
Array | None
|
Continuous-only sample times. Shape: |
None
|
input_interpolation
|
str
|
Continuous interval interpretation, zoh or foh. |
'zoh'
|
throw
|
bool
|
Raise on numerical failure, otherwise inspect result.status. |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
SimResult |
SimResult
|
Simulation result with |
Examples:
>>> import jax.numpy as jnp
>>> import contrax as cx
>>> sys = cx.dss(
... jnp.array([[1.0, 0.1], [0.0, 1.0]]),
... jnp.array([[0.0], [0.1]]),
... jnp.eye(2),
... jnp.zeros((2, 1)),
... dt=0.1,
... )
>>> result = cx.lsim(sys, jnp.zeros((20, 1)))
Source code in contrax/_lsim.py
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 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 | |
rollout(f: Callable[..., Any], x0: Any, us: Any, params: Any = None) -> Any
¶
Roll out arbitrary discrete-time dynamics over a fixed input sequence.
Applies f to each input with jax.lax.scan and returns the states from
x0 through the final transition. Define output calculations separately
when they are needed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
f
|
Callable[..., Any]
|
Transition function. If |
required |
x0
|
Any
|
Initial state. Shape: |
required |
us
|
Any
|
Input sequence. Shape: |
required |
params
|
Any
|
Optional static or array-valued parameters passed to |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
PyTree |
Any
|
State trajectory with the same structure as x0. Every leaf
includes the initial state and has shape |
Examples:
>>> import jax.numpy as jnp
>>> import contrax as cx
>>> def f(x, u, gain):
... return gain * x + u
>>> xs = cx.rollout(f, jnp.array([1.0]), jnp.zeros((10, 1)), 0.9)
Source code in contrax/_rollout.py
sample_system(sys: NonlinearSystem | PHSSystem, dt: float, *, input_interpolation: str = 'zoh', method: str = 'diffrax', n_substeps: int = 1, solver: Any = None, adjoint: Any = None, dt0: float | None = None, stepsize_controller: Any = None, max_steps: int = 100000, throw: bool = False) -> NonlinearSystem
¶
Sample a continuous nonlinear system into a discrete transition model.
Builds a discrete-time NonlinearSystem whose one-step dynamics integrate the continuous model over one sample interval using the chosen integrator.
With input_interpolation="zoh", each discrete input has shape (m,) and
is held constant over the interval. With input_interpolation="foh", each
discrete input has shape (2, m) and is interpreted as the pair
(u_k, u_{k+1}); use foh_inputs() to build those pairs from a sampled
input sequence.
Integrator methods
method="diffrax" (default) uses adaptive Diffrax integration with Tsit5.
For stiff dynamics, select an appropriate native implicit solver such as
Kvaerno3. A native step-size controller sets integration tolerances. Compare
execution costs at matched trajectory error before choosing a fixed method.
method="rk4" uses a 4th-order Runge-Kutta scheme implemented as pure JAX
arithmetic. The integration graph is fully static, so XLA compiles the entire
step to a single fused kernel. 4th-order accurate: local error O(h⁵), global
error O(h⁴) per sampling interval dt / n_substeps. As a fixed-step method,
its accuracy depends on choosing dt and n_substeps for the system's time
constants. Not symplectic — energy drift accumulates
over long simulation horizons, which matters for conservative PHS systems.
method="sv" uses Störmer-Verlet with Strang splitting for dissipation
and inputs (B(h/2) ∘ A(h) ∘ B(h/2), where A is the symplectic flow and B
is the dissipation/input flow). Use a
SeparablePHSSystem with the default
canonical J. Dissipative/input half-steps
use second-order explicit midpoint, with FOH sampled at each half-step's
own midpoint. The full split method has global order two for smooth data.
For purely conservative systems (R=None, G=None) this reduces to standard
Störmer-Verlet with no extra gradient evaluations. Per
substep it evaluates ∇V twice and ∇T once, compared to four full
dynamics evaluations for RK4. Symplectic — the conservative part
preserves phase-space volume exactly regardless of step count; the
dissipative/input half-steps can violate energy inequalities at large step
sizes; resolve dissipation and forcing with n_substeps.
Reference: Leimkuhler & Reich (2004), Simulating
Hamiltonian Dynamics, Cambridge University Press.
method="euler" and method="midpoint" are 1st- and 2nd-order fixed-step
baselines. They follow the same pure-JAX, no-Diffrax path as "rk4".
n_substeps subdivides dt into n_substeps equal sub-intervals. Larger
values improve accuracy at the cost of proportionally more dynamics
evaluations. Diffrax chooses its own adaptive step count, so
n_substeps applies to fixed-step methods.
solver, adjoint, dt0, stepsize_controller, and a nondefault
max_steps are Diffrax-only overrides; incompatible fixed-step
configurations raise ValueError.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sys
|
NonlinearSystem | PHSSystem
|
Continuous source system. Must have |
required |
dt
|
float
|
Sampling interval. |
required |
input_interpolation
|
str
|
|
'zoh'
|
method
|
str
|
Integration method. One of |
'diffrax'
|
n_substeps
|
int
|
Number of fixed sub-steps per sampling interval.
Ignored by |
1
|
solver
|
Any
|
Diffrax solver override. Only valid with |
None
|
adjoint
|
Any
|
Diffrax adjoint override. Only valid with |
None
|
dt0
|
float | None
|
Diffrax initial step-size hint. Only valid with |
None
|
stepsize_controller
|
Any
|
Native Diffrax step-size controller, defaulting to PIDController(rtol=1e-6, atol=1e-9). Diffrax-only. |
None
|
max_steps
|
int
|
Adaptive integration limit. Diffrax-only. |
100000
|
throw
|
bool
|
Raise on failed integration; otherwise step() returns diagnostics and dynamics() returns a nonfinite state on failure. |
False
|
Returns:
| Type | Description |
|---|---|
NonlinearSystem
|
NonlinearSystem: Discrete system with dt set and dynamics obtained from the chosen integrator. Its step(t, x, u) method returns SampleResult integration diagnostics. |
Examples:
>>> import contrax as cx
>>> import jax.numpy as jnp
>>> field = cx.nonlinear_system(lambda t, x, u: -x + u)
>>> sampled = cx.sample_system(field, 0.1, max_steps=256)
>>> result = sampled.step(0.0, jnp.ones(1), jnp.zeros(1))
>>> bool(result.successful)
True
Source code in contrax/_sampling.py
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 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 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 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 | |
simulate(sys: DiscLTI | ContLTI | NonlinearSystem | PHSSystem, x0: Array, policy: Callable[[float, Array], Array], *, num_steps: int | None = None, duration: float | None = None, dt: float | None = None, solver: Any = None, adjoint: Any = None, dt0: float | None = None, stepsize_controller: Any = None, max_steps: int = 100000, throw: bool = False) -> SimResult
¶
Simulate a system in closed loop under a control policy.
Evaluates policy(t, x) along the system trajectory and dispatches to the
simulation path for the system's time domain.
Discrete systems use lax.scan and require num_steps. Continuous systems
use Diffrax and require duration; their trajectories are sampled on a
fixed uniform save grid.
Continuous integration accepts native Diffrax solvers, adjoints and
step-size controllers, plus an initial step and integration limit.
The default continuous solver is
diffrax.Tsit5() with diffrax.RecursiveCheckpointAdjoint().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sys
|
DiscLTI | ContLTI | NonlinearSystem | PHSSystem
|
Continuous or discrete LTI, nonlinear, or port-Hamiltonian system. |
required |
x0
|
Array
|
Initial state. Shape: |
required |
policy
|
Callable[[float, Array], Array]
|
Control policy mapping |
required |
num_steps
|
int | None
|
Discrete-only number of time steps. |
None
|
duration
|
float | None
|
Continuous-only simulation duration. |
None
|
dt
|
float | None
|
Continuous-only output sample spacing hint. When omitted, the
continuous path returns 201 samples including |
None
|
solver
|
Any
|
Continuous-only Diffrax solver override. |
None
|
adjoint
|
Any
|
Continuous-only Diffrax adjoint override. |
None
|
dt0
|
float | None
|
Continuous-only initial solver step size hint. |
None
|
stepsize_controller
|
Any
|
Native Diffrax step-size controller. Defaults to PIDController(rtol=1e-6, atol=1e-9). |
None
|
max_steps
|
int
|
Continuous adaptive integration step limit. |
100000
|
throw
|
bool
|
Raise on numerical failure, otherwise return status diagnostics. |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
SimResult |
SimResult
|
Simulation result. Discrete trajectories have shapes |
SimResult
|
|
|
SimResult
|
|
|
SimResult
|
Use state_times with xs and output_times with ys; the discrete terminal |
|
SimResult
|
state has its own time. final_state returns the last state. |
Examples:
>>> import jax
>>> jax.config.update("jax_enable_x64", True)
>>> import jax.numpy as jnp
>>> import contrax as cx
>>> sys_d = cx.dss(
... jnp.array([[1.0, 0.05], [0.0, 1.0]]),
... jnp.array([[0.0], [0.05]]),
... jnp.eye(2),
... jnp.zeros((2, 1)),
... dt=0.05,
... )
>>> K = cx.lqr(sys_d, jnp.eye(2), jnp.array([[1.0]])).K
>>> result = cx.simulate(
... sys_d,
... jnp.array([1.0, 0.0]),
... lambda t, x: -K @ x,
... num_steps=60,
... )
Source code in contrax/sim.py
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 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 | |
step_response(sys: DiscLTI | ContLTI, *, num_steps: int | None = None, duration: float | None = None, input_index: int = 0, x0: Array | None = None, dt: float | None = None, solver: Any = None, adjoint: Any = None, dt0: float | None = None, stepsize_controller: Any = None, max_steps: int = 100000, throw: bool = False) -> SimResult
¶
Compute the unit-step response of an LTI system.
Applies a unit step to one input channel and returns the output trajectory.
For MIMO systems, select one input channel at a time via input_index.
Gradients can flow through system parameters when step timing is fixed. The switching time is not a differentiable input to this response.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sys
|
DiscLTI | ContLTI
|
Continuous or discrete-time system. |
required |
num_steps
|
int | None
|
Discrete-only number of time steps. |
None
|
duration
|
float | None
|
Continuous-only response duration. |
None
|
input_index
|
int
|
Input channel receiving the unit step. |
0
|
x0
|
Array | None
|
Optional initial state. Defaults to zeros. |
None
|
dt
|
float | None
|
Continuous-only output sample spacing hint. |
None
|
solver
|
Any
|
Continuous-only Diffrax solver override. |
None
|
adjoint
|
Any
|
Continuous-only Diffrax adjoint override. |
None
|
dt0
|
float | None
|
Continuous-only initial solver step size hint. |
None
|
stepsize_controller
|
Any
|
Native Diffrax step-size controller. |
None
|
max_steps
|
int
|
Continuous integration step limit. |
100000
|
throw
|
bool
|
Raise on numerical failure. |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
SimResult |
SimResult
|
Simulation result with output times, states, and outputs. |