Newer
Older
#include <cxxtest/TestSuite.h>
#include <cmath>
#include <ostream>
#include <vector>
#include "MantidKernel/Exception.h"
Gigg, Martyn Anthony
committed
#include "MantidKernel/V3D.h"
Gigg, Martyn Anthony
committed
#include <boost/lexical_cast.hpp>
Gigg, Martyn Anthony
committed
using Mantid::Kernel::Matrix;
using Mantid::Kernel::DblMatrix;
using Mantid::Kernel::V3D;
class MatrixTest : public CxxTest::TestSuite {
void makeMatrix(Matrix<double> &A) const {
A.setMem(3, 3);
A[0][0] = 1.0;
A[1][0] = 3.0;
A[0][1] = 4.0;
A[0][2] = 6.0;
A[2][0] = 5.0;
A[1][1] = 3.0;
A[2][1] = 1.0;
A[1][2] = 6.0;
A[2][2] = -7.0;
Janik Zikovsky
committed
/**
void testInvert() {
Matrix<double> A(3, 3);
A[0][0] = 1.0;
A[1][0] = 3.0;
A[0][1] = 4.0;
A[0][2] = 6.0;
A[2][0] = 5.0;
A[1][1] = 3.0;
A[2][1] = 1.0;
A[1][2] = 6.0;
A[2][2] = -7.0;
TS_ASSERT_DELTA(A.Invert(), 105.0, 1e-5);
Matrix<double> B(1, 1);
B[0][0] = 2.;
TS_ASSERT_DELTA(B.Invert(), 2, 1e-5);
TS_ASSERT_DELTA(B[0][0], 0.5, 1e-5);
void testIdent() {
Matrix<double> A(3, 3);
A[0][0] = 1.0;
A[1][0] = 0.0;
A[0][1] = 0.0;
A[0][2] = 0.0;
A[2][0] = 0.0;
A[1][1] = 1.0;
A[2][1] = 0.0;
A[1][2] = 0.0;
A[2][2] = 1.0;
Matrix<double> Ident(3, 3);
TS_ASSERT_DIFFERS(Ident, A);
Gigg, Martyn Anthony
committed
/** Test of equals with a user-specified tolerance */
void test_equals() {
Matrix<double> A(3, 3, true);
Matrix<double> B(3, 3, true);
Gigg, Martyn Anthony
committed
B[1][1] = 1.1;
TS_ASSERT(!A.equals(B, 0.05));
TS_ASSERT(A.equals(B, 0.15));
Gigg, Martyn Anthony
committed
}
void test_not_equal() {
Matrix<double> A(3, 3, true);
Matrix<double> B(3, 3, true);
TS_ASSERT(A != B);
TS_ASSERT(!(A == B));
Janik Zikovsky
committed
/**
void testSwapRows() {
Matrix<double> A(3, 3);
A.swapRows(1, 2);
A.swapCols(1, 2);
TS_ASSERT_EQUALS(A[0][0], B[0][0]);
TS_ASSERT_EQUALS(A[2][2], B[1][1]);
Matrix<double> A(3, 3); // NOTE: A must be symmetric
A[0][0] = 1.0;
A[1][0] = A[0][1] = 4.0;
A[0][2] = A[2][0] = 5.0;
A[1][1] = 3.0;
A[2][1] = A[1][2] = 6.0;
A[2][2] = -7.0;
TS_ASSERT(A.Diagonalise(Eval, Diag));
Matrix<double> MA = A * Eval;
Matrix<double> MV = Eval * Diag;
TS_ASSERT(Diag[0][0] < Diag[1][1]);
TS_ASSERT(Diag[1][1] < Diag[2][2]);
TS_ASSERT(MA == MV);
X[0] = Eval[0][1];
X[1] = Eval[1][1];
X[2] = Eval[2][1];
std::vector<double> out = A * X;
transform(X.begin(), X.end(), X.begin(),
std::bind2nd(std::multiplies<double>(), Diag[1][1]));
TS_ASSERT_DELTA(X[0], out[0], 0.0001);
TS_ASSERT_DELTA(X[1], out[1], 0.0001);
TS_ASSERT_DELTA(X[2], out[2], 0.0001);
Janik Zikovsky
committed
/**
Matrix<double> A(2, 2); // symmetric only
A[0][0] = 1.0;
A[1][0] = 3.0;
A[0][1] = 3.0;
A[1][1] = 4.0;
TS_ASSERT(A.Diagonalise(Eval, Diag)); // returns 1 or 2
Eval *= Diag;
Eval *= EvalT;
TS_ASSERT(Eval == A);
void testFromVectorThrows() {
std::vector<double> data(5, 0);
TSM_ASSERT_THROWS("building matrix by this construcor and data with wrong "
"number of elements should throw",
(Matrix<double>(data)), std::invalid_argument);
void test_Transpose_On_Square_Matrix_Matches_TPrime() {
Matrix<double> A(2, 2);
A[0][0] = 1.0;
A[0][1] = 2.0;
A[1][0] = 3.0;
A[1][1] = 4.0;
auto B = A.Tprime(); // new matrix
TS_ASSERT_EQUALS(1.0, B[0][0]);
TS_ASSERT_EQUALS(3.0, B[0][1]);
TS_ASSERT_EQUALS(2.0, B[1][0]);
TS_ASSERT_EQUALS(4.0, B[1][1]);
A.Transpose(); // in place
TS_ASSERT_EQUALS(1.0, A[0][0]);
TS_ASSERT_EQUALS(3.0, A[0][1]);
TS_ASSERT_EQUALS(2.0, A[1][0]);
TS_ASSERT_EQUALS(4.0, A[1][1]);
}
void test_Transpose_On_Irregular_Matrix_Matches_TPrime() {
Matrix<double> A(2, 3);
A[0][0] = 1.0;
A[0][1] = 2.0;
A[0][2] = 3.0;
A[1][0] = 4.0;
A[1][1] = 5.0;
A[1][2] = 6.0;
auto B = A.Tprime(); // new matrix
TS_ASSERT_EQUALS(2, B.numCols());
TS_ASSERT_EQUALS(3, B.numRows());
TS_ASSERT_EQUALS(1.0, B[0][0]);
TS_ASSERT_EQUALS(4.0, B[0][1]);
TS_ASSERT_EQUALS(2.0, B[1][0]);
TS_ASSERT_EQUALS(5.0, B[1][1]);
TS_ASSERT_EQUALS(3.0, B[2][0]);
TS_ASSERT_EQUALS(6.0, B[2][1]);
}
void testFromVectorBuildCorrect() {
std::vector<int> data(9, 0);
for (int i = 0; i < 9; i++) {
data[i] = i;
}
Matrix<int> myMat;
TSM_ASSERT_THROWS_NOTHING("building matrix by this construcor and data "
"with correct number of elements should not "
"throw",
myMat = Matrix<int>(data));
// and the range of the elements in the matrix is correct;
V3D rez1 = myMat * V3D(1, 0, 0);
V3D rez2 = myMat * V3D(0, 1, 0);
V3D rez3 = myMat * V3D(0, 0, 1);
TSM_ASSERT_EQUALS("The data in a matrix have to be located row-wise, so "
"multiplication by (1,0,0)^T selects 1-st column ",
true, V3D(0, 3, 6) == rez1);
TSM_ASSERT_EQUALS("The data in a matrix have to be located row-wise, so "
"multiplication by (0,1,0)^T selects 2-nd column ",
true, V3D(1, 4, 7) == rez2);
TSM_ASSERT_EQUALS("The data in a matrix have to be located row-wise, so "
"multiplication by (0,0,1)^T selects 3-rd column ",
true, V3D(2, 5, 8) == rez3);
void testIsRotation() {
Matrix<double> d(3, 3, true);
Savici, Andrei T.
committed
TS_ASSERT(d.isRotation());
Savici, Andrei T.
committed
TS_ASSERT(!d.isRotation());
}
Savici, Andrei T.
committed
/*
|1 0 0|
|1 2 0|
|0 0 -3|
transforms to
|-s-s 0|
|-s s 0|
|0 0 -1|
with s=sqrt(0.5) and scaling (-sqrt(2),sqrt(2),3)
*/
Matrix<double> d(3, 3, true);
d[1][0] = 1.0;
d[1][1] = 2.;
d[2][2] = -3.;
std::vector<double> v = d.toRotation();
TS_ASSERT_DELTA(d[0][0], -sqrt(0.5), 1e-7);
TS_ASSERT_DELTA(d[0][1], -sqrt(0.5), 1e-7);
TS_ASSERT_DELTA(d[1][0], -sqrt(0.5), 1e-7);
TS_ASSERT_DELTA(d[1][1], sqrt(0.5), 1e-7);
TS_ASSERT_DELTA(d[2][2], -1., 1e-7);
TS_ASSERT_DELTA(v[0], -sqrt(2.), 1e-7);
TS_ASSERT_DELTA(v[1], sqrt(2.), 1e-7);
TS_ASSERT_DELTA(v[2], 3., 1e-7);
Savici, Andrei T.
committed
}
void test_Input_Stream_Throws_On_Bad_Input() {
Gigg, Martyn Anthony
committed
DblMatrix rot;
std::istringstream is;
is.str("Matr(3,3)1,2,3,4,5,6,7,8,9");
TS_ASSERT_THROWS(is >> rot, std::invalid_argument);
is.str("Matrix3,3)1,2,3,4,5,6,7,8,9");
TS_ASSERT_THROWS(is >> rot, std::invalid_argument);
is.str("Matrix(3,31,2,3,4,5,6,7,8,9");
TS_ASSERT_THROWS(is >> rot, std::invalid_argument);
}
void test_Input_Stream_On_Square_Matrix() {
Gigg, Martyn Anthony
committed
DblMatrix rot;
std::istringstream is;
is.str("Matrix(3,3)1,2,3,4,5,6,7,8,9");
TS_ASSERT_THROWS_NOTHING(is >> rot);
TS_ASSERT_EQUALS(rot.numRows(), 3);
TS_ASSERT_EQUALS(rot.numCols(), 3);
for (size_t i = 0; i < 3; ++i) {
for (size_t j = 0; j < 3; ++j) {
TS_ASSERT_EQUALS(rot[i][j],
static_cast<double>(i * rot.numRows() + j + 1));
Gigg, Martyn Anthony
committed
}
}
}
void test_Input_Stream_On_Non_Square_Matrix() {
Gigg, Martyn Anthony
committed
DblMatrix rot;
std::istringstream is;
is.str("Matrix(2,4)0,1,2,3,10,11,12,13");
TS_ASSERT_THROWS_NOTHING(is >> rot);
TS_ASSERT_EQUALS(rot.numRows(), 2);
TS_ASSERT_EQUALS(rot.numCols(), 4);
for (size_t i = 0; i < 2; ++i) {
for (size_t j = 0; j < 4; ++j) {
if (i < 1) {
TS_ASSERT_EQUALS(rot[i][j], static_cast<double>(i + j));
} else {
TS_ASSERT_EQUALS(rot[i][j], static_cast<double>(9 + i + j));
Gigg, Martyn Anthony
committed
}
}
}
}
void test_fillMatrix_With_Good_Input_Gives_Expected_Matrix() {
DblMatrix rot;
std::istringstream is;
is.str("Matrix(3|3)1|2|3|4|5|6|7|8|9");
TS_ASSERT_THROWS_NOTHING(Mantid::Kernel::fillFromStream(is, rot, '|'));
checkMatrixHasExpectedValuesForSquareMatrixTest(rot);
}
void test_fillMatrix_Accepts_Any_Delimiter_Between_Number_Rows_And_Columns() {
DblMatrix rot;
std::istringstream is;
is.str("Matrix(3@3)1|2|3|4|5|6|7|8|9");
TS_ASSERT_THROWS_NOTHING(Mantid::Kernel::fillFromStream(is, rot, '|'));
checkMatrixHasExpectedValuesForSquareMatrixTest(rot);
}
Gigg, Martyn Anthony
committed
void test_fillMatrix_With_Mixed_Delimiters_In_Input_Values_Throws() {
DblMatrix rot;
std::istringstream is;
is.str("Matrix(3|3)1|2,3|4|5|6|7|8|9");
TS_ASSERT_THROWS(Mantid::Kernel::fillFromStream(is, rot, '|'),
std::invalid_argument);
}
void test_Construction_Non_Square_Matrix_From_Output_Stream() {
DblMatrix ref(2, 3);
ref[0][0] = 5;
ref[0][1] = 10;
ref[0][2] = 15;
ref[1][0] = 105;
ref[1][1] = 110;
ref[1][2] = 115;
std::ostringstream os;
os << ref;
TS_ASSERT_EQUALS(os.str(), "Matrix(2,3)5,10,15,105,110,115");
}
void test_Construction_Square_Matrix_From_Output_Stream() {
DblMatrix square(2, 2);
square[0][0] = 2;
square[0][1] = 4;
square[1][0] = 6;
square[1][1] = 8;
std::ostringstream os;
os << square;
TS_ASSERT_EQUALS(os.str(), "Matrix(2,2)2,4,6,8");
}
void test_Dump_Matrix_To_Output_Stream_With_Custom_Delimiter() {
DblMatrix square(2, 2);
square[0][0] = 2;
square[0][1] = 4;
square[1][0] = 6;
square[1][1] = 8;
std::ostringstream os;
Mantid::Kernel::dumpToStream(os, square, '|');
TS_ASSERT_EQUALS(os.str(), "Matrix(2|2)2|4|6|8");
}
void test_lexical_cast() {
try {
DblMatrix R = boost::lexical_cast<DblMatrix>("Matrix(2,2)2,4,6,8");
TS_ASSERT_EQUALS(R.numRows(), 2);
TS_ASSERT_EQUALS(R.numCols(), 2);
TS_ASSERT_EQUALS(R[0][0], 2.0);
TS_ASSERT_EQUALS(R[0][1], 4.0);
TS_ASSERT_EQUALS(R[1][0], 6.0);
TS_ASSERT_EQUALS(R[1][1], 8.0);
} catch (boost::bad_lexical_cast &e) {
TS_FAIL(e.what());
}
}
void testMultiplicationWithVector() {
DblMatrix M = boost::lexical_cast<DblMatrix>(
"Matrix(3,3)-0.23,0.55,5.22,2.96,4.2,0.1,-1.453,3.112,-2.34");
// Results from octave.
TS_ASSERT_DELTA(nv.X(), -0.403000000000000, 1e-15);
TS_ASSERT_DELTA(nv.Y(), 25.663000000000000, 1e-15);
TS_ASSERT_DELTA(nv.Z(), 11.715100000000003, 1e-15);
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
DblMatrix M4(4, 4, true);
TS_ASSERT_THROWS(M4.operator*(v),
Mantid::Kernel::Exception::MisMatch<size_t>);
DblMatrix M23 = boost::lexical_cast<DblMatrix>(
"Matrix(2,3)-0.23,0.55,5.22,2.96,4.2,0.1");
TS_ASSERT_THROWS_NOTHING(M23.operator*(v));
nv = M23 * v;
TS_ASSERT_DELTA(nv.X(), -0.403000000000000, 1e-15);
TS_ASSERT_DELTA(nv.Y(), 25.663000000000000, 1e-15);
TS_ASSERT_EQUALS(nv.Z(), 0);
DblMatrix M43 = boost::lexical_cast<DblMatrix>(
"Matrix(4,3)-0.23,0.55,5.22,2.96,4.2,0.1,-0.23,0.55,5.22,2.96,4.2,0.1");
TS_ASSERT_THROWS(M43.operator*(v),
Mantid::Kernel::Exception::MisMatch<size_t>);
}
private:
void checkMatrixHasExpectedValuesForSquareMatrixTest(const DblMatrix &mat) {
TS_ASSERT_EQUALS(mat.numRows(), 3);
TS_ASSERT_EQUALS(mat.numCols(), 3);
for (size_t i = 0; i < 3; ++i) {
for (size_t j = 0; j < 3; ++j) {
TS_ASSERT_EQUALS(mat[i][j],
static_cast<double>(i * mat.numRows() + j + 1));
}
}
}