package edu.stanford.rsl.conrad.utils;
import edu.stanford.rsl.conrad.geometry.Rotations;
import edu.stanford.rsl.conrad.geometry.Rotations.BasicAxis;
import edu.stanford.rsl.conrad.numerics.SimpleMatrix;
import edu.stanford.rsl.conrad.numerics.SimpleOperators;
import edu.stanford.rsl.conrad.numerics.SimpleVector;
import static org.junit.Assert.assertTrue;
public abstract class TestingTools {
///////////////////////////////////////////////////////
// numerical equivalence //
///////////////////////////////////////////////////////
/** delta for error margins */
public static final double DELTA = Math.sqrt(edu.stanford.rsl.conrad.utils.CONRAD.DOUBLE_EPSILON);
/** own assert for matrices */
public static final void assertEqualElementWise(final SimpleMatrix M1, final SimpleMatrix M2, final double delta) {
assertTrue(SimpleOperators.equalElementWise(M1, M2, delta));
}
/** own assert for vectors */
public static final void assertEqualElementWise(final SimpleVector v1, final SimpleVector v2, final double delta) {
assertTrue(SimpleOperators.equalElementWise(v1, v2, delta));
}
///////////////////////////////////////////////////////
// some generally useful random number generators //
///////////////////////////////////////////////////////
/** Randomly generates either +1.0 or -1.0, i.e. from the set {-1, +1}. */
public static final double randPmOne() { // {-1, +1}
return (Math.random() < 0.5) ? -1.0 : 1.0;
}
/** Randomly generates a number in [-2.0, 2.0) but not +1.0 or -1.0, i.e. from the set [-2.0, 2.0) \ {-1.0, 1.0}. */
public static final double randNotPmOne() { // [-2, 2) \ {-1, +1}
double notPmOne;
do notPmOne = 4.0 * (Math.random() - 0.5);
while (Math.abs(Math.abs(notPmOne) - 1.0) < DELTA);
return notPmOne;
}
/** Randomly generates a non-negative number, i.e. from the set [0.0, 1.0). */
public static final double randNonNegative() { // [0, 1), with an increased probability (50%) for 0
return (Math.random() < 0.5) ? 0.0 : Math.random();
}
/** Randomly generates , i.e. from the set .*/
public static final double randPositive() { // (0, 1)
return Math.random() + Double.MIN_VALUE;
}
/** Randomly generates , i.e. from the set .*/
public static final double randNonPositive() { // (-1, 0], with an increased probability (50%) for 0
return (Math.random() < 0.5) ? 0.0 : -Math.random();
}
/** Randomly generates , i.e. from the set .*/
public static final double randNegative() { // (-1, 0)
return -randPositive();
}
/** Randomly generates , i.e. from the set .*/
public static final double randNonZero() { // (-1, 1) \ {0}
return randPmOne() * randPositive();
}
/** Randomly generates , i.e. from the set .*/
public static final double rand(final double min, final double max) { // [min, max)
return min + (max-min)*Math.random();
}
/** Randomly generates , i.e. from the set .*/
public static final int rand(final int min, final int max) { // {min, ..., max}
return (int)Math.floor(rand((double)min, (double)(max+1)));
}
/** Randomly generates , i.e. from the set .*/
public static final double randAng() { // [-pi, pi), with an increased probability for -pi, -pi/2, 0, pi/2
double whatToDo = Math.random();
final double oneFifth = 1.0/5.0;
if (whatToDo < 1*oneFifth) return -Math.PI;
else if (whatToDo < 2*oneFifth) return -0.5*Math.PI;
else if (whatToDo < 3*oneFifth) return 0.0;
else if (whatToDo < 4*oneFifth) return 0.5*Math.PI;
else return (Math.random()-0.5) * 2.0 * Math.PI; // [-pi, pi)
}
/** Randomly generates a vector of the given length. */
public static final SimpleVector randVector(int len) {
assert len > 0;
SimpleVector v = new SimpleVector(len);
v.randomize(-1.0, 1.0);
return v;
}
/** Randomly generates a matrix of the given size. */
public static final SimpleMatrix randMatrix(int rows, int cols) {
assert (rows > 0 && cols > 0);
SimpleMatrix M = new SimpleMatrix(rows, cols);
M.randomize(-1.0, 1.0);
return M;
}
/** Randomly generates a matrix of the given size which is not singular. */
public static final SimpleMatrix randMatrixNonSingular(int size) {
assert (size > 0);
SimpleMatrix M = new SimpleMatrix(size, size);
do {
M.randomize(-1.0, 1.0);
} while (M.isSingular(DELTA));
return M;
}
/** Randomly generates a 2x2 rotation matrix (representing a 2D rotation). */
public static final SimpleMatrix randRotationMatrix2D() {
final SimpleMatrix R = new SimpleMatrix(2, 2);
final double ang = randAng();
final double c = Math.cos(ang);
final double s = Math.sin(ang);
R.setElementValue(0, 0, c);
R.setElementValue(0, 1, -s);
R.setElementValue(1, 0, s);
R.setElementValue(1, 1, c);
return R;
}
/** Randomly generates a 3x3 rotation matrix (representing a 3D rotation). */
public static final SimpleMatrix randRotationMatrix3D() {
final SimpleMatrix Rx = Rotations.createBasicRotationMatrix(BasicAxis.X_AXIS, randAng());
final SimpleMatrix Ry = Rotations.createBasicRotationMatrix(BasicAxis.Y_AXIS, randAng());
final SimpleMatrix Rz = Rotations.createBasicRotationMatrix(BasicAxis.Z_AXIS, randAng());
return SimpleOperators.multiplyMatrixProd(SimpleOperators.multiplyMatrixProd(Rx, Ry), Rz);
}
/** Randomly generates an orthogonal matrix of the given size, i.e. a matrix from O(size). */
public static final SimpleMatrix randOrthogonalMatrix(int size) {
// create random O
final SimpleMatrix Q = new SimpleMatrix(size, size);
Q.randomize(-1.0, 1.0);
// orthogonalize columns using the Gram-Schmidt algorithm
for (int col = 0; col < size; ++col) {
SimpleVector colVec = Q.getCol(col);
for (int prevCol = 0; prevCol < col; ++prevCol) {
SimpleVector prevColVec = Q.getCol(prevCol);
colVec.subtract(prevColVec.multipliedBy(SimpleOperators.multiplyInnerProd(colVec, prevColVec)));
}
colVec.normalizeL2();
Q.setColValue(col, colVec);
}
// orthogonalize rows using the Gram-Schmidt algorithm
// this additional orthogonalization is not necessary in theory but should enhance the numerical orthogonality of the matrix
for (int row = 0; row < size; ++row) {
SimpleVector rowVec = Q.getRow(row);
for (int prevRow = 0; prevRow < row; ++prevRow) {
SimpleVector prevRowVec = Q.getRow(prevRow);
rowVec.subtract(prevRowVec.multipliedBy(SimpleOperators.multiplyInnerProd(rowVec, prevRowVec)));
}
rowVec.normalizeL2();
Q.setRowValue(row, rowVec);
}
return Q;
}
/** Randomly generates an special orthogonal matrix of the given size, i.e. a matrix from SO(size). */
public static final SimpleMatrix randSpecialOrthogonalMatrix(int size) {
// create orthogonal matrix
final SimpleMatrix Q = randOrthogonalMatrix(size);
// make sure it's from SO(size), not just O(size)
Q.multiplyBy(Q.determinant());
return Q;
}
/** Randomly generates an upper-triangular matrix of the given size. */
public static final SimpleMatrix randUpperTriangularMatrix(int rows, int cols) {
SimpleMatrix U = new SimpleMatrix(rows, cols);
U.randomize(-1.0, 1.0);
for (int row = 1; row < rows; ++row)
for (int col = 0; col < Math.min(cols, row); ++col)
U.setElementValue(row, col, 0.0);
return U;
}
/** Randomly generates a lower-triangular matrix of the given size. */
public static final SimpleMatrix randLowerTriangularMatrix(int rows, int cols) {
SimpleMatrix L = new SimpleMatrix(rows, cols);
L.randomize(-1.0, 1.0);
for (int row = 0; row < rows; ++row)
for (int col = row+1; col < cols; ++col)
L.setElementValue(row, col, 0.0);
return L;
}
}
/*
* Copyright (C) 2010-2014 Andreas Maier
* CONRAD is developed as an Open Source project under the GNU General Public License (GPL).
*/