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). */