use std::ops::{AddAssign, MulAssign};
use nalgebra::{ComplexField, DMatrix, DVector};
use num_traits::Float;
use crate::{Discrete, TransferFunction};
use crate::system_identification::arx::Arx;
use crate::system_identification::lsm;
pub struct InstrumentalVariable<T> {
pub arx: Arx<T>,
pub auxiliary: Arx<T>,
x_hat: T,
zeta_y_sum: DVector<T>,
zeta_phi_sum: DMatrix<T>,
}
impl<T: Float + AddAssign + MulAssign + ComplexField> InstrumentalVariable<T> {
pub fn new(auxiliary: &Arx<T>) -> Self {
let n = auxiliary.parameter_len();
let mut auxiliary = auxiliary.clone();
auxiliary.clear_history();
let mut arx = auxiliary.clone();
arx.parameter.fill(T::zero());
Self {
arx,
auxiliary,
x_hat: T::zero(),
zeta_y_sum: DVector::zeros(n),
zeta_phi_sum: DMatrix::zeros(n, n),
}
}
pub fn add(&mut self, u: T, x: T, y: T) {
self.arx.push(u, x);
let phi = self.arx.regressor();
self.auxiliary.push(u, self.x_hat);
let zeta = self.auxiliary.regressor();
self.x_hat = self.auxiliary.predict();
self.zeta_y_sum += &zeta * y;
self.zeta_phi_sum += &zeta * &phi.transpose();
}
pub fn identify(&mut self) -> Option<TransferFunction<T, Discrete>> {
self.arx.parameter = self.zeta_phi_sum.clone().lu().solve(&self.zeta_y_sum)?;
Some(self.arx.transfer_function())
}
}
pub fn identify<T>(u: &[T], y: &[T], structure: &Arx<T>, iterations: usize) -> Option<Arx<T>>
where
T: Float + AddAssign + MulAssign + ComplexField,
{
let mut model = least_squares(u, y, structure)?;
for _ in 0..iterations {
model = iv_step(u, y, &model.stabilized())?;
}
Some(model)
}
pub fn identify_sriv<T>(u: &[T], y: &[T], structure: &Arx<T>, iterations: usize) -> Option<Arx<T>>
where
T: Float + AddAssign + MulAssign + ComplexField,
{
let mut model = least_squares(u, y, structure)?;
for _ in 0..iterations {
let stable = model.stabilized();
let f = stable.parameter.rows(0, stable.state_order());
let prefilter = |x: &[T]| {
let mut xf = x.to_vec();
for k in 0..xf.len() {
for (i, &fi) in f.iter().enumerate().take(k) {
let v = xf[k - i - 1];
xf[k] += fi * v;
}
}
xf
};
model = iv_step(&prefilter(u), &prefilter(y), &stable)?;
}
Some(model)
}
fn least_squares<T>(u: &[T], y: &[T], structure: &Arx<T>) -> Option<Arx<T>>
where
T: Float + AddAssign + MulAssign + ComplexField,
{
let mut arx = structure.clone();
arx.clear_history();
let mut buffer = lsm::arx::LeastSquares::from_arx(arx);
for (k, (&uk, &yk)) in u.iter().zip(y).enumerate() {
buffer.add(uk, if k > 0 { y[k - 1] } else { T::zero() }, yk);
}
buffer.identify()?;
Some(buffer.arx)
}
fn iv_step<T>(u: &[T], y: &[T], auxiliary: &Arx<T>) -> Option<Arx<T>>
where
T: Float + AddAssign + MulAssign + ComplexField,
{
let mut buffer = InstrumentalVariable::new(auxiliary);
for (k, (&uk, &yk)) in u.iter().zip(y).enumerate() {
buffer.add(uk, if k > 0 { y[k - 1] } else { T::zero() }, yk);
}
buffer.identify()?;
buffer.arx.parameter.iter().all(|v| v.is_finite()).then_some(buffer.arx)
}