diff --git a/src/control_laws.rs b/src/control_laws.rs index 4be88f5..286b461 100644 --- a/src/control_laws.rs +++ b/src/control_laws.rs @@ -3,7 +3,9 @@ use std::ops::Mul; -use ndarray::{linalg::kron, s, Array1, Array2, Data, LinalgScalar, ScalarOperand}; +use ndarray::{ + linalg::kron, s, Array1, Array2, ArrayBase, Data, Ix1, Ix2, LinalgScalar, ScalarOperand, +}; use ndarray_linalg::{error::LinalgError, EigVals, Lapack, Scalar}; /// Create the consensus feedback control law for homogenous single-integrators. @@ -33,13 +35,13 @@ use ndarray_linalg::{error::LinalgError, EigVals, Lapack, Scalar}; /// /// assert_eq!(step_state, array![0., 0., 1., 2., 0., 0.]); /// ``` -pub fn single_integrator_consensus<'a, T: LinalgScalar>( - neg_laplacian: &Array2, - offsets: &'a Array1, +pub fn single_integrator_consensus<'a, T: LinalgScalar, S: Data>( + neg_laplacian: &ArrayBase, + offsets: &'a ArrayBase, n_states: usize, -) -> impl Fn(T, &Array1) -> Array1 + 'a { +) -> impl Fn(T, &ArrayBase) -> Array1 + 'a { let feedback_mat = kron(neg_laplacian, &Array2::eye(n_states)); - move |_t: T, x: &Array1| -> Array1 { feedback_mat.dot(&(x - offsets)) } + move |_t: T, x: &ArrayBase| -> Array1 { feedback_mat.dot(&(x - offsets)) } } /// Create the control for single integrator forced consensus. @@ -79,17 +81,22 @@ pub fn single_integrator_consensus<'a, T: LinalgScalar>( /// ) /// ) /// ``` -pub fn single_integrator_forced_consensus<'a, T: LinalgScalar>( - neg_laplacian: &Array2, - offsets: &'a Array1, - pinning_gains: &Array1, - reference: impl Fn(T) -> Array1 + 'a, +pub fn single_integrator_forced_consensus< + 'a, + T: LinalgScalar, + S1: Data, + S2: Data, +>( + neg_laplacian: &ArrayBase, + offsets: &'a ArrayBase, + pinning_gains: &'a ArrayBase, + reference: impl Fn(T) -> ArrayBase + 'a, n_states: usize, -) -> impl Fn(T, &Array1) -> Array1 + 'a { +) -> impl Fn(T, &ArrayBase) -> Array1 + 'a { let pinning_gains = pinning_gains.clone(); let neg_l_plus_k = neg_laplacian - Array2::from_diag(&pinning_gains); let state_feedback = kron(&neg_l_plus_k, &Array2::eye(n_states)); - move |t: T, x: &Array1| -> Array1 { + move |t: T, x: &ArrayBase| -> Array1 { let r_vec = reference(t); let pinned_ref = kron( &pinning_gains.slice(s![.., ndarray::NewAxis]), @@ -110,13 +117,18 @@ pub fn single_integrator_forced_consensus<'a, T: LinalgScalar>( /// $$ /// where $c$ is the coupling gain, $K$ the feedback gain matrix, $L$ is the /// Laplacian, and $\otimes$ denotes the Kronecker product. -pub fn homogeneous_leaderless_synchronization( - neg_laplacian: &Array2, +pub fn homogeneous_leaderless_synchronization< + 'a, + T: LinalgScalar + ScalarOperand, + S: Data, +>( + neg_laplacian: &ArrayBase, + offsets: &'a ArrayBase, coupling_gain: T, - feedback_gain: &Array2, -) -> impl Fn(T, &Array1) -> Array1 { + feedback_gain: &ArrayBase, +) -> impl Fn(T, &ArrayBase) -> Array1 + 'a { let feedback_mat = kron(&neg_laplacian.mul(coupling_gain), feedback_gain); - move |_t: T, x: &Array1| -> Array1 { feedback_mat.dot(x) } + move |_t: T, x: &ArrayBase| feedback_mat.dot(&(x - offsets)) } /// Create the leader-follower synchronizing controller for a homogenous MAS @@ -180,8 +192,10 @@ pub fn homogeneous_leaderless_synchronization( /// .unwrap(), /// ) /// }; +/// let offsets = array![0., 0., 0., 0., 0., 0.]; /// let control = homogeneous_leader_follower_synchronization( /// &-laplacian, +/// &offsets, /// c, /// &k_mat, /// &pinning_gains, @@ -197,17 +211,16 @@ pub fn homogeneous_leaderless_synchronization( pub fn homogeneous_leader_follower_synchronization< 'a, T: LinalgScalar + ScalarOperand, - S: Data, + S1: Data, + S2: Data, >( - neg_laplacian: &Array2, + neg_laplacian: &ArrayBase, + offsets: &'a ArrayBase, coupling_gain: T, - feedback_gain: &Array2, - pinning_gains: &Array1, - reference: impl Fn(T) -> ndarray::ArrayBase + 'a, -) -> impl Fn(T, &Array1) -> Array1 + 'a -where - S: ndarray::Data, -{ + feedback_gain: &ArrayBase, + pinning_gains: &ArrayBase, + reference: impl Fn(T) -> ArrayBase + 'a, +) -> impl Fn(T, &ArrayBase) -> Array1 + 'a { let pinning_gains = Array2::from_diag(pinning_gains); let feedback_mat = kron(&(neg_laplacian - &pinning_gains), feedback_gain).mul(coupling_gain); let reference_mat = kron(&pinning_gains, feedback_gain).mul(coupling_gain); @@ -218,7 +231,7 @@ where .flatten() .cloned(), ); - feedback_mat.dot(x) + reference_mat.dot(&reference) + feedback_mat.dot(&(x - offsets)) + reference_mat.dot(&reference) } } @@ -228,8 +241,8 @@ where /// $$ /// c = \frac{1}{2\operatorname{Re}(\lambda_2(L))} /// $$ -pub fn coupling_gain( - laplacian: &Array2, +pub fn coupling_gain>( + laplacian: &ArrayBase, ) -> Result { // let (_, eig, _) = SVD::svd(laplacian, false, false)?; let eig = laplacian.eigvals()?; @@ -356,7 +369,8 @@ mod tests { ) .unwrap(); let c = coupling_gain(&laplacian).unwrap(); - let control = homogeneous_leaderless_synchronization(&-laplacian, c, &k_mat); + let offsets = array![0., 0., 0., 0., 0., 0.]; + let control = homogeneous_leaderless_synchronization(&-laplacian, &offsets, c, &k_mat); let state_step = EulerIntegration::step(0.0, 1.0, &x0, &mas, &control); assert!(state_step.abs_diff_eq( @@ -405,8 +419,10 @@ mod tests { .unwrap(), ) }; + let offsets = array![0., 0., 0., 0., 0., 0.]; let control = homogeneous_leader_follower_synchronization( &-laplacian, + &offsets, c, &k_mat, &pinning_gains,