Skip to main content

StateSpace

Struct StateSpace 

Source
pub struct StateSpace { /* private fields */ }
Expand description

A linear time-invariant (LTI) state-space model.

ẋ = Ax + Bu y = Cx + Du

where:

  • x is the state vector (n×1)
  • u is the input vector (m×1)
  • y is the output vector (p×1)
  • A is the state matrix (n×n)
  • B is the input matrix (n×m)
  • C is the output matrix (p×n)
  • D is 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

Source

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()
);
Source

pub fn a(&self) -> &Matrix

State matrix A (n×n).

Source

pub fn b(&self) -> &Matrix

Input matrix B (n×m).

Source

pub fn c(&self) -> &Matrix

Output matrix C (p×n).

Source

pub fn d(&self) -> &Matrix

Feedthrough matrix D (p×m).

Source

pub fn num_states(&self) -> usize

Number of states (dimension of the state vector).

Source

pub fn num_inputs(&self) -> usize

Number of inputs (dimension of the input vector).

Source

pub fn num_outputs(&self) -> usize

Number of outputs (dimension of the output vector).

Source

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.

Source

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.)

Source

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.

Source

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);
Source

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.

Source

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.

Source

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.

Source

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.

Source

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).

Source

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.

Source

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.

Source

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

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for StateSpace

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for StateSpace

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more