extern crate nalgebra as na;
use crate::model::StateSpaceModel;
pub fn compute_controllability_matrix(
mat_a: &na::DMatrix<f64>,
mat_b: &na::DMatrix<f64>,
) -> Result<na::DMatrix<f64>, &'static str> {
if !mat_a.is_square() {
return Err("Error when computing controllability matrix. The A matrix is not square.");
}
let n = mat_a.nrows(); let mut controllability_matrix = mat_b.clone_owned();
for i in 1..n {
let column_block = mat_a.pow(i as u32) * mat_b; controllability_matrix = na::stack![controllability_matrix, column_block];
}
Ok(controllability_matrix)
}
pub fn is_ss_controllable<T: StateSpaceModel>(model: &T) -> (bool, na::DMatrix<f64>) {
let mat_a = model.mat_a();
let mat_b = model.mat_b();
let mat_contr = compute_controllability_matrix(&mat_a, &mat_b)
.expect("State matrix of the model is not square");
return (mat_contr.rank(1e-3) == mat_a.nrows(), mat_contr);
}
pub fn compute_observability_matrix(
mat_a: &na::DMatrix<f64>,
mat_c: &na::DMatrix<f64>,
) -> Result<na::DMatrix<f64>, &'static str> {
if !mat_a.is_square() {
return Err("Error when computing observability matrix. The A matrix is not square.");
}
let n = mat_a.nrows(); let mut observability_mat = mat_c.clone_owned();
for i in 1..n {
let column_block = mat_c * mat_a.pow(i as u32);
observability_mat = na::stack![observability_mat; column_block];
}
Ok(observability_mat)
}
pub fn is_ss_observable<T: StateSpaceModel>(model: &T) -> (bool, na::DMatrix<f64>) {
let mat_a = model.mat_a();
let mat_c = model.mat_c();
let mat_obs = compute_observability_matrix(&mat_a, &mat_c)
.expect("State matrix of the model is not square");
return (mat_obs.rank(1e-3) == mat_a.nrows(), mat_obs);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::model::DiscreteStateSpaceModel;
#[test]
fn test_compute_controllability_matrix_2x2() {
let mat_a = nalgebra::dmatrix![1.0, -2.0;
2.0, 1.0];
let mat_b = nalgebra::dmatrix![1.0;
2.0];
let result = compute_controllability_matrix(&mat_a, &mat_b).unwrap();
assert_eq!(result.nrows(), mat_b.ncols() * mat_a.nrows());
assert_eq!(result.ncols(), mat_a.ncols());
assert_eq!(result, na::stack![mat_b, mat_a * &mat_b])
}
#[test]
fn test_compute_controllability_matrix_mat_a_not_square() {
let mat_a = nalgebra::dmatrix![1.0, -2.0];
let mat_b = nalgebra::dmatrix![1.0;
2.0];
let result = compute_controllability_matrix(&mat_a, &mat_b);
assert_eq!(
result,
Err("Error when computing controllability matrix. The A matrix is not square.")
);
}
#[test]
fn test_controllability_2x2_controllable() {
let ss_model = DiscreteStateSpaceModel::from_matrices(
&nalgebra::dmatrix![1.0, -2.0;
2.0, 1.0],
&nalgebra::dmatrix![1.0;
2.0],
&nalgebra::dmatrix![],
&nalgebra::dmatrix![],
0.05,
);
let (result, _) = is_ss_controllable(&ss_model);
assert!(result);
}
#[test]
fn test_controllability_3x3_not_controllable() {
let ss_model = DiscreteStateSpaceModel::from_matrices(
&na::dmatrix![
0.0, 1.0, 0.0;
0.0, 0.0, 1.0;
0.0, 0.0, 0.0],
&na::dmatrix![
1.0;
0.0;
0.0],
&nalgebra::dmatrix![],
&nalgebra::dmatrix![],
0.05,
);
assert!(!is_ss_controllable(&ss_model).0);
}
#[test]
fn test_compute_observability_matrix_2x2() {
let mat_a = nalgebra::dmatrix![1.0, -2.0;
2.0, 1.0];
let mat_c = nalgebra::dmatrix![1.0, 2.0];
let result = compute_observability_matrix(&mat_a, &mat_c).unwrap();
assert_eq!(result.nrows(), mat_a.ncols());
assert_eq!(result.ncols(), mat_c.ncols());
assert_eq!(result, na::stack![&mat_c; &mat_c * mat_a]);
}
#[test]
fn test_compute_observability_matrix_mat_a_not_square() {
let mat_a = nalgebra::dmatrix![1.0, -2.0];
let mat_b = nalgebra::dmatrix![1.0;
2.0];
let result = compute_observability_matrix(&mat_a, &mat_b);
assert_eq!(
result,
Err("Error when computing observability matrix. The A matrix is not square.")
);
}
#[test]
fn test_is_observable_2x2_observable_system() {
let ss_model = DiscreteStateSpaceModel::from_matrices(
&nalgebra::dmatrix![1.0, -2.0;
2.0, 1.0],
&nalgebra::dmatrix![],
&nalgebra::dmatrix![1.0, 2.0],
&nalgebra::dmatrix![],
0.05,
);
let (result, _) = is_ss_observable(&ss_model);
assert!(result);
}
#[test]
fn test_observability_3x3_not_observable() {
let ss_model = DiscreteStateSpaceModel::from_matrices(
&na::dmatrix![
1.0, 0.0, 0.0;
0.0, 1.0, 0.0;
0.0, 0.0, 0.0],
&na::dmatrix![],
&nalgebra::dmatrix![1.0, 0.0, 0.0],
&nalgebra::dmatrix![],
0.05,
);
let (observable, obs_mat) = is_ss_observable(&ss_model);
assert!(!observable);
assert_eq!(
obs_mat,
na::dmatrix![
1.0, 0.0, 0.0;
1.0, 0.0, 0.0;
1.0, 0.0, 0.0]
);
}
}