2023-05-14 22:23:00 -07:00
|
|
|
|
// Copyright (c) FIRST and other WPILib contributors.
|
|
|
|
|
|
// Open Source Software; you can modify and/or share it under the terms of
|
|
|
|
|
|
// the WPILib BSD license file in the root directory of this project.
|
|
|
|
|
|
|
|
|
|
|
|
package edu.wpi.first.math;
|
|
|
|
|
|
|
|
|
|
|
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
2023-08-12 19:45:45 -07:00
|
|
|
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
2023-05-14 22:23:00 -07:00
|
|
|
|
|
2023-06-09 00:11:26 -04:00
|
|
|
|
import edu.wpi.first.wpilibj.UtilityClassTest;
|
2023-05-14 22:23:00 -07:00
|
|
|
|
import org.ejml.simple.SimpleMatrix;
|
|
|
|
|
|
import org.junit.jupiter.api.Test;
|
|
|
|
|
|
|
2023-06-09 00:11:26 -04:00
|
|
|
|
class DARETest extends UtilityClassTest<DARE> {
|
|
|
|
|
|
DARETest() {
|
|
|
|
|
|
super(DARE.class);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-08-17 13:56:15 -07:00
|
|
|
|
public static <R extends Num, C extends Num> void assertMatrixEqual(
|
|
|
|
|
|
Matrix<R, C> A, Matrix<R, C> B) {
|
2023-08-11 23:25:43 -07:00
|
|
|
|
for (int i = 0; i < A.getNumRows(); i++) {
|
|
|
|
|
|
for (int j = 0; j < A.getNumCols(); j++) {
|
2023-05-14 22:23:00 -07:00
|
|
|
|
assertEquals(A.get(i, j), B.get(i, j), 1e-4);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-08-17 13:56:15 -07:00
|
|
|
|
<States extends Num, Inputs extends Num> void assertDARESolution(
|
|
|
|
|
|
Matrix<States, States> A,
|
|
|
|
|
|
Matrix<States, Inputs> B,
|
|
|
|
|
|
Matrix<States, States> Q,
|
|
|
|
|
|
Matrix<Inputs, Inputs> R,
|
|
|
|
|
|
Matrix<States, States> X) {
|
2023-05-14 22:23:00 -07:00
|
|
|
|
// Check that X is the solution to the DARE
|
|
|
|
|
|
// Y = AᵀXA − X − AᵀXB(BᵀXB + R)⁻¹BᵀXA + Q = 0
|
|
|
|
|
|
var Y =
|
2023-08-17 13:56:15 -07:00
|
|
|
|
(A.transpose().times(X).times(A))
|
2023-05-14 22:23:00 -07:00
|
|
|
|
.minus(X)
|
|
|
|
|
|
.minus(
|
2023-08-17 13:56:15 -07:00
|
|
|
|
(A.transpose().times(X).times(B))
|
|
|
|
|
|
.times((B.transpose().times(X).times(B).plus(R)).inv())
|
|
|
|
|
|
.times(B.transpose().times(X).times(A)))
|
2023-05-14 22:23:00 -07:00
|
|
|
|
.plus(Q);
|
2023-08-17 13:56:15 -07:00
|
|
|
|
assertMatrixEqual(
|
|
|
|
|
|
new Matrix<States, States>(new SimpleMatrix(Y.getNumRows(), Y.getNumCols())), Y);
|
2023-05-14 22:23:00 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-08-17 13:56:15 -07:00
|
|
|
|
<States extends Num, Inputs extends Num> void assertDARESolution(
|
|
|
|
|
|
Matrix<States, States> A,
|
|
|
|
|
|
Matrix<States, Inputs> B,
|
|
|
|
|
|
Matrix<States, States> Q,
|
|
|
|
|
|
Matrix<Inputs, Inputs> R,
|
|
|
|
|
|
Matrix<States, Inputs> N,
|
|
|
|
|
|
Matrix<States, States> X) {
|
2023-05-14 22:23:00 -07:00
|
|
|
|
// Check that X is the solution to the DARE
|
|
|
|
|
|
// Y = AᵀXA − X − (AᵀXB + N)(BᵀXB + R)⁻¹(BᵀXA + Nᵀ) + Q = 0
|
|
|
|
|
|
var Y =
|
2023-08-17 13:56:15 -07:00
|
|
|
|
(A.transpose().times(X).times(A))
|
2023-05-14 22:23:00 -07:00
|
|
|
|
.minus(X)
|
|
|
|
|
|
.minus(
|
2023-08-17 13:56:15 -07:00
|
|
|
|
(A.transpose().times(X).times(B).plus(N))
|
|
|
|
|
|
.times((B.transpose().times(X).times(B).plus(R)).inv())
|
|
|
|
|
|
.times(B.transpose().times(X).times(A).plus(N.transpose())))
|
2023-05-14 22:23:00 -07:00
|
|
|
|
.plus(Q);
|
2023-08-17 13:56:15 -07:00
|
|
|
|
assertMatrixEqual(
|
|
|
|
|
|
new Matrix<States, States>(new SimpleMatrix(Y.getNumRows(), Y.getNumCols())), Y);
|
2023-05-14 22:23:00 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
|
void testNonInvertibleA_ABQR() {
|
|
|
|
|
|
// Example 2 of "On the Numerical Solution of the Discrete-Time Algebraic
|
|
|
|
|
|
// Riccati Equation"
|
|
|
|
|
|
|
|
|
|
|
|
var A =
|
2023-08-17 13:56:15 -07:00
|
|
|
|
new Matrix<>(
|
|
|
|
|
|
Nat.N4(), Nat.N4(), new double[] {0.5, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0});
|
|
|
|
|
|
var B = new Matrix<>(Nat.N4(), Nat.N1(), new double[] {0, 0, 0, 1});
|
2023-05-14 22:23:00 -07:00
|
|
|
|
var Q =
|
2023-08-17 13:56:15 -07:00
|
|
|
|
new Matrix<>(
|
|
|
|
|
|
Nat.N4(), Nat.N4(), new double[] {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
|
|
|
|
|
|
var R = new Matrix<>(Nat.N1(), Nat.N1(), new double[] {0.25});
|
2023-05-14 22:23:00 -07:00
|
|
|
|
|
|
|
|
|
|
var X = DARE.dare(A, B, Q, R);
|
|
|
|
|
|
assertMatrixEqual(X, X.transpose());
|
|
|
|
|
|
assertDARESolution(A, B, Q, R, X);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
|
void testNonInvertibleA_ABQRN() {
|
|
|
|
|
|
// Example 2 of "On the Numerical Solution of the Discrete-Time Algebraic
|
|
|
|
|
|
// Riccati Equation"
|
|
|
|
|
|
|
|
|
|
|
|
var A =
|
2023-08-17 13:56:15 -07:00
|
|
|
|
new Matrix<>(
|
|
|
|
|
|
Nat.N4(), Nat.N4(), new double[] {0.5, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0});
|
|
|
|
|
|
var B = new Matrix<>(Nat.N4(), Nat.N1(), new double[] {0, 0, 0, 1});
|
2023-05-14 22:23:00 -07:00
|
|
|
|
var Q =
|
2023-08-17 13:56:15 -07:00
|
|
|
|
new Matrix<>(
|
|
|
|
|
|
Nat.N4(), Nat.N4(), new double[] {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
|
|
|
|
|
|
var R = new Matrix<>(Nat.N1(), Nat.N1(), new double[] {0.25});
|
2023-05-14 22:23:00 -07:00
|
|
|
|
|
|
|
|
|
|
var Aref =
|
2023-08-17 13:56:15 -07:00
|
|
|
|
new Matrix<>(
|
|
|
|
|
|
Nat.N4(), Nat.N4(), new double[] {0.25, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0});
|
|
|
|
|
|
Q = A.minus(Aref).transpose().times(Q).times(A.minus(Aref));
|
|
|
|
|
|
R = B.transpose().times(Q).times(B).plus(R);
|
|
|
|
|
|
var N = A.minus(Aref).transpose().times(Q).times(B);
|
2023-05-14 22:23:00 -07:00
|
|
|
|
|
|
|
|
|
|
var X = DARE.dare(A, B, Q, R, N);
|
|
|
|
|
|
assertMatrixEqual(X, X.transpose());
|
|
|
|
|
|
assertDARESolution(A, B, Q, R, N, X);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
|
void testInvertibleA_ABQR() {
|
2023-08-17 13:56:15 -07:00
|
|
|
|
var A = new Matrix<>(Nat.N2(), Nat.N2(), new double[] {1, 1, 0, 1});
|
|
|
|
|
|
var B = new Matrix<>(Nat.N2(), Nat.N1(), new double[] {0, 1});
|
|
|
|
|
|
var Q = new Matrix<>(Nat.N2(), Nat.N2(), new double[] {1, 0, 0, 0});
|
|
|
|
|
|
var R = new Matrix<>(Nat.N1(), Nat.N1(), new double[] {0.3});
|
2023-05-14 22:23:00 -07:00
|
|
|
|
|
|
|
|
|
|
var X = DARE.dare(A, B, Q, R);
|
|
|
|
|
|
assertMatrixEqual(X, X.transpose());
|
|
|
|
|
|
assertDARESolution(A, B, Q, R, X);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
|
void testInvertibleA_ABQRN() {
|
2023-08-17 13:56:15 -07:00
|
|
|
|
var A = new Matrix<>(Nat.N2(), Nat.N2(), new double[] {1, 1, 0, 1});
|
|
|
|
|
|
var B = new Matrix<>(Nat.N2(), Nat.N1(), new double[] {0, 1});
|
|
|
|
|
|
var Q = new Matrix<>(Nat.N2(), Nat.N2(), new double[] {1, 0, 0, 0});
|
|
|
|
|
|
var R = new Matrix<>(Nat.N1(), Nat.N1(), new double[] {0.3});
|
2023-05-14 22:23:00 -07:00
|
|
|
|
|
2023-08-17 13:56:15 -07:00
|
|
|
|
var Aref = new Matrix<>(Nat.N2(), Nat.N2(), new double[] {0.5, 1, 0, 1});
|
|
|
|
|
|
Q = A.minus(Aref).transpose().times(Q).times(A.minus(Aref));
|
|
|
|
|
|
R = B.transpose().times(Q).times(B).plus(R);
|
|
|
|
|
|
var N = A.minus(Aref).transpose().times(Q).times(B);
|
2023-05-14 22:23:00 -07:00
|
|
|
|
|
|
|
|
|
|
var X = DARE.dare(A, B, Q, R, N);
|
|
|
|
|
|
assertMatrixEqual(X, X.transpose());
|
|
|
|
|
|
assertDARESolution(A, B, Q, R, N, X);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
|
void testFirstGeneralizedEigenvalueOfSTIsStable_ABQR() {
|
|
|
|
|
|
// The first generalized eigenvalue of (S, T) is stable
|
|
|
|
|
|
|
2023-08-17 13:56:15 -07:00
|
|
|
|
var A = new Matrix<>(Nat.N2(), Nat.N2(), new double[] {0, 1, 0, 0});
|
|
|
|
|
|
var B = new Matrix<>(Nat.N2(), Nat.N1(), new double[] {0, 1});
|
|
|
|
|
|
var Q = new Matrix<>(Nat.N2(), Nat.N2(), new double[] {1, 0, 0, 1});
|
|
|
|
|
|
var R = new Matrix<>(Nat.N1(), Nat.N1(), new double[] {1});
|
2023-05-14 22:23:00 -07:00
|
|
|
|
|
|
|
|
|
|
var X = DARE.dare(A, B, Q, R);
|
|
|
|
|
|
assertMatrixEqual(X, X.transpose());
|
|
|
|
|
|
assertDARESolution(A, B, Q, R, X);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
|
void testFirstGeneralizedEigenvalueOfSTIsStable_ABQRN() {
|
|
|
|
|
|
// The first generalized eigenvalue of (S, T) is stable
|
|
|
|
|
|
|
2023-08-17 13:56:15 -07:00
|
|
|
|
var A = new Matrix<>(Nat.N2(), Nat.N2(), new double[] {0, 1, 0, 0});
|
|
|
|
|
|
var B = new Matrix<>(Nat.N2(), Nat.N1(), new double[] {0, 1});
|
|
|
|
|
|
var Q = new Matrix<>(Nat.N2(), Nat.N2(), new double[] {1, 0, 0, 1});
|
|
|
|
|
|
var R = new Matrix<>(Nat.N1(), Nat.N1(), new double[] {1});
|
2023-05-14 22:23:00 -07:00
|
|
|
|
|
2023-08-17 13:56:15 -07:00
|
|
|
|
var Aref = new Matrix<>(Nat.N2(), Nat.N2(), new double[] {0, 0.5, 0, 0});
|
|
|
|
|
|
Q = A.minus(Aref).transpose().times(Q).times(A.minus(Aref));
|
|
|
|
|
|
R = B.transpose().times(Q).times(B).plus(R);
|
|
|
|
|
|
var N = A.minus(Aref).transpose().times(Q).times(B);
|
2023-05-14 22:23:00 -07:00
|
|
|
|
|
|
|
|
|
|
var X = DARE.dare(A, B, Q, R, N);
|
|
|
|
|
|
assertMatrixEqual(X, X.transpose());
|
|
|
|
|
|
assertDARESolution(A, B, Q, R, N, X);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
|
void testIdentitySystem_ABQR() {
|
2023-08-17 13:56:15 -07:00
|
|
|
|
var A = Matrix.eye(Nat.N2());
|
|
|
|
|
|
var B = Matrix.eye(Nat.N2());
|
|
|
|
|
|
var Q = Matrix.eye(Nat.N2());
|
|
|
|
|
|
var R = Matrix.eye(Nat.N2());
|
2023-05-14 22:23:00 -07:00
|
|
|
|
|
|
|
|
|
|
var X = DARE.dare(A, B, Q, R);
|
|
|
|
|
|
assertMatrixEqual(X, X.transpose());
|
|
|
|
|
|
assertDARESolution(A, B, Q, R, X);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
|
void testIdentitySystem_ABQRN() {
|
2023-08-17 13:56:15 -07:00
|
|
|
|
var A = Matrix.eye(Nat.N2());
|
|
|
|
|
|
var B = Matrix.eye(Nat.N2());
|
|
|
|
|
|
var Q = Matrix.eye(Nat.N2());
|
|
|
|
|
|
var R = Matrix.eye(Nat.N2());
|
|
|
|
|
|
var N = Matrix.eye(Nat.N2());
|
2023-05-14 22:23:00 -07:00
|
|
|
|
|
|
|
|
|
|
var X = DARE.dare(A, B, Q, R, N);
|
|
|
|
|
|
assertMatrixEqual(X, X.transpose());
|
|
|
|
|
|
assertDARESolution(A, B, Q, R, N, X);
|
|
|
|
|
|
}
|
2023-08-12 19:45:45 -07:00
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
|
void testMoreInputsThanStates_ABQR() {
|
2023-08-17 13:56:15 -07:00
|
|
|
|
var A = Matrix.eye(Nat.N2());
|
|
|
|
|
|
var B = new Matrix<>(Nat.N2(), Nat.N3(), new double[] {1, 0, 0, 0, 0.5, 0.3});
|
|
|
|
|
|
var Q = Matrix.eye(Nat.N2());
|
|
|
|
|
|
var R = Matrix.eye(Nat.N3());
|
2023-08-12 19:45:45 -07:00
|
|
|
|
|
|
|
|
|
|
var X = DARE.dare(A, B, Q, R);
|
|
|
|
|
|
assertMatrixEqual(X, X.transpose());
|
|
|
|
|
|
assertDARESolution(A, B, Q, R, X);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
|
void testMoreInputsThanStates_ABQRN() {
|
2023-08-17 13:56:15 -07:00
|
|
|
|
var A = Matrix.eye(Nat.N2());
|
|
|
|
|
|
var B = new Matrix<>(Nat.N2(), Nat.N3(), new double[] {1, 0, 0, 0, 0.5, 0.3});
|
|
|
|
|
|
var Q = Matrix.eye(Nat.N2());
|
|
|
|
|
|
var R = Matrix.eye(Nat.N3());
|
|
|
|
|
|
var N = new Matrix<>(Nat.N2(), Nat.N3(), new double[] {1, 0, 0, 0, 1, 0});
|
2023-08-12 19:45:45 -07:00
|
|
|
|
|
|
|
|
|
|
var X = DARE.dare(A, B, Q, R, N);
|
|
|
|
|
|
assertMatrixEqual(X, X.transpose());
|
|
|
|
|
|
assertDARESolution(A, B, Q, R, N, X);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
|
void testQNotSymmetricPositiveSemidefinite_ABQR() {
|
2023-08-17 13:56:15 -07:00
|
|
|
|
var A = Matrix.eye(Nat.N2());
|
|
|
|
|
|
var B = Matrix.eye(Nat.N2());
|
|
|
|
|
|
var Q = new Matrix<>(Nat.N2(), Nat.N2(), new double[] {-1.0, 0.0, 0.0, -1.0});
|
|
|
|
|
|
var R = Matrix.eye(Nat.N2());
|
2023-08-12 19:45:45 -07:00
|
|
|
|
|
|
|
|
|
|
assertThrows(IllegalArgumentException.class, () -> DARE.dare(A, B, Q, R));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
|
void testQNotSymmetricPositiveSemidefinite_ABQRN() {
|
2023-08-17 13:56:15 -07:00
|
|
|
|
var A = Matrix.eye(Nat.N2());
|
|
|
|
|
|
var B = Matrix.eye(Nat.N2());
|
|
|
|
|
|
var Q = Matrix.eye(Nat.N2());
|
|
|
|
|
|
var R = new Matrix<>(Nat.N2(), Nat.N2(), new double[] {-1.0, 0.0, 0.0, -1.0});
|
|
|
|
|
|
var N = new Matrix<>(Nat.N2(), Nat.N2(), new double[] {2.0, 0.0, 0.0, 2.0});
|
2023-08-12 19:45:45 -07:00
|
|
|
|
|
|
|
|
|
|
assertThrows(IllegalArgumentException.class, () -> DARE.dare(A, B, Q, R, N));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
|
void testRNotSymmetricPositiveDefinite_ABQR() {
|
2023-08-17 13:56:15 -07:00
|
|
|
|
var A = Matrix.eye(Nat.N2());
|
|
|
|
|
|
var B = Matrix.eye(Nat.N2());
|
|
|
|
|
|
var Q = Matrix.eye(Nat.N2());
|
2023-08-12 19:45:45 -07:00
|
|
|
|
|
2023-08-17 13:56:15 -07:00
|
|
|
|
var R1 = new Matrix<>(Nat.N2(), Nat.N2());
|
2023-08-12 19:45:45 -07:00
|
|
|
|
assertThrows(IllegalArgumentException.class, () -> DARE.dare(A, B, Q, R1));
|
|
|
|
|
|
|
2023-08-17 13:56:15 -07:00
|
|
|
|
var R2 = new Matrix<>(Nat.N2(), Nat.N2(), new double[] {-1.0, 0.0, 0.0, -1.0});
|
2023-08-12 19:45:45 -07:00
|
|
|
|
assertThrows(IllegalArgumentException.class, () -> DARE.dare(A, B, Q, R2));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
|
void testRNotSymmetricPositiveDefinite_ABQRN() {
|
2023-08-17 13:56:15 -07:00
|
|
|
|
var A = Matrix.eye(Nat.N2());
|
|
|
|
|
|
var B = Matrix.eye(Nat.N2());
|
|
|
|
|
|
var Q = Matrix.eye(Nat.N2());
|
|
|
|
|
|
var N = Matrix.eye(Nat.N2());
|
2023-08-12 19:45:45 -07:00
|
|
|
|
|
2023-08-17 13:56:15 -07:00
|
|
|
|
var R1 = new Matrix<>(Nat.N2(), Nat.N2());
|
2023-08-12 19:45:45 -07:00
|
|
|
|
assertThrows(IllegalArgumentException.class, () -> DARE.dare(A, B, Q, R1, N));
|
|
|
|
|
|
|
2023-08-17 13:56:15 -07:00
|
|
|
|
var R2 = new Matrix<>(Nat.N2(), Nat.N2(), new double[] {-1.0, 0.0, 0.0, -1.0});
|
2023-08-12 19:45:45 -07:00
|
|
|
|
assertThrows(IllegalArgumentException.class, () -> DARE.dare(A, B, Q, R2, N));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
|
void testABNotStabilizable_ABQR() {
|
2023-08-17 13:56:15 -07:00
|
|
|
|
var A = Matrix.eye(Nat.N2());
|
|
|
|
|
|
var B = new Matrix<>(Nat.N2(), Nat.N2());
|
|
|
|
|
|
var Q = Matrix.eye(Nat.N2());
|
|
|
|
|
|
var R = Matrix.eye(Nat.N2());
|
2023-08-12 19:45:45 -07:00
|
|
|
|
|
|
|
|
|
|
assertThrows(IllegalArgumentException.class, () -> DARE.dare(A, B, Q, R));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
|
void testABNotStabilizable_ABQRN() {
|
2023-08-17 13:56:15 -07:00
|
|
|
|
var A = Matrix.eye(Nat.N2());
|
|
|
|
|
|
var B = new Matrix<>(Nat.N2(), Nat.N2());
|
|
|
|
|
|
var Q = Matrix.eye(Nat.N2());
|
|
|
|
|
|
var R = Matrix.eye(Nat.N2());
|
|
|
|
|
|
var N = Matrix.eye(Nat.N2());
|
2023-08-12 19:45:45 -07:00
|
|
|
|
|
|
|
|
|
|
assertThrows(IllegalArgumentException.class, () -> DARE.dare(A, B, Q, R, N));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
|
void testACNotDetectable_ABQR() {
|
2023-08-17 13:56:15 -07:00
|
|
|
|
var A = Matrix.eye(Nat.N2());
|
|
|
|
|
|
var B = Matrix.eye(Nat.N2());
|
|
|
|
|
|
var Q = new Matrix<>(Nat.N2(), Nat.N2());
|
|
|
|
|
|
var R = Matrix.eye(Nat.N2());
|
2023-08-12 19:45:45 -07:00
|
|
|
|
|
|
|
|
|
|
assertThrows(IllegalArgumentException.class, () -> DARE.dare(A, B, Q, R));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
|
void testACNotDetectable_ABQRN() {
|
2023-08-17 13:56:15 -07:00
|
|
|
|
var A = Matrix.eye(Nat.N2());
|
|
|
|
|
|
var B = Matrix.eye(Nat.N2());
|
|
|
|
|
|
var Q = new Matrix<>(Nat.N2(), Nat.N2());
|
|
|
|
|
|
var R = Matrix.eye(Nat.N2());
|
|
|
|
|
|
var N = new Matrix<>(Nat.N2(), Nat.N2());
|
2023-08-12 19:45:45 -07:00
|
|
|
|
|
|
|
|
|
|
assertThrows(IllegalArgumentException.class, () -> DARE.dare(A, B, Q, R, N));
|
|
|
|
|
|
}
|
2023-05-14 22:23:00 -07:00
|
|
|
|
}
|