pub struct StateSpace { /* private fields */ }Expand description
A linear time-invariant (LTI) state-space model.
ẋ = Ax + Bu y = Cx + Du
where:
xis the state vector (n×1)uis the input vector (m×1)yis the output vector (p×1)Ais the state matrix (n×n)Bis the input matrix (n×m)Cis the output matrix (p×n)Dis the feedthrough matrix (p×m)
The fields are private: StateSpace::new is the only way in, so every
model has conformant shapes. Read them with a,
b, c and d.
Implementations§
Source§impl StateSpace
impl StateSpace
Sourcepub fn new(
a: Matrix,
b: Matrix,
c: Matrix,
d: Matrix,
) -> Result<Self, SymplexError>
pub fn new( a: Matrix, b: Matrix, c: Matrix, d: Matrix, ) -> Result<Self, SymplexError>
Create a new state-space model from the four system matrices.
§Errors
Returns SymplexError::InvalidArgument if the dimensions are
inconsistent:
- A must be n×n (square)
- B must be n×m
- C must be p×n
- D must be p×m
§Examples
use symplex::prelude::*;
let ctx = Context::new();
let ss = StateSpace::new(
matrix![ctx, [0, 1], [-2, -3]],
matrix![ctx, [0], [1]],
matrix![ctx, [1, 0]],
matrix![ctx, [0]],
)
.unwrap();
assert_eq!((ss.num_states(), ss.num_inputs(), ss.num_outputs()), (2, 1, 1));
// B has 1 row, A has 2.
assert!(
StateSpace::new(
matrix![ctx, [0, 1], [-2, -3]],
matrix![ctx, [1]],
matrix![ctx, [1, 0]],
matrix![ctx, [0]],
)
.is_err()
);Sourcepub fn num_states(&self) -> usize
pub fn num_states(&self) -> usize
Number of states (dimension of the state vector).
Sourcepub fn num_inputs(&self) -> usize
pub fn num_inputs(&self) -> usize
Number of inputs (dimension of the input vector).
Sourcepub fn num_outputs(&self) -> usize
pub fn num_outputs(&self) -> usize
Number of outputs (dimension of the output vector).
Sourcepub fn poles(&self) -> Vec<Ex> ⓘ
pub fn poles(&self) -> Vec<Ex> ⓘ
Poles of the system (eigenvalues of A), repeated with multiplicity.
Returns an empty vector if the eigenvalue solver cannot find any root of the characteristic polynomial.
Sourcepub fn char_poly(&self, s: &Ex) -> Ex
pub fn char_poly(&self, s: &Ex) -> Ex
Characteristic polynomial: det(sI - A).
Returns a polynomial expression in the given variable s, or NaN if
the determinant cannot be computed — use
try_char_poly for the error. (A is
square: StateSpace::new validates it.)
Sourcepub fn try_char_poly(&self, s: &Ex) -> Result<Ex, SymplexError>
pub fn try_char_poly(&self, s: &Ex) -> Result<Ex, SymplexError>
Characteristic polynomial det(sI − A) in the variable s.
§Errors
Returns an error if the determinant cannot be computed. A is
square by construction, so a shape error cannot occur.
Sourcepub fn to_transfer_function(
&self,
s: &Ex,
) -> Result<TransferFunction, SymplexError>
pub fn to_transfer_function( &self, s: &Ex, ) -> Result<TransferFunction, SymplexError>
Transfer function G(s) = C (sI − A)⁻¹ B + D of a single-input,
single-output system.
The result has denominator det(sI − A) and numerator
C·adj(sI − A)·B + D·det(sI − A), both expanded polynomials in
s; common factors are not cancelled.
§Errors
Returns SymplexError::InvalidArgument if the system is not SISO.
§Examples
use symplex::prelude::*;
let ctx = Context::new();
let s = ctx.symbol("s");
// ẋ = [[0, 1], [-2, -3]] x + [0, 1] u, y = [1, 0] x
let ss = StateSpace::new(
matrix![ctx, [0, 1], [-2, -3]],
matrix![ctx, [0], [1]],
matrix![ctx, [1, 0]],
matrix![ctx, [0]],
)
.unwrap();
let g = ss.to_transfer_function(&s).unwrap();
// G(s) = 1 / (s² + 3s + 2)
assert_eq!(g.num, ctx.int(1));
assert_eq!(g.den, &s.powi(2) + &s * 3 + 2);Sourcepub fn controllability_matrix(&self) -> Result<Matrix, SymplexError>
pub fn controllability_matrix(&self) -> Result<Matrix, SymplexError>
Controllability matrix: [B, AB, A²B, …, Aⁿ⁻¹B].
For an n-state, m-input system, this is an n×(n·m) matrix. The system is controllable if and only if this matrix has rank n.
§Errors
Returns an error if a matrix product fails. The shapes of A and
B are validated by StateSpace::new, so a shape error cannot
occur.
Sourcepub fn observability_matrix(&self) -> Result<Matrix, SymplexError>
pub fn observability_matrix(&self) -> Result<Matrix, SymplexError>
Observability matrix: [C; CA; CA²; …; CAⁿ⁻¹].
For an n-state, p-output system, this is an (n·p)×n matrix. The system is observable if and only if this matrix has rank n.
§Errors
Returns an error if a matrix product fails. The shapes of A and
C are validated by StateSpace::new, so a shape error cannot
occur.
Sourcepub fn is_controllable(&self) -> bool
pub fn is_controllable(&self) -> bool
Check controllability: rank(controllability_matrix) == n.
Returns true if the system is fully state controllable, false
if it is not or the controllability matrix cannot be computed.
Sourcepub fn is_observable(&self) -> bool
pub fn is_observable(&self) -> bool
Check observability: rank(observability_matrix) == n.
Returns true if the system is fully observable, false if it is
not or the observability matrix cannot be computed.
Sourcepub fn is_stable(&self) -> Option<bool>
pub fn is_stable(&self) -> Option<bool>
Check stability: all eigenvalues have negative real part.
Returns Some(true) if all poles are in the open left half-plane.
Returns Some(false) if any pole can be shown to have non-negative real part.
Returns None if stability cannot be determined symbolically.
Numeric poles are decided by certified evaluation. When some pole
is symbolic (or not found), the Routh–Hurwitz test
is_routh_stable decides from the coefficients of det(sI − A),
as SymPy’s get_asymptotic_stability_conditions does: A = [[−k, 1], [0, −k − 1]] with k > 0 is stable (before 0.41 every
symbolic pole gave None).
Sourcepub fn discretize_zoh(
&self,
dt: &Ex,
order: usize,
) -> Result<StateSpace, SymplexError>
pub fn discretize_zoh( &self, dt: &Ex, order: usize, ) -> Result<StateSpace, SymplexError>
Discretize using zero-order hold (ZOH).
Aᵈ = eᴬᵈᵗ (matrix exponential) Bᵈ = A⁻¹(eᴬᵈᵗ - I)B — computed via series to avoid inverting A.
Uses series approximation for the matrix exponential.
dt is the sampling period (symbolic or numeric).
order is the Taylor series truncation order.
The discrete-time B matrix is computed as: Bᵈ = (I·dt + A·dt²/2! + A²·dt³/3! + …)B which avoids requiring A to be invertible.
§Errors
Returns an error if the matrix exponential series or a matrix
product fails. The shapes of A and B are validated by
StateSpace::new, so a shape error cannot occur.
Sourcepub fn riccati_residual(
&self,
p: &Matrix,
q: &Matrix,
r: &Matrix,
) -> Result<Matrix, SymplexError>
pub fn riccati_residual( &self, p: &Matrix, q: &Matrix, r: &Matrix, ) -> Result<Matrix, SymplexError>
Set up the continuous-time algebraic Riccati equation (CARE).
Returns the residual matrix: AᵀP + PA − PBR⁻¹BᵀP + Q which should equal zero when P is the solution.
§Errors
Returns an error if R is singular, or if P is not n×n, Q is not
n×n or R is not m×m.
Sourcepub fn ackermann(&self, desired_poles: &[Ex]) -> Result<Matrix, SymplexError>
pub fn ackermann(&self, desired_poles: &[Ex]) -> Result<Matrix, SymplexError>
Pole placement via Ackermann’s formula (single-input systems only).
Given desired pole locations, computes feedback gain K such that the eigenvalues of (A − BK) equal the desired poles.
Only works for single-input (m=1) controllable systems. The number of desired poles must equal the number of states.
§Errors
Returns SymplexError::InvalidArgument if the system is not
single-input or the number of desired poles is not n, and
SymplexError::ComputationFailed if the system is not controllable.
Trait Implementations§
Source§impl Clone for StateSpace
impl Clone for StateSpace
Source§impl Debug for StateSpace
impl Debug for StateSpace
Auto Trait Implementations§
impl !Freeze for StateSpace
impl !RefUnwindSafe for StateSpace
impl !UnwindSafe for StateSpace
impl Send for StateSpace
impl Sync for StateSpace
impl Unpin for StateSpace
impl UnsafeUnpin for StateSpace
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more