use nalgebra::{Matrix3, RowVector3, Vector3};
use num_complex::Complex;
use std::fmt;
const Z_CPLX: Complex<f64> = Complex { re: 0., im: 0. };
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Default)]
pub struct ExponentialMatrix {
pub tau: f64,
phi: (f64, f64, f64, f64),
gamma: (f64, f64),
b: Vec<f64>,
c: Vec<f64>,
pub y: Vec<f64>,
x: (f64, f64),
}
impl ExponentialMatrix {
pub fn n_inputs(&self) -> usize {
self.b.len()
}
pub fn n_outputs(&self) -> usize {
self.c.len()
}
}
impl super::Solver for ExponentialMatrix {
fn from_second_order(
tau: f64,
omega: f64,
zeta: f64,
continuous_bb: Vec<f64>,
continuous_cc: Vec<f64>,
) -> Self {
let exp_3by3m = if omega != 0. {
let lambda_cplx = Complex {
re: -omega * zeta,
im: omega * (1. - (zeta * zeta)).sqrt(),
};
let v11 = Complex {
re: 1. / ((1. + omega.powi(4)).sqrt()),
im: 0.,
};
let v31 = (omega * omega) * v11;
let v12 = 1. / (1. + lambda_cplx.norm_sqr()).sqrt();
let v13 = 1. / (1. + lambda_cplx.conj().norm_sqr()).sqrt();
let v = Matrix3::from_columns(&[
Vector3::new(v11, Z_CPLX, v31),
Vector3::new(Complex { re: v12, im: 0. }, lambda_cplx * v12, Z_CPLX),
Vector3::new(
Complex { re: v13, im: 0. },
lambda_cplx.conj() * v13,
Z_CPLX,
),
]);
let k_row2 = (v12 * (lambda_cplx.conj() - lambda_cplx)).inv();
let k_row3 = (v13 * (lambda_cplx.conj() - lambda_cplx)).inv();
let inv_v = Matrix3::from_rows(&[
RowVector3::new(Z_CPLX, Z_CPLX, v31.inv()),
RowVector3::new(
lambda_cplx.conj() * k_row2,
Complex { re: -1., im: 0. } * k_row2,
-lambda_cplx.conj().unscale(omega * omega) * k_row2,
),
RowVector3::new(
-lambda_cplx * k_row3,
Complex { re: 1., im: 0. } * k_row3,
lambda_cplx.unscale(omega * omega) * k_row3,
),
]);
let diag_exp = Matrix3::from_columns(&[
Vector3::new(Complex { re: 1., im: 0. }, Z_CPLX, Z_CPLX),
Vector3::new(Z_CPLX, lambda_cplx.scale(tau).exp(), Z_CPLX),
Vector3::new(Z_CPLX, Z_CPLX, lambda_cplx.conj().scale(tau).exp()),
]);
v * diag_exp * inv_v
} else {
Matrix3::from_columns(&[
Vector3::new(Complex { re: 1., im: 0. }, Z_CPLX, Z_CPLX),
Vector3::new(
Complex { re: tau, im: 0. },
Complex { re: 1., im: 0. },
Z_CPLX,
),
Vector3::new(
Complex {
re: 0.5 * tau * tau,
im: 0.,
},
Complex { re: tau, im: 0. },
Complex { re: 1., im: 0. },
),
])
};
let n = continuous_cc.len();
Self {
tau,
phi: (
exp_3by3m[0].re,
exp_3by3m[3].re,
exp_3by3m[1].re,
exp_3by3m[4].re,
),
gamma: (exp_3by3m[6].re, exp_3by3m[7].re),
b: continuous_bb,
c: continuous_cc,
y: vec![0.; n],
x: (0f64, 0f64),
}
}
fn solve(&mut self, u: &[f64]) -> &[f64] {
let (x0, x1): (f64, f64) = self.x;
let v = self.b.iter().zip(u).fold(0., |s, (b, u)| s + b * u);
self.x.0 = self.phi.0 * x0 + self.phi.1 * x1 + self.gamma.0 * v;
self.x.1 = self.phi.2 * x0 + self.phi.3 * x1 + self.gamma.1 * v;
self.y.iter_mut().zip(self.c.iter()).for_each(|(y, c)| {
*y = c * self.x.0;
});
self.y.as_slice()
}
}
impl fmt::Display for ExponentialMatrix {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"2x2 discrete state space model: {}->{} ({:.3}Hz)\n - A: {:.9?}\n - B: {:.9?}",
self.b.len(),
self.c.len(),
self.tau.recip(),
self.phi,
self.gamma
)
}
}