Systems¶
Build continuous and discrete LTI models, nonlinear models, and port-Hamiltonian systems for simulation, analysis, control, and estimation.
ss and dss promote matrix inputs to a common floating dtype while preserving
explicit float32 arrays when x64 mode is enabled. Integer inputs become
floating arrays. dss stores its sample time in the resulting real matrix
dtype. Inconsistent matrix shapes raise ValueError, including during tracing.
Nonlinear and PHS constructors also require finite positive real scalar sample
periods when dt is supplied. Dimension metadata must be nonnegative static
integers. The default canonical PHS structure requires a positive even state
dimension; separable models require a positive integer number of coordinates.
Block sizes and indices are static integers, and block-matrix entries outside
the declared grid raise an error. project_psd explicitly projects real square
matrices with a finite nonnegative eigenvalue floor; it supports JAX transforms
where the eigendecomposition is differentiable.
Discrete NonlinearSystem objects expose step(t, x, u) for a terminal state
and numerical diagnostics. Models made by sample_system retain their adaptive
integration status in that result. Recursive estimators require discrete
transitions: sample a continuous vector field before passing its model to EKF,
UKF or MHE. See Simulation for interpolation, tolerances,
integration limits and adjoint selection.
The namespace includes:
- continuous and discrete LTI models:
ContLTI,DiscLTI,ss(),dss() - nonlinear and port-Hamiltonian model objects:
NonlinearSystem,PHSSystem,nonlinear_system(),phs_system(),schedule_phs() - PHS structure helpers:
canonical_J()to define the symplectic structure map,phs_to_ss()to extract the local state-space linearization around an operating point - structured-system helpers:
partition_state(),block_observation(),block_matrix(),symmetrize_matrix(),project_psd(),phs_diagnostics() - discretization and local model construction:
c2d(),linearize(),linearize_ss() - simple state-space composition:
series(),parallel(), plus operator overloads onContLTIandDiscLTI
Minimal Example¶
Use the systems namespace to construct and transform models:
import jax
jax.config.update("jax_enable_x64", True)
import jax.numpy as jnp
import contrax.systems as cxs
sys_c = cxs.ss(
jnp.array([[0.0, 1.0], [0.0, 0.0]]),
jnp.array([[0.0], [1.0]]),
jnp.eye(2),
jnp.zeros((2, 1)),
)
sys_d = cxs.c2d(sys_c, dt=0.05)
Canonical Model Forms¶
LTI And Nonlinear Systems¶
These equations describe common LTI and nonlinear models.
Port-Hamiltonian Systems¶
PHSSystem represents structured nonlinear
dynamics.
For scheduled coefficients, schedule_phs() binds an exogenous context into
that canonical model:
Conventions¶
A: state matrix with shape(n, n)B: input matrix with shape(n, m)C: output matrix with shape(p, n)D: feedthrough matrix with shape(p, m)DiscLTI.dt: scalar sample time stored as an array leaf
For nonlinear models, the reusable model-object contract is:
dynamics(t, x, u)for the transition or vector fieldoutput(t, x, u)for the output or measurement map
Continuous and discrete LTI systems use the same matrix field names.
PHSSystem uses these fields:
H(x)is the storage functionR(x)is the dissipation map when presentG(x)is the input map when presentJ(x)defaults to the canonical symplectic structure unless you override itschedule_phs()is the extension path when dissipation or input maps depend on observed scheduling contextschedule_phs()can also bind scheduled structure mapsJ(t, x, context)when the structure itself varies with observed context
With the default J=None, the state has even dimension and ordering (q, p).
Dynamics apply the canonical block swap/sign change directly and omit absent
dissipation/input matrix products. Explicit J(x) and scheduled structure maps
retain their supplied values and derivatives.
Structured Helpers¶
The structured helpers cover common operations on nonlinear and port-Hamiltonian models:
Use:
partition_state(x, block_sizes)to split a structured state into consecutive blocksblock_observation(block_sizes, block_indices)to build partial-state output maps from those blocksblock_matrix(row_block_sizes, col_block_sizes, blocks)to assemble dense structured input, process, or observation maps from block entriessymmetrize_matrix(M)andproject_psd(M)when dissipation or covariance matrices pick up small numerical asymmetry or PSD driftphs_diagnostics(sys, x, u)for local skew-symmetry, dissipation, and instantaneous power-balance diagnostics
The helpers operate on arrays and model data, and can be combined to build larger structured models.
Port-Hamiltonian Structure Helpers¶
canonical_J(n_dofs) returns the \(2n_{dofs} \times 2n_{dofs}\) canonical
symplectic structure matrix:
Use it when a PHSSystem has the standard
canonical structure map.
phs_to_ss(sys, x_eq, u_eq) linearizes a PHS at an operating point and
returns a ContLTI for a continuous model or a
DiscLTI retaining an explicit discrete period.
For a continuous model, this converts a structured nonlinear PHS model to the
linear control design path (lqr, place, kalman_gain, and related APIs):
lti = cx.phs_to_ss(phs_sys, x_eq=jnp.zeros(4), u_eq=jnp.zeros(1))
design = cx.lqr(cx.c2d(lti, dt=0.01), Q, R)
Linearization And Discretization¶
linearize() maps nonlinear dynamics to a local LTI model; linearize_ss is
an alias.
Use:
linearize(f, x0, u0)when you have a plain dynamics callable with signature(t, x, u) → x_dot; returns aContLTIwith full-state output by defaultlinearize(f, x0, u0, output=h)when you also want a specific output maph(x, u) → y;A,B,C,Dare all computed and packed into the returnedContLTIlinearize(sys, x0, u0)when you have a reusableNonlinearSystemorPHSSystem; the system's owndynamicsandoutputcallables are used directly
A discrete model returns a DiscLTI with the original sample period. This
includes ZOH models made by sample_system; continuous models and bare vector
fields return ContLTI. An LTI input retains its realization. The result models
perturbations around the operating point, without storing constant offsets.
State/input operating points must be vectors. For FOH endpoint pairs or other
structured inputs, use jax.jacfwd on the model callbacks directly. Sampled
models retain the derivative support of their integration adjoint.
The local linear model is the usual first-order expansion around an operating point:
with
Then use c2d() when the downstream workflow is discrete-time design or
simulation.
c2d converts continuous LTI matrices. For nonlinear/PHS vector fields,
sample_system integrates dynamics over each sample interval and returns a
discrete model with the same canonical dynamics/output callback contract.
Both require a real, finite positive sample period. dss stores a supplied
period in the real matrix precision; c2d promotes explicit higher-precision
array periods together with its computed realization.
The c2d() methods are:
zoh: exact held-input discretization through the native differentiable matrix exponential; supports JVP, VJP and higher derivativestustin: a convenience bilinear transform
ContLTI
with linearize(), discretize it with c2d(),
then move into controller design and validation.
Interconnection¶
Compose LTI models with state-space algebra:
Use:
series(sys2, sys1)orsys2 @ sys1for feedforward compositionparallel(sys1, sys2)orsys1 + sys2for summed-output compositionsys1 - sys2for parallel subtraction
Interconnected systems must share a time domain and, for discrete models, a
sample period. The composition helpers validate these conditions. In addition,
discrete interconnections require matching dt.
State ordering is explicit:
series(sys2, sys1)putssys1states first, thensys2parallel(sys1, sys2)putssys1states first, thensys2
Transform Behavior¶
System containers are pytrees and compose with jit and vmap when their
shapes are compatible. This supports vmapped linearization, batched controller
design, and compiled design-and-simulation objectives.
The important user-facing transform contracts here are:
linearize()andlinearize_ss()are designed to compose withjitandvmapDiscLTI.dtis an array leaf, so batches can include different sample periods- composition helpers use state-space algebra with checked family, shape and matching-period contracts; period mismatch checks remain active under jit
Numerical Notes¶
Importing the package leaves JAX x64 mode unchanged. Precision-sensitive
helpers such as c2d() require jax_enable_x64=True.
linearize() and linearize_ss() approximate behavior near the chosen
operating point. Accuracy can decrease farther from that point.
The structured-system diagnostics report local algebraic properties at the supplied state and input. Interpret them with the model assumptions when assessing passivity or robustness.
Related Pages¶
- Simulation for
lsim(),simulate(), and response helpers - Control for LQR, Riccati solves, and state feedback
- Discretization and linearization
for the explanation page behind
c2d()andlinearize_ss() - Linearize, LQR, simulate for an end-to-end workflow
contrax.systems
¶
Public systems namespace.
This module gathers the system-model concepts that users typically look for first: LTI containers and constructors, nonlinear model containers, basic interconnection, and linearization helpers.
ContLTI
¶
Bases: Module
Continuous-time LTI system: ẋ = Ax + Bu, y = Cx + Du.
Source code in contrax/core.py
DiscLTI
¶
Bases: Module
Discrete-time LTI system: x_{k+1} = Ax_k + Bu_k, y_k = Cx_k + Du_k.
Source code in contrax/core.py
NonlinearSystem
¶
Bases: Module
Container for nonlinear dynamics and an optional output map.
Define dynamics with dynamics(t, x, u) and the measured output with
output(t, x, u).
Estimators can use the model's dynamics, input-dependent output, and sample
period directly. Recursive-estimator callables use the same (t, x, u)
signature and require a separate observation function. For discrete models,
step(t, x, u) returns the next state with numerical diagnostics.
Source code in contrax/nonlinear.py
output(t: Array | float, x: Array, u: Array) -> Array
¶
Evaluate the observation map, defaulting to the full state.
step(t: Array | float, x: Array, u: Array) -> SampleResult
¶
Evaluate a discrete transition and retain integration diagnostics.
Close over the model in jax.jit, or use Equinox filter_jit on this bound method. For sampled models, the model's integration adjoint determines derivative support; ordinary discrete transitions use their JAX graph.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
t
|
Array | float
|
Transition start time. Shape: scalar. |
required |
x
|
Array
|
Current state. Shape: |
required |
u
|
Array
|
Transition input, or an FOH endpoint pair for a sampled FOH model. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
SampleResult |
SampleResult
|
Next state, integration status and step count. |
Source code in contrax/nonlinear.py
PHSSystem
¶
Bases: Module
Port-Hamiltonian system with user-supplied storage and structure maps.
Source code in contrax/phs.py
as_nonlinear_system() -> NonlinearSystem
¶
Return the model as a generic nonlinear system.
Source code in contrax/phs.py
dynamics(t: Array | float, x: Array, u: Array) -> Array
¶
Evaluate the port-Hamiltonian vector field.
Source code in contrax/phs.py
SeparablePHSSystem
¶
Bases: PHSSystem
PHSSystem subtype for separable Hamiltonians H(q,p) = V(q) + T(p).
Stores V and T as separate callables so structure-preserving integrators
such as Störmer-Verlet can use their gradients directly.
The stored H is always the derived V(q) + T(p). All PHS behaviour
(dynamics, diagnostics, simulation) falls through to PHSSystem unchanged.
Create instances with separable_phs_system(), which validates the fields
and derives the Hamiltonian from V and T.
Source code in contrax/phs.py
as_nonlinear_system() -> NonlinearSystem
¶
Return the model as a generic nonlinear system.
Source code in contrax/phs.py
dynamics(t: Array | float, x: Array, u: Array) -> Array
¶
Evaluate the port-Hamiltonian vector field.
Source code in contrax/phs.py
block_matrix(row_block_sizes: Sequence[int], col_block_sizes: Sequence[int], blocks: dict[tuple[int, int], ArrayLike] | None = None, *, dtype: dtype | None = None) -> Array
¶
Assemble blocks with positive static sizes and (row, column) index keys.
Entries outside the declared grid or with incorrect shapes raise ValueError. Omitted blocks are zero; provided blocks determine the promoted dtype.
Source code in contrax/phs.py
block_observation(block_sizes: Sequence[int], block_indices: Sequence[int]) -> Callable[[Array | float, Array, Array], Array]
¶
Concatenate selected blocks, using positive static sizes and valid indices.
Source code in contrax/phs.py
c2d(sys: ContLTI, dt: float, method: str = 'zoh') -> DiscLTI
¶
Discretize a continuous-time state-space system.
Converts a ContLTI into a DiscLTI using either zero-order hold or the bilinear Tustin transform.
The zero-order-hold path uses the JAX matrix exponential, including for derivatives. Requires float64 precision.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sys
|
ContLTI
|
Continuous-time system. |
required |
dt
|
float
|
Sampling period. Shape: scalar. |
required |
method
|
str
|
Discretization method. Supported values are |
'zoh'
|
Returns:
| Type | Description |
|---|---|
DiscLTI
|
DiscLTI: A discrete-time realization with the requested sample time and the selected hold/frequency mapping. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
>>> import jax
>>> jax.config.update("jax_enable_x64", True)
>>> import jax.numpy as jnp
>>> import contrax as cx
>>> sys_c = cx.ss(
... jnp.array([[0.0, 1.0], [0.0, 0.0]]),
... jnp.array([[0.0], [1.0]]),
... jnp.eye(2),
... jnp.zeros((2, 1)),
... )
>>> sys_d = cx.c2d(sys_c, dt=0.05)
Source code in contrax/core.py
canonical_J(n_dofs: int, *, dtype: dtype | None = None) -> Array
¶
Construct the (2n, 2n) canonical structure for a positive static integer n.
Source code in contrax/phs.py
dss(A: ArrayLike, B: ArrayLike, C: ArrayLike, D: ArrayLike, dt: ArrayLike) -> DiscLTI
¶
Construct a discrete-time state-space system.
Wraps discrete-time matrices in a
DiscLTI pytree with MATLAB-familiar
ss(A, B, C, D, dt) ergonomics.
Stores validated matrices and the sampling period in a DiscLTI pytree.
The sample period is an array leaf, so it can vary across batched systems.
Matrix dtypes are promoted together, preserving explicit float32 data.
The sample time uses the resulting real matrix dtype.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
A
|
ArrayLike
|
State transition matrix. Shape: |
required |
B
|
ArrayLike
|
Input matrix. Shape: |
required |
C
|
ArrayLike
|
Output matrix. Shape: |
required |
D
|
ArrayLike
|
Feedthrough matrix. Shape: |
required |
dt
|
ArrayLike
|
Finite positive sampling period. Shape: scalar. |
required |
Returns:
| Type | Description |
|---|---|
DiscLTI
|
DiscLTI: A discrete-time system with
array-valued |
Raises:
| Type | Description |
|---|---|
ValueError
|
If matrix shapes or the real, positive sample time are invalid. |
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,
... )
Source code in contrax/core.py
linearize(model_or_f: DynamicsLike, x0: Array, u0: Array, *, output: OutputFn | None = None, t: ArrayLike = 0.0) -> ContLTI | DiscLTI
¶
Linearize dynamics and output maps, preserving a model's time domain.
Computes A, B, C, and D Jacobians at (x0, u0) using forward-mode
automatic differentiation and returns them as a
ContLTI or DiscLTI.
A discrete model supplies its sample period; plain dynamics callables are
interpreted as continuous vector fields. Supports jit, vmap and derivatives
whenever the underlying callbacks do. Sampled models inherit their integration
adjoint's transform support. State and input operating points must be vectors;
use jacfwd directly for structured inputs such as FOH endpoint pairs.
Use the local model for discretization and controller design. For example:
sys_lin = cx.linearize(nonlinear_sys, x_eq, u_eq)
sys_d = cx.c2d(sys_lin, dt=0.05)
K = cx.lqr(sys_d, Q, R).K
cx.simulate(nonlinear_sys, x0, lambda t, x: -K @ x, ...)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model_or_f
|
DynamicsLike
|
A continuous/discrete LTI or nonlinear system, or a pure JAX
vector field with signature |
required |
x0
|
Array
|
State operating point. Shape: |
required |
u0
|
Array
|
Input operating point. Shape: |
required |
output
|
OutputFn | None
|
Output function |
None
|
t
|
ArrayLike
|
Operating-point time. Relevant for time-varying system models. |
0.0
|
Returns:
| Type | Description |
|---|---|
ContLTI | DiscLTI
|
ContLTI | DiscLTI: Local perturbation model with Jacobians A, B, C, D. Discrete models retain their sample period. Constant operating-point offsets are not stored; states/inputs represent deviations from x0/u0. |
Examples:
>>> import jax.numpy as jnp
>>> import contrax as cx
>>> def dynamics(t, x, u):
... return jnp.array([x[1], -jnp.sin(x[0]) + u[0]])
>>> sys_lin = cx.linearize(dynamics, jnp.zeros(2), jnp.zeros(1))
Source code in contrax/core.py
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 367 | |
nonlinear_system(dynamics: Callable, output: Callable | None = None, *, observation: Callable | None = None, dt: ArrayLike | None = None, state_dim: int | None = None, input_dim: int | None = None, output_dim: int | None = None) -> NonlinearSystem
¶
Construct a reusable nonlinear system model.
The model supplies dynamics with signature dynamics(t, x, u) and an
optional output map with signature output(t, x, u). Use it for nonlinear
simulation, linearization, and estimation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dynamics
|
Callable
|
State transition or vector-field callable with signature
|
required |
output
|
Callable | None
|
Optional output/measurement map with signature |
None
|
observation
|
Callable | None
|
Deprecated synonym for |
None
|
dt
|
ArrayLike | None
|
Optional finite positive real scalar sample time. Omit for continuous models. |
None
|
state_dim
|
int | None
|
Optional nonnegative static integer state dimension. |
None
|
input_dim
|
int | None
|
Optional static input dimension. Enables omitted zero controls in recursive estimators; otherwise provide us for controlled models. |
None
|
output_dim
|
int | None
|
Optional static output dimension hint. Same caveat as
|
None
|
Returns:
| Type | Description |
|---|---|
NonlinearSystem
|
NonlinearSystem: A reusable nonlinear system model. |
Source code in contrax/nonlinear.py
parallel(sys1: DiscLTI | ContLTI, sys2: DiscLTI | ContLTI, *, sign: float = 1.0) -> DiscLTI | ContLTI
¶
Connect two systems in parallel and sum their outputs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sys1
|
DiscLTI | ContLTI
|
First system. |
required |
sys2
|
DiscLTI | ContLTI
|
Second system. |
required |
sign
|
float
|
Output sign applied to |
1.0
|
Returns:
| Type | Description |
|---|---|
DiscLTI | ContLTI
|
Source code in contrax/interconnect.py
partition_state(x: ArrayLike, block_sizes: Sequence[int]) -> tuple[Array, ...]
¶
Split a vector into blocks with positive static integer sizes summing to n.
Source code in contrax/phs.py
phs_diagnostics(sys: PHSSystem, x: ArrayLike, u: ArrayLike | None = None) -> PHSStructureDiagnostics
¶
Evaluate local structure and power-balance diagnostics for a PHS state.
Source code in contrax/phs.py
phs_system(H: Callable[[Array], Array], *, R: Callable[[Array], Array] | None = None, G: Callable[[Array], Array] | None = None, J: Callable[[Array], Array] | None = None, output: Callable | None = None, observation: Callable | None = None, dt: ArrayLike | None = None, state_dim: int | None = None, input_dim: int | None = None, output_dim: int | None = None) -> PHSSystem
¶
Construct a port-Hamiltonian system object.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
H
|
Callable[[Array], Array]
|
Storage/Hamiltonian function. |
required |
R
|
Callable[[Array], Array] | None
|
Optional dissipation map. |
None
|
G
|
Callable[[Array], Array] | None
|
Optional input map. |
None
|
J
|
Callable[[Array], Array] | None
|
Optional structure map. |
None
|
output
|
Callable | None
|
Optional output/measurement map with signature |
None
|
observation
|
Callable | None
|
Deprecated synonym for |
None
|
dt
|
ArrayLike | None
|
Optional finite positive real scalar sample time. |
None
|
state_dim
|
int | None
|
Optional nonnegative static integer; positive even for default J. |
None
|
input_dim
|
int | None
|
Optional nonnegative static integer input dimension. |
None
|
output_dim
|
int | None
|
Optional nonnegative static integer output dimension. |
None
|
Returns:
| Type | Description |
|---|---|
PHSSystem
|
PHSSystem: A reusable port-Hamiltonian model. |
Source code in contrax/phs.py
phs_to_ss(sys: PHSSystem, x_eq: Array, u_eq: Array) -> ContLTI | DiscLTI
¶
Linearize a port-Hamiltonian model, preserving its time domain.
Computes the Jacobian linearization of the PHS dynamics and observation map
at (x_eq, u_eq). The result models state/input/output deviations; constant
operating-point offsets are omitted. Continuous models use
delta_x_dot = A delta_x + B delta_u; discrete models use delta_x_next.
For a linear PHS with quadratic Hamiltonian H(x) = ½ x^T M x, the
linearization is exact and gives A = (J - R) M, B = G.
Calls linearize_ss on the system's nonlinear representation at time zero
and returns the local LTI model.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sys
|
PHSSystem
|
Port-Hamiltonian system. |
required |
x_eq
|
Array
|
Equilibrium state. Shape: |
required |
u_eq
|
Array
|
Equilibrium input. Shape: |
required |
Returns:
| Type | Description |
|---|---|
ContLTI | DiscLTI
|
ContLTI | DiscLTI: Local LTI approximation at |
Examples:
>>> import jax.numpy as jnp
>>> import contrax as cx
>>> def H(x): return 0.5 * jnp.dot(x, x)
>>> phs = cx.phs_system(H, state_dim=2, input_dim=1)
>>> lti = cx.phs_to_ss(phs, jnp.zeros(2), jnp.zeros(1))
>>> isinstance(lti, cx.ContLTI)
True
Source code in contrax/phs.py
project_psd(M: ArrayLike, *, min_eigenvalue: float = 0.0) -> Array
¶
Project a real square matrix onto the symmetric PSD cone.
min_eigenvalue must be a finite nonnegative scalar. Supports JAX transforms where the eigendecomposition is differentiable; repeated eigenvalues and the clipping boundary can make derivatives undefined.
Source code in contrax/phs.py
schedule_phs(sys: PHSSystem, context_fn: Callable[[Array | float], Any], *, J: Callable[[Array | float, Array, Any], Array] | None = None, R: Callable[[Array | float, Array, Any], Array] | None = None, G: Callable[[Array | float, Array, Any], Array] | None = None, observation: Callable[[Array | float, Array, Array, Any], Array] | None = None) -> NonlinearSystem
¶
Bind an exogenous schedule/context to a canonical PHS model.
The returned object is a NonlinearSystem accepted by simulation, estimation, and linearization functions.
context_fn(t) should be a pure JAX-friendly callable returning any pytree
of observed scheduling/context variables, such as a scale parameter
theta(t).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sys
|
PHSSystem
|
Canonical PHS model with state-only |
required |
context_fn
|
Callable[[Array | float], Any]
|
Callable mapping time to observed scheduling/context data. |
required |
J
|
Callable[[Array | float, Array, Any], Array] | None
|
Optional scheduled structure map |
None
|
R
|
Callable[[Array | float, Array, Any], Array] | None
|
Optional scheduled dissipation map |
None
|
G
|
Callable[[Array | float, Array, Any], Array] | None
|
Optional scheduled input map |
None
|
observation
|
Callable[[Array | float, Array, Array, Any], Array] | None
|
Optional scheduled observation map
|
None
|
Returns:
| Type | Description |
|---|---|
NonlinearSystem
|
NonlinearSystem: A scheduled nonlinear system with the context bound. |
Source code in contrax/phs.py
separable_phs_system(V: Callable[[Array], Array], T: Callable[[Array], Array], *, n_dofs: int, R: Callable[[Array], Array] | None = None, G: Callable[[Array], Array] | None = None, J: Callable[[Array], Array] | None = None, output: Callable | None = None, input_dim: int | None = None, output_dim: int | None = None) -> SeparablePHSSystem
¶
Construct a separable port-Hamiltonian system.
Builds a SeparablePHSSystem whose
Hamiltonian is H(q, p) = V(q) + T(p). The potential and kinetic energy
remain separate model fields. sample_system(method="sv") requires this
type with the default canonical J; other methods use the ordinary
PHSSystem interface.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
V
|
Callable[[Array], Array]
|
Potential energy |
required |
T
|
Callable[[Array], Array]
|
Kinetic energy |
required |
n_dofs
|
int
|
Number of generalized coordinates. |
required |
R
|
Callable[[Array], Array] | None
|
Optional dissipation map |
None
|
G
|
Callable[[Array], Array] | None
|
Optional input map |
None
|
J
|
Callable[[Array], Array] | None
|
Optional structure map. Defaults to |
None
|
output
|
Callable | None
|
Optional measurement map with signature |
None
|
input_dim
|
int | None
|
Optional static input dimension metadata. |
None
|
output_dim
|
int | None
|
Optional static output dimension metadata. |
None
|
Returns:
| Type | Description |
|---|---|
SeparablePHSSystem
|
SeparablePHSSystem with |
SeparablePHSSystem
|
derived from |
Examples:
>>> import jax.numpy as jnp
>>> import contrax as cx
>>> def V(q): return 0.5 * jnp.dot(q, q)
>>> def T(p): return 0.5 * jnp.dot(p, p)
>>> sys = cx.separable_phs_system(V, T, n_dofs=1, input_dim=1)
>>> isinstance(sys, cx.PHSSystem)
True
Source code in contrax/phs.py
series(sys2: DiscLTI | ContLTI, sys1: DiscLTI | ContLTI) -> DiscLTI | ContLTI
¶
Connect two systems in series as sys2 @ sys1.
The output of sys1 feeds the input of sys2. State ordering follows the
MATLAB-style convention: sys1 states first, then sys2 states.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sys2
|
DiscLTI | ContLTI
|
Downstream system. |
required |
sys1
|
DiscLTI | ContLTI
|
Upstream system. |
required |
Returns:
| Type | Description |
|---|---|
DiscLTI | ContLTI
|
Source code in contrax/interconnect.py
ss(A: ArrayLike, B: ArrayLike, C: ArrayLike, D: ArrayLike) -> ContLTI
¶
Construct a continuous-time state-space system.
Wraps continuous-time matrices in a
ContLTI pytree with MATLAB-familiar
ss(A, B, C, D) ergonomics.
Stores validated matrices in a data-only ContLTI pytree. Fixed-shape
matrix inputs can participate in JAX transformations.
Matrix dtypes are promoted together; explicit float32 arrays retain their
precision even when float64 mode is enabled. Integer matrices become floating.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
A
|
ArrayLike
|
State matrix. Shape: |
required |
B
|
ArrayLike
|
Input matrix. Shape: |
required |
C
|
ArrayLike
|
Output matrix. Shape: |
required |
D
|
ArrayLike
|
Feedthrough matrix. Shape: |
required |
Returns:
| Type | Description |
|---|---|
ContLTI
|
ContLTI: A continuous-time system with
array-valued |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the matrix shapes are inconsistent. |
Examples:
>>> import jax.numpy as jnp
>>> import contrax as cx
>>> sys = cx.ss(
... jnp.array([[0.0, 1.0], [0.0, 0.0]]),
... jnp.array([[0.0], [1.0]]),
... jnp.eye(2),
... jnp.zeros((2, 1)),
... )
Source code in contrax/core.py
symmetrize_matrix(M: ArrayLike) -> Array
¶
Return the symmetric part 0.5 * (M + M.T) of a square matrix.
Source code in contrax/phs.py
Optional python-control conversion¶
Install the optional bridge dependency with uv add control, then import
from_python_control and to_python_control from contrax.compat.python_control.
Both functions convert state-space realizations on the host; they do not support
JAX transforms. Enable JAX x64 before conversion to retain source float64 arrays.
Transfer functions must first be converted with control.ss.
An explicitly continuous source stays continuous. Positive discrete periods are
preserved or overridden by dt=. A source with dt=True requires a positive
explicit period; dt=None also requires an override, with zero selecting a
continuous model. Negative, nonfinite, complex and boolean overrides are invalid
for these timebase selections.