use crate::system::principal_part;
use crate::laplace_transform::{
jump_rational, DelayedRationalSum, PiecewisePolynomial, StableInverse, StableInverseError, TimeExpression,
};
use crate::{vieta_formula, Continuous, PartialFraction, PoleTerm, Polynomial, TransferFunction};
use num_complex::Complex;
use num_traits::{Float, Zero};
use std::ops::AddAssign;
#[derive(Clone, Debug)]
pub struct StateReference<T> {
states: Vec<Vec<Component<T>>>,
polynomial: Option<(PiecewisePolynomial<T>, Vec<Vec<T>>)>,
}
#[derive(Clone, Debug)]
struct Component<T> {
delay: T,
causal: TimeExpression<T>,
anticausal: TimeExpression<T>,
}
impl<T: Float> Component<T> {
fn new(delay: T, inv: StableInverse<T>) -> Self {
Self { delay, causal: inv.causal_expression(), anticausal: inv.anticausal_expression() }
}
fn eval(&self, t: T) -> T {
let t = t - self.delay;
if t < T::zero() {
self.anticausal.eval(t)
} else {
self.causal.eval(t)
}
}
}
pub trait ReferenceSignal<T> {
fn to_state_reference(&self, plant: &TransferFunction<T, Continuous>) -> Result<StateReference<T>, StableInverseError>;
}
struct Numerator<T> {
coefficients: Vec<T>,
scale: T,
zeros: Vec<PoleTerm<T>>,
order: usize,
}
impl<T: Float + AddAssign> Numerator<T> {
fn of(plant: &TransferFunction<T, Continuous>) -> Result<Self, StableInverseError> {
let coefficients: Vec<T> = plant.numerator.iter().copied().skip_while(|c| c.is_zero()).collect();
let order = plant.denominator.iter().skip_while(|c| c.is_zero()).count().saturating_sub(1);
let Some(&scale) = coefficients.last() else {
return Err(StableInverseError::ZeroSystem);
};
let zeros = TransferFunction::<T, Continuous>::from_polynomials(
Polynomial(vec![T::one()]),
Polynomial(coefficients.clone()),
)
.partial_fraction()
.terms;
if let Some(z) = zeros.iter().find(|z| z.pole.re.is_zero()) {
return Err(StableInverseError::PoleOnImaginaryAxis { re: to_f64(z.pole.re), im: to_f64(z.pole.im) });
}
Ok(Self { coefficients, scale, zeros, order })
}
fn degree(&self) -> usize {
self.coefficients.len() - 1
}
fn scaled_s_j(&self, j: usize) -> Polynomial<T> {
let mut p = Polynomial(vec![T::zero(); j + 1]);
p[0] = self.scale;
p
}
}
impl<T: Float + AddAssign> ReferenceSignal<T> for DelayedRationalSum<T> {
fn to_state_reference(&self, plant: &TransferFunction<T, Continuous>) -> Result<StateReference<T>, StableInverseError> {
let numer = Numerator::of(plant)?;
let causal_re = self
.terms
.iter()
.flat_map(|c| c.rational.partial_fraction().terms)
.map(|term| term.pole.re)
.chain(numer.zeros.iter().map(|z| z.pole.re).filter(|&re| re < T::zero()))
.reduce(T::max);
let anticausal_re = numer.zeros.iter().map(|z| z.pole.re).filter(|&re| re > T::zero()).reduce(T::min);
let sigma = match (causal_re, anticausal_re) {
(Some(lo), Some(hi)) if lo < hi => (lo + hi) / T::from(2.0).unwrap(),
(Some(lo), Some(hi)) => {
return Err(StableInverseError::NoRegionOfConvergence { causal_re: to_f64(lo), anticausal_re: to_f64(hi) });
}
(Some(lo), None) => lo + T::one(),
(None, Some(hi)) => hi - T::one(),
(None, None) => T::zero(),
};
let n_poly = Polynomial(numer.coefficients.clone());
let states = (0..numer.order)
.map(|j| {
let scaled_s_j = numer.scaled_s_j(j);
self.terms
.iter()
.map(|c| {
let pf = TransferFunction::<T, Continuous>::from_polynomials(
&scaled_s_j * &c.rational.numerator,
&n_poly * &c.rational.denominator,
)
.partial_fraction();
if !pf.direct.is_empty() {
return Err(StableInverseError::NotSmoothEnough { state: j });
}
Ok(Component::new(c.delay, StableInverse::bilateral_with_abscissa(pf, sigma)?))
})
.collect::<Result<Vec<_>, _>>()
})
.collect::<Result<Vec<_>, _>>()?;
Ok(StateReference { states, polynomial: None })
}
}
impl<T: Float + AddAssign> ReferenceSignal<T> for PiecewisePolynomial<T> {
fn to_state_reference(&self, plant: &TransferFunction<T, Continuous>) -> Result<StateReference<T>, StableInverseError> {
let numer = Numerator::of(plant)?;
let jumps = self.jumps();
if let Some(lowest) = jumps.iter().map(|jump| jump.order()).min() {
let first_bad = numer.degree() + lowest + 1;
if first_bad < numer.order {
return Err(StableInverseError::NotSmoothEnough { state: first_bad });
}
}
let count = self.degree() + 1;
let n_ascending: Vec<T> = numer.coefficients.iter().rev().copied().collect();
let series: Vec<Vec<T>> = (0..numer.order)
.map(|j| {
let mut f = vec![T::zero(); count];
for k in 0..count {
let mut acc = if k == j { numer.scale } else { T::zero() };
for l in 1..=k.min(numer.degree()) {
acc = acc - n_ascending[l] * f[k - l];
}
f[k] = acc / n_ascending[0];
}
f
})
.collect();
let lead = Complex::from(numer.coefficients[0]);
let states = (0..numer.order)
.map(|j| {
jumps
.iter()
.map(|jump| {
let rational = jump_rational(jump);
let mut num: Vec<Complex<T>> =
rational.numerator.iter().map(|&c| Complex::from(c * numer.scale)).collect();
num.extend(std::iter::repeat_n(Complex::zero(), j));
let power_of_s = rational.denominator.len() - 1;
let terms = numer
.zeros
.iter()
.enumerate()
.map(|(i, z)| {
let others: Vec<Complex<T>> = numer
.zeros
.iter()
.enumerate()
.filter(|&(k, _)| k != i)
.flat_map(|(_, o)| std::iter::repeat_n(o.pole, o.multiplicity()))
.collect();
let mut rest: Vec<Complex<T>> =
vieta_formula(&others).0.iter().map(|&c| c * lead).collect();
rest.extend(std::iter::repeat_n(Complex::zero(), power_of_s));
PoleTerm { pole: z.pole, residues: principal_part(&num, &rest, z.pole, z.multiplicity()) }
})
.collect();
let pf = PartialFraction { direct: Polynomial(vec![]), terms };
Ok(Component::new(jump.time, StableInverse::bilateral(pf)?))
})
.collect::<Result<Vec<_>, _>>()
})
.collect::<Result<Vec<_>, _>>()?;
Ok(StateReference { states, polynomial: Some((self.clone(), series)) })
}
}
impl<T: Float> StateReference<T> {
pub fn order(&self) -> usize {
self.states.len()
}
pub fn state(&self, t: T) -> Vec<T> {
let mut x: Vec<T> = self
.states
.iter()
.map(|components| {
components.iter().fold(T::zero(), |acc, c| acc + c.eval(t))
})
.collect();
if let Some((y_d, series)) = &self.polynomial {
let count = series.first().map_or(0, |f| f.len());
let derivatives = y_d.derivatives(t, count);
for (xj, f) in x.iter_mut().zip(series) {
*xj = *xj + f.iter().zip(&derivatives).fold(T::zero(), |acc, (&fk, &dk)| acc + fk * dk);
}
}
x
}
pub fn sample(&self, t0: T, ts: T, samples: usize) -> Vec<Vec<T>> {
(0..samples).map(|k| self.state(t0 + ts * T::from(k).unwrap())).collect()
}
}
fn to_f64<T: Float>(x: T) -> f64 {
x.to_f64().unwrap_or(f64::NAN)
}