-
Hahn, Steven authoredHahn, Steven authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
Matrix.cpp 47.97 KiB
#include "MantidKernel/Matrix.h"
#include "MantidKernel/Exception.h"
#include "MantidKernel/make_unique.h"
#include "MantidKernel/MersenneTwister.h"
#include "MantidKernel/TimeSeriesProperty.h"
#include "MantidKernel/V3D.h"
#include <algorithm>
#include <memory>
#include <sstream>
namespace Mantid {
namespace Kernel {
//
#define fabs(x) std::fabs((x)*1.0)
namespace {
//-------------------------------------------------------------------------
// Utility methods and function objects in anonymous namespace
//-------------------------------------------------------------------------
/**
\class PIndex
\author S. Ansell
\date Aug 2005
\version 1.0
\brief Class to fill an index with a progressive count
*/
template <typename T> struct PIndex {
private:
int count; ///< counter
public:
/// Constructor
PIndex() : count(0) {}
/// functional
std::pair<T, int> operator()(const T &A) {
return std::pair<T, int>(A, count++);
}
};
/**
\class PSep
\author S. Ansell
\date Aug 2005
\version 1.0
\brief Class to access the second object in index pair.
*/
template <typename T> struct PSep {
/// Functional to the second object
int operator()(const std::pair<T, int> &A) { return A.second; }
};
/**
* Function to take a vector and sort the vector
* so as to produce an index. Leaves the vector unchanged.
* @param pVec :: Input vector
* @param Index :: Output vector
*/
template <typename T>
void indexSort(const std::vector<T> &pVec, std::vector<int> &Index) {
Index.resize(pVec.size());
std::vector<typename std::pair<T, int>> PartList;
PartList.resize(pVec.size());
transform(pVec.begin(), pVec.end(), PartList.begin(), PIndex<T>());
sort(PartList.begin(), PartList.end());
transform(PartList.begin(), PartList.end(), Index.begin(), PSep<T>());
}
template void indexSort(const std::vector<double> &, std::vector<int> &);
template void indexSort(const std::vector<float> &, std::vector<int> &);
template void indexSort(const std::vector<int> &, std::vector<int> &);
} // End of Anonymous name space
template <typename T> std::vector<T> Matrix<T>::getVector() const {
std::vector<T> rez(m_numRows * m_numColumns);
size_t ic(0);
for (size_t i = 0; i < m_numRows; i++) {
for (size_t j = 0; j < m_numColumns; j++) {
rez[ic] = m_rawData[i][j];
ic++;
}
}
return rez;
}
//
template <typename T>
Matrix<T>::Matrix(const size_t nrow, const size_t ncol, const bool makeIdentity)
: m_numRows(0), m_numColumns(0)
/**
Constructor with pre-set sizes. Matrix is zeroed
@param nrow :: number of rows
@param ncol :: number of columns
@param makeIdentity :: flag for the constructor to return an identity matrix
*/
{
// Note:: m_numRows, m_numColumns zeroed so setMem always works
setMem(nrow, ncol);
zeroMatrix();
if (makeIdentity)
identityMatrix();
}
template <typename T>
Matrix<T>::Matrix(const std::vector<T> &A, const std::vector<T> &B)
: m_numRows(0), m_numColumns(0)
/**
Constructor to take two vectors and multiply them to
construct a matrix. (assuming that we have columns x row
vector.
@param A :: Column vector to multiply
@param B :: Row vector to multiply
*/
{
// Note:: m_numRows,m_numColumns zeroed so setMem always works
setMem(A.size(), B.size());
for (size_t i = 0; i < m_numRows; i++) {
for (size_t j = 0; j < m_numColumns; j++) {
m_rawData[i][j] = A[i] * B[j];
}
}
}
//
template <typename T>
Matrix<T>::Matrix(const std::vector<T> &data)
: m_numRows(0), m_numColumns(0) {
size_t numElements = data.size();
size_t numRows = static_cast<size_t>(sqrt(double(numElements)));
size_t numRowsSquare = numRows * numRows;
if (numElements != numRowsSquare) {
throw(std::invalid_argument(
"number of elements in input vector have to be square of some value"));
}
setMem(numRows, numRows);
size_t ic(0);
for (size_t i = 0; i < m_numRows; i++) {
for (size_t j = 0; j < m_numColumns; j++) {
m_rawData[i][j] = data[ic];
ic++;
}
}
}
template <typename T>
Matrix<T>::Matrix(const std::vector<T> &data, const size_t nrow,
const size_t ncol)
: m_numRows(0), m_numColumns(0) {
size_t numel = data.size();
size_t test = nrow * ncol;
if (test != numel) {
throw(std::invalid_argument("number of elements in input vector have is "
"incompatible with the number of rows and "
"columns"));
}
setMem(nrow, ncol);
size_t ic(0);
for (size_t i = 0; i < m_numRows; i++) {
for (size_t j = 0; j < m_numColumns; j++) {
m_rawData[i][j] = data[ic];
ic++;
}
}
}
template <typename T>
Matrix<T>::Matrix(const Matrix<T> &A, const size_t nrow, const size_t ncol)
: m_numRows(A.m_numRows - 1), m_numColumns(A.m_numColumns - 1)
/**
Constructor with for a missing row/column.
@param A :: The input matrix
@param nrow :: number of row to miss
@param ncol :: number of column to miss
*/
{
if (nrow > m_numRows)
throw Kernel::Exception::IndexError(nrow, A.m_numRows,
"Matrix::Constructor without row");
if (ncol > m_numColumns)
throw Kernel::Exception::IndexError(ncol, A.m_numColumns,
"Matrix::Constructor without col");
setMem(m_numRows, m_numColumns);
size_t iR(0);
for (size_t i = 0; i <= m_numRows; i++) {
if (i != nrow) {
size_t jR(0);
for (size_t j = 0; j <= m_numColumns; j++) {
if (j != ncol) {
m_rawData[iR][jR] = A.m_rawData[i][j];
jR++;
}
}
iR++;
}
}
}
template <typename T>
Matrix<T>::Matrix(const Matrix<T> &A)
: m_numRows(0), m_numColumns(0)
/**
Simple copy constructor
@param A :: Object to copy
*/
{
// Note:: m_numRows,m_numColumns zeroed so setMem always works
setMem(A.m_numRows, A.m_numColumns);
if ((m_numRows * m_numColumns) > 0) {
for (size_t i = 0; i < m_numRows; i++) {
for (size_t j = 0; j < m_numColumns; j++) {
m_rawData[i][j] = A.m_rawData[i][j];
}
}
}
}
template <typename T>
Matrix<T> &Matrix<T>::operator=(const Matrix<T> &A)
/**
Simple assignment operator
@param A :: Object to copy
@return the copied object
*/
{
if (&A != this) {
setMem(A.m_numRows, A.m_numColumns);
if ((m_numRows * m_numColumns) > 0) {
for (size_t i = 0; i < m_numRows; i++) {
for (size_t j = 0; j < m_numColumns; j++) {
m_rawData[i][j] = A.m_rawData[i][j];
}
}
}
}
return *this;
}
template <typename T>
Matrix<T>::Matrix(Matrix<T> &&other) noexcept
: m_numRows(other.m_numRows),
m_numColumns(other.m_numColumns),
m_rawDataAlloc(std::move(other.m_rawDataAlloc)),
m_rawData(std::move(other.m_rawData)) {
other.m_numRows = 0;
other.m_numColumns = 0;
}
template <typename T>
Matrix<T> &Matrix<T>::operator=(Matrix<T> &&other) noexcept {
m_numRows = other.m_numRows;
m_numColumns = other.m_numColumns;
m_rawData = std::move(other.m_rawData);
m_rawDataAlloc = std::move(other.m_rawDataAlloc);
other.m_numRows = 0;
other.m_numColumns = 0;
return *this;
}
template <typename T>
Matrix<T> &Matrix<T>::operator+=(const Matrix<T> &A)
/**
Matrix addition THIS + A
If the size is different then 0 is added where appropiate
Matrix A is not expanded.
@param A :: Matrix to add
@return Matrix(this + A)
*/
{
const size_t Xpt((m_numRows > A.m_numRows) ? A.m_numRows : m_numRows);
const size_t Ypt((m_numColumns > A.m_numColumns) ? A.m_numColumns
: m_numColumns);
for (size_t i = 0; i < Xpt; i++) {
for (size_t j = 0; j < Ypt; j++) {
m_rawData[i][j] += A.m_rawData[i][j];
}
}
return *this;
}
template <typename T>
Matrix<T> &Matrix<T>::operator-=(const Matrix<T> &A)
/**
Matrix subtractoin THIS - A
If the size is different then 0 is added where appropiate
Matrix A is not expanded.
@param A :: Matrix to add
@return Ma
*/
{
const size_t Xpt((m_numRows > A.m_numRows) ? A.m_numRows : m_numRows);
const size_t Ypt((m_numColumns > A.m_numColumns) ? A.m_numColumns
: m_numColumns);
for (size_t i = 0; i < Xpt; i++) {
for (size_t j = 0; j < Ypt; j++) {
m_rawData[i][j] -= A.m_rawData[i][j];
}
}
return *this;
}
template <typename T>
Matrix<T> Matrix<T>::operator+(const Matrix<T> &A) const
/**
Matrix addition THIS + A
If the size is different then 0 is added where appropiate
Matrix A is not expanded.
@param A :: Matrix to add
@return Matrix(this + A)
*/
{
Matrix<T> X(*this);
return X += A;
}
template <typename T>
Matrix<T> Matrix<T>::operator-(const Matrix<T> &A) const
/**
Matrix subtraction THIS - A
If the size is different then 0 is subtracted where
appropiate. This matrix determines the size
@param A :: Matrix to add
@return Matrix(this + A)
*/
{
Matrix<T> X(*this);
return X -= A;
}
template <typename T>
Matrix<T> Matrix<T>::operator*(const Matrix<T> &A) const
/**
Matrix multiplication THIS * A
@param A :: Matrix to multiply by (this->row must == A->columns)
@throw MisMatch<size_t> if there is a size mismatch.
@return Matrix(This * A)
*/
{
if (m_numColumns != A.m_numRows)
throw Kernel::Exception::MisMatch<size_t>(m_numColumns, A.m_numRows,
"Matrix::operator*(Matrix)");
Matrix<T> X(m_numRows, A.m_numColumns);
for (size_t i = 0; i < m_numRows; i++) {
for (size_t j = 0; j < A.m_numColumns; j++) {
for (size_t kk = 0; kk < m_numColumns; kk++) {
X.m_rawData[i][j] += m_rawData[i][kk] * A.m_rawData[kk][j];
}
}
}
return X;
}
template <typename T>
std::vector<T> Matrix<T>::operator*(const std::vector<T> &Vec) const
/**
Matrix multiplication THIS * Vec to produce a vec
@param Vec :: size of vector > this->nrows
@throw MisMatch<size_t> if there is a size mismatch.
@return Matrix(This * Vec)
*/
{
if (m_numColumns > Vec.size())
throw Kernel::Exception::MisMatch<size_t>(m_numColumns, Vec.size(),
"Matrix::operator*(m_rawDataec)");
std::vector<T> Out(m_numRows);
for (size_t i = 0; i < m_numRows; i++) {
for (size_t j = 0; j < m_numColumns; j++) {
Out[i] += m_rawData[i][j] * Vec[j];
}
}
return Out;
}
/**
Matrix multiplication THIS * Vec to produce a vec
@param in :: size of vector > this->nrows
@param out :: result of Matrix(This * Vec)
@throw MisMatch<size_t> if there is a size mismatch.
*/
template <typename T>
void Matrix<T>::multiplyPoint(const std::vector<T> &in,
std::vector<T> &out) const {
out.resize(m_numRows);
std::fill(std::begin(out), std::end(out), static_cast<T>(0.0));
if (m_numColumns > in.size())
throw Kernel::Exception::MisMatch<size_t>(m_numColumns, in.size(),
"Matrix::multiplyPoint(in,out)");
for (size_t i = 0; i < m_numRows; i++) {
for (size_t j = 0; j < m_numColumns; j++) {
out[i] += m_rawData[i][j] * in[j];
}
}
}
template <typename T>
V3D Matrix<T>::operator*(const V3D &Vx) const
/**
Matrix multiplication THIS * V
@param Vx :: Colunm vector to multiply by
@throw MisMatch<size_t> if there is a size mismatch.
@return Matrix(This * A)
*/
{
if (m_numColumns != 3 || m_numRows > 3)
throw Kernel::Exception::MisMatch<size_t>(m_numColumns, 3,
"Matrix::operator*(m_rawData3D)");
V3D v;
for (size_t i = 0; i < m_numRows; ++i) {
v[i] = m_rawData[i][0] * Vx.X() + m_rawData[i][1] * Vx.Y() +
m_rawData[i][2] * Vx.Z();
}
return v;
}
template <typename T>
Matrix<T> Matrix<T>::operator*(const T &Value) const
/**
Matrix multiplication THIS * Value
@param Value :: Scalar to multiply by
@return V * (this)
*/
{
Matrix<T> X(*this);
for (size_t i = 0; i < m_numRows; i++) {
for (size_t j = 0; j < m_numColumns; j++) {
X.m_rawData[i][j] *= Value;
}
}
return X;
}
/**
Matrix multiplication THIS *= A
Note that we call operator* to avoid the problem
of changing matrix size.
@param A :: Matrix to multiply by (this->row must == A->columns)
@return This *= A
*/
template <typename T> Matrix<T> &Matrix<T>::operator*=(const Matrix<T> &A) {
if (m_numColumns != A.m_numRows)
throw Kernel::Exception::MisMatch<size_t>(m_numColumns, A.m_numRows,
"Matrix*=(Matrix<T>)");
// This construct to avoid the problem of changing size
*this = this->operator*(A);
return *this;
}
template <typename T>
Matrix<T> &Matrix<T>::operator*=(const T &Value)
/**
Matrix multiplication THIS * Value
@param Value :: Scalar to multiply matrix by
@return *this
*/
{
for (size_t i = 0; i < m_numRows; i++) {
for (size_t j = 0; j < m_numColumns; j++) {
m_rawData[i][j] *= Value;
}
}
return *this;
}
template <typename T>
Matrix<T> &Matrix<T>::operator/=(const T &Value)
/**
Matrix divishio THIS / Value
@param Value :: Scalar to multiply matrix by
@return *this
*/
{
for (size_t i = 0; i < m_numRows; i++) {
for (size_t j = 0; j < m_numColumns; j++) {
m_rawData[i][j] /= Value;
}
}
return *this;
}
template <typename T>
bool Matrix<T>::operator!=(const Matrix<T> &A) const
/**
Element by Element comparison
@param A :: Matrix to check
@return true :: on succees
@return false :: failure
*/
{
return !(this->operator==(A));
}
template <typename T>
bool Matrix<T>::operator==(const Matrix<T> &A) const
/**
Element by element comparison within tolerance.
Tolerance means that the value must be > tolerance
and less than (diff/max)>tolerance
Always returns 0 if the Matrix have different sizes
@param A :: matrix to check.
@return true on success
*/
{
return this->equals(A, 1e-8);
}
//---------------------------------------------------------------------------------------
template <typename T>
bool Matrix<T>::equals(const Matrix<T> &A, const double Tolerance) const
/**
Element by element comparison within tolerance.
Tolerance means that the value must be > tolerance
and less than (diff/max)>tolerance
Always returns 0 if the Matrix have different sizes
@param A :: matrix to check.
@param Tolerance :: tolerance in comparing elements
@return true on success
*/
{
if (&A != this) // this == A == always true
{
if (A.m_numRows != m_numRows || A.m_numColumns != m_numColumns)
return false;
double maxS(0.0);
double maxDiff(0.0); // max di
for (size_t i = 0; i < m_numRows; i++)
for (size_t j = 0; j < m_numColumns; j++) {
const T diff = (m_rawData[i][j] - A.m_rawData[i][j]);
if (fabs(diff) > maxDiff)
maxDiff = fabs(diff);
if (fabs(m_rawData[i][j]) > maxS)
maxS = fabs(m_rawData[i][j]);
}
if (maxDiff < Tolerance)
return true;
if (maxS > 1.0 && (maxDiff / maxS) < Tolerance)
return true;
return false;
}
// this == this is true
return true;
}
//---------------------------------------------------------------------------------------
/** Element by element comparison of <
Always returns false if the Matrix have different sizes
@param A :: matrix to check.
@return true if this < A
*/
template <typename T> bool Matrix<T>::operator<(const Matrix<T> &A) const {
if (&A == this) // this < A == always false
return false;
if (A.m_numRows != m_numRows || A.m_numColumns != m_numColumns)
return false;
for (size_t i = 0; i < m_numRows; i++)
for (size_t j = 0; j < m_numColumns; j++) {
if (m_rawData[i][j] >= A.m_rawData[i][j])
return false;
}
return true;
}
//---------------------------------------------------------------------------------------
/** Element by element comparison of >=
Always returns false if the Matrix have different sizes
@param A :: matrix to check.
@return true if this >= A
*/
template <typename T> bool Matrix<T>::operator>=(const Matrix<T> &A) const {
if (&A == this)
return true;
if (A.m_numRows != m_numRows || A.m_numColumns != m_numColumns)
return false;
for (size_t i = 0; i < m_numRows; i++)
for (size_t j = 0; j < m_numColumns; j++) {
if (m_rawData[i][j] < A.m_rawData[i][j])
return false;
}
return true;
}
/**
Sets the memory held in matrix
@param a :: number of rows
@param b :: number of columns
*/
template <typename T> void Matrix<T>::setMem(const size_t a, const size_t b) {
if (a == m_numRows && b == m_numColumns && m_rawData != nullptr)
return;
if (a <= 0 || b <= 0)
return;
m_numRows = a;
m_numColumns = b;
// Allocate memory first - this has to be a flat 1d array masquerading as a 2d
// array so we can expose the memory to Python APIs via numpy which expects
// this
// style of memory layout.
auto allocatedMemory = Kernel::make_unique<T[]>((m_numRows * m_numColumns));
// Next allocate an array of pointers for the rows (X). This partitions
// the 1D array into a 2D array for callers.
auto rowPtrs = Kernel::make_unique<T *[]>(m_numRows);
for (size_t i = 0; i < m_numRows; i++) {
// Calculate offsets into the allocated memory array (Y)
rowPtrs[i] = &allocatedMemory[i * m_numColumns];
}
m_rawDataAlloc = std::move(allocatedMemory);
m_rawData = std::move(rowPtrs);
}
/**
Swap rows I and J
@param RowI :: row I to swap
@param RowJ :: row J to swap
*/
template <typename T>
void Matrix<T>::swapRows(const size_t RowI, const size_t RowJ) {
if ((m_numRows * m_numColumns > 0) && RowI < m_numRows && RowJ < m_numRows &&
RowI != RowJ) {
for (size_t k = 0; k < m_numColumns; k++) {
T tmp = m_rawData[RowI][k];
m_rawData[RowI][k] = m_rawData[RowJ][k];
m_rawData[RowJ][k] = tmp;
}
}
}
/**
Swap columns I and J
@param colI :: col I to swap
@param colJ :: col J to swap
*/
template <typename T>
void Matrix<T>::swapCols(const size_t colI, const size_t colJ) {
if ((m_numRows * m_numColumns) > 0 && colI < m_numColumns &&
colJ < m_numColumns && colI != colJ) {
for (size_t k = 0; k < m_numRows; k++) {
T tmp = m_rawData[k][colI];
m_rawData[k][colI] = m_rawData[k][colJ];
m_rawData[k][colJ] = tmp;
}
}
}
template <typename T>
void Matrix<T>::zeroMatrix()
/**
Zeros all elements of the matrix
*/
{
if ((m_numRows * m_numColumns) > 0) {
for (size_t i = 0; i < m_numRows; i++) {
for (size_t j = 0; j < m_numColumns; j++) {
m_rawData[i][j] = static_cast<T>(0);
}
}
}
}
template <typename T>
void Matrix<T>::identityMatrix()
/**
Makes the matrix an idenity matrix.
Zeros all the terms outside of the square
*/
{
if ((m_numRows * m_numColumns) > 0) {
for (size_t i = 0; i < m_numRows; i++) {
for (size_t j = 0; j < m_numColumns; j++) {
m_rawData[i][j] = static_cast<T>(j == i);
}
}
}
}
template <typename T>
void Matrix<T>::setColumn(const size_t nCol, const std::vector<T> &newCol) {
if (nCol >= this->m_numColumns) {
throw(std::invalid_argument("nCol requested> nCol availible"));
}
size_t m_numRowsM = newCol.size();
if (m_numRows < m_numRowsM)
m_numRowsM = m_numRows;
for (size_t i = 0; i < m_numRowsM; i++) {
m_rawData[i][nCol] = newCol[i];
}
}
template <typename T>
void Matrix<T>::setRow(const size_t nRow, const std::vector<T> &newRow) {
if (nRow >= this->m_numRows) {
throw(std::invalid_argument("nRow requested> nRow availible"));
}
size_t m_numColumnsM = newRow.size();
if (m_numColumns < m_numColumnsM)
m_numColumnsM = m_numColumns;
for (size_t j = 0; j < m_numColumnsM; j++) {
m_rawData[nRow][j] = newRow[j];
}
}
template <typename T>
void Matrix<T>::rotate(const double tau, const double s, const int i,
const int j, const int k, const int m)
/**
Applies a rotation to a particular point of tan(theta)=tau.
Note that you need both sin(theta) and tan(theta) because of
sign preservation.
@param tau :: tan(theta)
@param s :: sin(theta)
@param i :: first index (xpos)
@param j :: first index (ypos)
@param k :: second index (xpos)
@param m :: second index (ypos)
*/
{
const T gg = m_rawData[i][j];
const T hh = m_rawData[k][m];
m_rawData[i][j] = static_cast<T>(gg - s * (hh + gg * tau));
m_rawData[k][m] = static_cast<T>(hh + s * (gg - hh * tau));
}
template <typename T>
Matrix<T> Matrix<T>::preMultiplyByDiagonal(const std::vector<T> &Dvec) const
/**
Creates a diagonal matrix D from the given vector Dvec and
PRE-multiplies the matrix by it (i.e. D * M).
@param Dvec :: diagonal matrix (just centre points)
@return D*this
*/
{
if (Dvec.size() != m_numRows) {
std::ostringstream cx;
cx << "Matrix::preMultiplyByDiagonal Size: " << Dvec.size() << " "
<< m_numRows << " " << m_numColumns;
throw std::runtime_error(cx.str());
}
Matrix<T> X(Dvec.size(), m_numColumns);
for (size_t i = 0; i < Dvec.size(); i++) {
for (size_t j = 0; j < m_numColumns; j++) {
X.m_rawData[i][j] = Dvec[i] * m_rawData[i][j];
}
}
return X;
}
template <typename T>
Matrix<T> Matrix<T>::postMultiplyByDiagonal(const std::vector<T> &Dvec) const
/**
Creates a diagonal matrix D from the given vector Dvec and
POST-multiplies the matrix by it (i.e. M * D).
@param Dvec :: diagonal matrix (just centre points)
@return this*D
*/
{
if (Dvec.size() != m_numColumns) {
std::ostringstream cx;
cx << "Error Matrix::bDiaognal size:: " << Dvec.size() << " " << m_numRows
<< " " << m_numColumns;
throw std::runtime_error(cx.str());
}
Matrix<T> X(m_numRows, Dvec.size());
for (size_t i = 0; i < m_numRows; i++) {
for (size_t j = 0; j < Dvec.size(); j++) {
X.m_rawData[i][j] = Dvec[j] * m_rawData[i][j];
}
}
return X;
}
template <typename T>
Matrix<T> Matrix<T>::Tprime() const
/**
Transpose the matrix :
Has transpose for a square matrix case.
@return M^T
*/
{
if ((m_numRows * m_numColumns) == 0)
return *this;
if (m_numRows == m_numColumns) // inplace transpose
{
Matrix<T> MT(*this);
MT.Transpose();
return MT;
}
// irregular matrix
Matrix<T> MT(m_numColumns, m_numRows);
for (size_t i = 0; i < m_numRows; i++)
for (size_t j = 0; j < m_numColumns; j++)
MT.m_rawData[j][i] = m_rawData[i][j];
return MT;
}
template <typename T>
Matrix<T> &Matrix<T>::Transpose()
/**
Transpose the matrix :
Has a in place transpose for a square matrix case.
@return this^T
*/
{
if ((m_numRows * m_numColumns) == 0)
return *this;
if (m_numRows == m_numColumns) // in place transpose
{
for (size_t i = 0; i < m_numRows; i++) {
for (size_t j = i + 1; j < m_numColumns; j++) {
std::swap(m_rawData[i][j], m_rawData[j][i]);
}
}
return *this;
}
// irregular matrix
// get some memory
auto allocatedMemory = Kernel::make_unique<T[]>((m_numRows * m_numColumns));
auto transposePtrs = Kernel::make_unique<T *[]>(m_numRows);
for (size_t i = 0; i < m_numColumns; i++) {
// Notice how this partitions using Rows (X) instead of Cols(Y)
transposePtrs[i] = &allocatedMemory[i * m_numRows];
}
for (size_t i = 0; i < m_numRows; i++) {
for (size_t j = 0; j < m_numColumns; j++) {
transposePtrs[j][i] = m_rawData[i][j];
}
}
// remove old memory
std::swap(m_numRows, m_numColumns);
m_rawDataAlloc = std::move(allocatedMemory);
m_rawData = std::move(transposePtrs);
return *this;
}
template <>
void Matrix<int>::GaussJordan(Kernel::Matrix<int> &)
/**
Not valid for Integer
@throw std::invalid_argument
*/
{
throw std::invalid_argument(
"Gauss-Jordan inversion not valid for integer matrix");
}
template <typename T>
void Matrix<T>::GaussJordan(Matrix<T> &B)
/**
Invert this matrix in place using Gauss-Jordan elimination.
Matrix will be replaced by its inverse.
@param B :: [input, output] Must have same dimensions as A. Returned as
identity matrix. (?)
@throw std::invalid_argument on input error
@throw std::runtime_error if singular
*/
{
// check for input errors
if (m_numRows != m_numColumns || B.m_numRows != m_numRows) {
throw std::invalid_argument("Matrix not square, or sizes do not match");
}
// pivoted rows
std::vector<int> pivoted(m_numRows);
fill(pivoted.begin(), pivoted.end(), 0);
std::vector<int> indxcol(m_numRows); // Column index
std::vector<int> indxrow(m_numRows); // row index
size_t irow(0), icol(0);
for (size_t i = 0; i < m_numRows; i++) {
// Get Biggest non-pivoted item
double bigItem = 0.0; // get point to pivot over
for (size_t j = 0; j < m_numRows; j++) {
if (pivoted[j] != 1) // check only non-pivots
{
for (size_t k = 0; k < m_numRows; k++) {
if (!pivoted[k]) {
if (fabs(m_rawData[j][k]) >= bigItem) {
bigItem = fabs(m_rawData[j][k]);
irow = j;
icol = k;
}
}
}
} else if (pivoted[j] > 1) {
throw std::runtime_error("Error doing G-J elem on a singular matrix");
}
}
pivoted[icol]++;
// Swap in row/col to make a diagonal item
if (irow != icol) {
swapRows(irow, icol);
B.swapRows(irow, icol);
}
indxrow[i] = static_cast<int>(irow);
indxcol[i] = static_cast<int>(icol);
if (m_rawData[icol][icol] == 0.0) {
throw std::runtime_error("Error doing G-J elem on a singular matrix");
}
const T pivDiv = T(1.0) / m_rawData[icol][icol];
m_rawData[icol][icol] = 1;
for (size_t l = 0; l < m_numRows; l++) {
m_rawData[icol][l] *= pivDiv;
}
for (size_t l = 0; l < B.m_numColumns; l++) {
B.m_rawData[icol][l] *= pivDiv;
}
for (size_t ll = 0; ll < m_numRows; ll++) {
if (ll != icol) {
const T div_num = m_rawData[ll][icol];
m_rawData[ll][icol] = 0.0;
for (size_t l = 0; l < m_numRows; l++) {
m_rawData[ll][l] -= m_rawData[icol][l] * div_num;
}
for (size_t l = 0; l < B.m_numColumns; l++) {
B.m_rawData[ll][l] -= B.m_rawData[icol][l] * div_num;
}
}
}
}
// Un-roll interchanges
if (m_numRows > 0) {
for (int l = static_cast<int>(m_numRows) - 1; l >= 0; l--) {
if (indxrow[l] != indxcol[l]) {
swapCols(indxrow[l], indxcol[l]);
}
}
}
}
template <typename T>
T Matrix<T>::Invert()
/**
If the Matrix is square then invert the matrix
using LU decomposition
@return Determinant (0 if the matrix is singular)
*/
{
if (m_numRows != m_numColumns && m_numRows < 1)
return 0;
if (m_numRows == 1) {
T det = m_rawData[0][0];
if (m_rawData[0][0] != static_cast<T>(0.))
m_rawData[0][0] = static_cast<T>(1.) / m_rawData[0][0];
return det;
}
std::vector<int> indx(m_numRows); // Set in lubcmp
std::vector<double> col(m_numRows);
int determinant = 0;
Matrix<T> Lcomp(*this);
Lcomp.lubcmp(indx.data(), determinant);
double det = static_cast<double>(determinant);
for (size_t j = 0; j < m_numRows; j++)
det *= Lcomp.m_rawData[j][j];
for (size_t j = 0; j < m_numRows; j++) {
for (size_t i = 0; i < m_numRows; i++)
col[i] = 0.0;
col[j] = 1.0;
Lcomp.lubksb(indx.data(), col.data());
for (size_t i = 0; i < m_numRows; i++)
m_rawData[i][j] = static_cast<T>(col[i]);
}
return static_cast<T>(det);
}
template <typename T>
T Matrix<T>::determinant() const
/**
Calculate the derminant of the matrix
@return Determinant of matrix.
*/
{
if (m_numRows != m_numColumns)
throw Kernel::Exception::MisMatch<size_t>(
m_numRows, m_numColumns, "Determinant error :: Matrix is not square");
Matrix<T> Mt(*this); // temp copy
T D = Mt.factor();
return D;
}
template <typename T>
T Matrix<T>::factor()
/**
Gauss jordan diagonal factorisation
The diagonal is left as the values,
the lower part is zero.
@return the factored matrix
*/
{
if (m_numRows != m_numColumns || m_numRows < 1)
throw std::runtime_error("Matrix::factor Matrix is not square");
double deter = 1.0;
for (int i = 0; i < static_cast<int>(m_numRows) - 1;
i++) // loop over each row
{
int jmax = i;
double Pmax = fabs(m_rawData[i][i]);
for (int j = i + 1; j < static_cast<int>(m_numRows);
j++) // find max in Row i
{
if (fabs(m_rawData[i][j]) > Pmax) {
Pmax = fabs(m_rawData[i][j]);
jmax = j;
}
}
if (Pmax < 1e-8) // maxtrix signular
{
// std::cerr<<"Matrix Singular"<<'\n';
return 0;
}
// Swap Columns
if (i != jmax) {
swapCols(i, jmax);
deter *= -1; // change sign.
}
// zero all rows below diagonal
Pmax = m_rawData[i][i];
deter *= Pmax;
for (int k = i + 1; k < static_cast<int>(m_numRows); k++) // row index
{
const double scale = m_rawData[k][i] / Pmax;
m_rawData[k][i] = static_cast<T>(0);
for (int q = i + 1; q < static_cast<int>(m_numRows); q++) // column index
m_rawData[k][q] -= static_cast<T>(scale * m_rawData[i][q]);
}
}
deter *= m_rawData[m_numRows - 1][m_numRows - 1];
return static_cast<T>(deter);
}
template <typename T>
void Matrix<T>::normVert()
/**
Normalise EigenVectors
Assumes that they have already been calculated
*/
{
for (size_t i = 0; i < m_numRows; i++) {
T sum = 0;
for (size_t j = 0; j < m_numColumns; j++) {
sum += m_rawData[i][j] * m_rawData[i][j];
}
sum = static_cast<T>(std::sqrt(static_cast<double>(sum)));
for (size_t j = 0; j < m_numColumns; j++) {
m_rawData[i][j] /= sum;
}
}
}
template <typename T>
T Matrix<T>::compSum() const
/**
Add up each component sums for the matrix
@return \f$ \sum_i \sum_j V_{ij}^2 \f$
*/
{
T sum(0);
for (size_t i = 0; i < m_numRows; i++) {
for (size_t j = 0; j < m_numColumns; j++) {
sum += m_rawData[i][j] * m_rawData[i][j];
}
}
return sum;
}
template <typename T>
void Matrix<T>::lubcmp(int *rowperm, int &interchange)
/**
Find biggest pivot and move to top row. Then
divide by pivot.
@param interchange :: odd/even nterchange (+/-1)
@param rowperm :: row permutations [m_numRows values]
*/
{
double sum, dum, big, temp;
if (m_numRows != m_numColumns || m_numRows < 2) {
std::cerr << "Error with lubcmp\n";
return;
}
std::vector<double> result(m_numRows);
interchange = 1;
for (int i = 0; i < static_cast<int>(m_numRows); i++) {
big = 0.0;
for (int j = 0; j < static_cast<int>(m_numRows); j++)
if ((temp = fabs(m_rawData[i][j])) > big)
big = temp;
if (big == 0.0) {
for (int j = 0; j < static_cast<int>(m_numRows); j++) {
rowperm[j] = j;
}
return;
}
result[i] = 1.0 / big;
}
for (int j = 0; j < static_cast<int>(m_numRows); j++) {
for (int i = 0; i < j; i++) {
sum = m_rawData[i][j];
for (int k = 0; k < i; k++)
sum -= m_rawData[i][k] * m_rawData[k][j];
m_rawData[i][j] = static_cast<T>(sum);
}
big = 0.0;
int imax = j;
for (int i = j; i < static_cast<int>(m_numRows); i++) {
sum = m_rawData[i][j];
for (int k = 0; k < j; k++)
sum -= m_rawData[i][k] * m_rawData[k][j];
m_rawData[i][j] = static_cast<T>(sum);
if ((dum = result[i] * fabs(sum)) >= big) {
big = dum;
imax = i;
}
}
if (j != imax) {
for (int k = 0; k < static_cast<int>(m_numRows);
k++) { // Interchange rows
dum = m_rawData[imax][k];
m_rawData[imax][k] = m_rawData[j][k];
m_rawData[j][k] = static_cast<T>(dum);
}
interchange *= -1;
result[imax] = static_cast<T>(result[j]);
}
rowperm[j] = imax;
if (m_rawData[j][j] == 0.0)
m_rawData[j][j] = static_cast<T>(1e-14);
if (j != static_cast<int>(m_numRows) - 1) {
dum = 1.0 / (m_rawData[j][j]);
for (int i = j + 1; i < static_cast<int>(m_numRows); i++)
m_rawData[i][j] *= static_cast<T>(dum);
}
}
}
template <typename T>
void Matrix<T>::lubksb(const int *rowperm, double *b)
/**
Implements a separation of the Matrix
into a triangular matrix
*/
{
int ii = -1;
for (int i = 0; i < static_cast<int>(m_numRows); i++) {
int ip = rowperm[i];
double sum = b[ip];
b[ip] = b[i];
if (ii != -1)
for (int j = ii; j < i; j++)
sum -= m_rawData[i][j] * b[j];
else if (sum != 0.)
ii = i;
b[i] = sum;
}
for (int i = static_cast<int>(m_numRows) - 1; i >= 0; i--) {
double sum = static_cast<T>(b[i]);
for (int j = i + 1; j < static_cast<int>(m_numRows); j++)
sum -= m_rawData[i][j] * b[j];
b[i] = sum / m_rawData[i][i];
}
}
template <typename T>
void Matrix<T>::averSymmetric()
/**
Simple function to create an average symmetric matrix
out of the Matrix
*/
{
const size_t minSize = (m_numRows > m_numColumns) ? m_numColumns : m_numRows;
for (size_t i = 0; i < minSize; i++) {
for (size_t j = i + 1; j < minSize; j++) {
m_rawData[i][j] = (m_rawData[i][j] + m_rawData[j][i]) / 2;
m_rawData[j][i] = m_rawData[i][j];
}
}
}
template <typename T>
std::vector<T> Matrix<T>::Diagonal() const
/**
Returns the diagonal form as a vector
@return Diagonal elements
*/
{
const size_t Msize = (m_numColumns > m_numRows) ? m_numRows : m_numColumns;
std::vector<T> Diag(Msize);
for (size_t i = 0; i < Msize; i++) {
Diag[i] = m_rawData[i][i];
}
return Diag;
}
template <typename T>
T Matrix<T>::Trace() const
/**
Calculates the trace of the matrix
@return Trace of matrix
*/
{
const size_t Msize = (m_numColumns > m_numRows) ? m_numRows : m_numColumns;
T Trx = 0;
for (size_t i = 0; i < Msize; i++) {
Trx += m_rawData[i][i];
}
return Trx;
}
template <typename T>
void Matrix<T>::sortEigen(Matrix<T> &DiagMatrix)
/**
Sorts the eigenvalues into increasing
size. Moves the EigenVectors correspondingly
@param DiagMatrix :: matrix of the EigenValues
*/
{
if (m_numColumns != m_numRows || m_numRows != DiagMatrix.m_numRows ||
m_numRows != DiagMatrix.m_numColumns) {
std::cerr << "Matrix not Eigen Form\n";
throw(std::invalid_argument(" Matrix is not in an eigenvalue format"));
}
std::vector<int> index;
std::vector<T> X = DiagMatrix.Diagonal();
indexSort(X, index);
Matrix<T> EigenVec(*this);
for (size_t Icol = 0; Icol < m_numRows; Icol++) {
for (size_t j = 0; j < m_numRows; j++) {
m_rawData[j][Icol] = EigenVec[j][index[Icol]];
}
DiagMatrix[Icol][Icol] = X[index[Icol]];
}
}
template <typename T>
int Matrix<T>::Diagonalise(Matrix<T> &EigenVec, Matrix<T> &DiagMatrix) const
/**
Attempt to diagonalise the matrix IF symmetric
@param EigenVec :: (output) the Eigenvectors matrix
@param DiagMatrix :: the diagonal matrix of eigenvalues
@return :: 1 on success 0 on failure
*/
{
if (m_numRows != m_numColumns || m_numRows < 1) {
std::cerr << "Matrix not square\n";
return 0;
}
for (size_t i = 0; i < m_numRows; i++)
for (size_t j = i + 1; j < m_numRows; j++)
if (fabs(m_rawData[i][j] - m_rawData[j][i]) > 1e-6) {
std::cerr << "Matrix not symmetric\n";
std::cerr << (*this);
return 0;
}
Matrix<T> A(*this);
// Make V an identity matrix
EigenVec.setMem(m_numRows, m_numRows);
EigenVec.identityMatrix();
DiagMatrix.setMem(m_numRows, m_numRows);
DiagMatrix.zeroMatrix();
std::vector<double> Diag(m_numRows);
std::vector<double> B(m_numRows);
std::vector<double> ZeroComp(m_numRows);
// set b and d to the diagonal elements o A
for (size_t i = 0; i < m_numRows; i++) {
Diag[i] = B[i] = A.m_rawData[i][i];
ZeroComp[i] = 0;
}
int iteration = 0;
for (int i = 0; i < 100; i++) // max 50 iterations
{
double sm = 0.0; // sum of off-diagonal terms
for (size_t ip = 0; ip < m_numRows - 1; ip++)
for (size_t iq = ip + 1; iq < m_numRows; iq++)
sm += fabs(A.m_rawData[ip][iq]);
if (sm == 0.0) // Nothing to do return...
{
// Make OUTPUT -- D + A
// sort Output::
for (size_t ix = 0; ix < m_numRows; ix++)
DiagMatrix.m_rawData[ix][ix] = static_cast<T>(Diag[ix]);
return 1;
}
// Threshold large for first 5 sweeps
double tresh =
(i < 6) ? 0.2 * sm / static_cast<int>(m_numRows * m_numRows) : 0.0;
for (int ip = 0; ip < static_cast<int>(m_numRows) - 1; ip++) {
for (int iq = ip + 1; iq < static_cast<int>(m_numRows); iq++) {
double g = 100.0 * fabs(A.m_rawData[ip][iq]);
// After 4 sweeps skip if off diagonal small
if (i > 6 &&
static_cast<float>(fabs(Diag[ip] + g)) ==
static_cast<float>(fabs(Diag[ip])) &&
static_cast<float>(fabs(Diag[iq] + g)) ==
static_cast<float>(fabs(Diag[iq])))
A.m_rawData[ip][iq] = 0;
else if (fabs(A.m_rawData[ip][iq]) > tresh) {
double tanAngle, cosAngle, sinAngle;
double h = Diag[iq] - Diag[ip];
if (static_cast<float>((fabs(h) + g)) == static_cast<float>(fabs(h)))
tanAngle = A.m_rawData[ip][iq] / h; // tanAngle=1/(2theta)
else {
double theta = 0.5 * h / A.m_rawData[ip][iq];
tanAngle = 1.0 / (fabs(theta) + sqrt(1.0 + theta * theta));
if (theta < 0.0)
tanAngle = -tanAngle;
}
cosAngle = 1.0 / sqrt(1 + tanAngle * tanAngle);
sinAngle = tanAngle * cosAngle;
double tau = sinAngle / (1.0 + cosAngle);
h = tanAngle * A.m_rawData[ip][iq];
ZeroComp[ip] -= h;
ZeroComp[iq] += h;
Diag[ip] -= h;
Diag[iq] += h;
A.m_rawData[ip][iq] = 0;
// Rotations 0<j<p
for (int j = 0; j < ip; j++)
A.rotate(tau, sinAngle, j, ip, j, iq);
for (int j = ip + 1; j < iq; j++)
A.rotate(tau, sinAngle, ip, j, j, iq);
for (int j = iq + 1; j < static_cast<int>(m_numRows); j++)
A.rotate(tau, sinAngle, ip, j, iq, j);
for (int j = 0; j < static_cast<int>(m_numRows); j++)
EigenVec.rotate(tau, sinAngle, j, ip, j, iq);
iteration++;
}
}
}
for (size_t j = 0; j < m_numRows; j++) {
B[j] += ZeroComp[j];
Diag[j] = B[j];
ZeroComp[j] = 0.0;
}
}
std::cerr << "Error :: Iterations are a problem\n";
return 0;
}
template <typename T>
bool Matrix<T>::isRotation() const
/** Check if a matrix represents a proper rotation
@ return :: true/false
*/
{
if (this->m_numRows != this->m_numColumns)
throw(std::invalid_argument("matrix is not square"));
// std::cout << "Matrix determinant-1 is " << (this->determinant()-1) <<
// '\n';
if (fabs(this->determinant() - 1) > 1e-5) {
return false;
} else {
Matrix<T> prod(m_numRows, m_numColumns),
ident(m_numRows, m_numColumns, true);
prod = this->operator*(this->Tprime());
// std::cout << "Matrix * Matrix' = " << std::endl << prod << '\n';
return prod.equals(ident, 1e-5);
}
}
template <typename T>
bool Matrix<T>::isOrthogonal() const
/** Check if a matrix is orthogonal. Same as isRotation, but allows determinant
to be -1
@ return :: true/false
*/
{
if (this->m_numRows != this->m_numColumns)
throw(std::invalid_argument("matrix is not square"));
if (fabs(fabs(this->determinant()) - 1.) > 1e-5) {
return false;
} else {
Matrix<T> prod(m_numRows, m_numColumns),
ident(m_numRows, m_numColumns, true);
prod = this->operator*(this->Tprime());
return prod.equals(ident, 1e-7);
}
}
template <typename T>
std::vector<T> Matrix<T>::toRotation()
/**
Transform the matrix to a rotation matrix, by normalizing each column to 1
@return :: a vector of scaling factors
@throw :: std::invalid_argument if the absolute value of the determinant is
less then 1e-10 or not square matrix
*/
{
if (this->m_numRows != this->m_numColumns)
throw(std::invalid_argument("matrix is not square"));
if (fabs(this->determinant()) < 1e-10)
throw(std::invalid_argument("Determinant is too small"));
// step 1: orthogonalize the matrix
for (size_t i = 0; i < this->m_numColumns; ++i) {
double spself = 0.;
for (size_t j = 0; j < this->m_numRows; ++j)
spself += (m_rawData[j][i] * m_rawData[j][i]);
for (size_t k = i + 1; k < this->m_numColumns; ++k) {
double spother = 0;
for (size_t j = 0; j < this->m_numRows; ++j)
spother += (m_rawData[j][i] * m_rawData[j][k]);
for (size_t j = 0; j < this->m_numRows; ++j)
m_rawData[j][k] -= static_cast<T>(m_rawData[j][i] * spother / spself);
}
}
// step 2: get scales and rescsale the matrix
std::vector<T> scale(this->m_numRows);
for (size_t i = 0; i < this->m_numColumns; ++i) {
T currentScale{0};
for (size_t j = 0; j < this->m_numRows; ++j)
currentScale += (m_rawData[j][i] * m_rawData[j][i]);
currentScale = static_cast<T>(sqrt(static_cast<double>(currentScale)));
if (currentScale < 1e-10)
throw(std::invalid_argument("Scale is too small"));
scale[i] = currentScale;
}
Matrix<T> scalingMatrix(m_numRows, m_numColumns),
change(m_numRows, m_numColumns, true);
for (size_t i = 0; i < this->m_numColumns; ++i)
scalingMatrix[i][i] = static_cast<T>(1.0 / scale[i]);
*this = this->operator*(scalingMatrix);
if (this->determinant() < 0.) {
scale[0] = -scale[0];
change[0][0] = static_cast<T>(-1);
*this = this->operator*(change);
}
return scale;
}
template <typename T>
void Matrix<T>::print() const
/**
Simple print out routine
*/
{
write(std::cout, 10);
}
/** set matrix elements ito random values in the range from rMin to rMax*/
template <typename T>
void Matrix<T>::setRandom(size_t seed, double rMin, double rMax) {
MersenneTwister rng(seed, rMin, rMax);
for (size_t i = 0; i < m_numRows; i++) {
for (size_t j = 0; j < m_numColumns; j++) {
m_rawData[i][j] = static_cast<T>(rng.nextValue());
}
}
}
template <typename T>
void Matrix<T>::write(std::ostream &Fh, const int blockCnt) const
/**
Write out function for blocks of 10 Columns
@param Fh :: file stream for output
@param blockCnt :: number of columns per line (0 == full)
*/
{
std::ios::fmtflags oldFlags = Fh.flags();
Fh.setf(std::ios::floatfield, std::ios::scientific);
const size_t blockNumber((blockCnt > 0) ? blockCnt : m_numColumns);
size_t BCnt(0);
do {
const size_t ACnt = BCnt;
BCnt += blockNumber;
if (BCnt > m_numColumns) {
BCnt = m_numColumns;
}
if (ACnt) {
Fh << " ----- " << ACnt << " " << BCnt << " ------ \n";
}
for (size_t i = 0; i < m_numRows; i++) {
for (size_t j = ACnt; j < BCnt; j++) {
Fh << std::setw(10) << m_rawData[i][j] << " ";
}
Fh << '\n';
}
} while (BCnt < m_numColumns);
Fh.flags(oldFlags);
}
template <typename T>
std::string Matrix<T>::str() const
/**
Convert the matrix into a simple linear string expression
@return String value of output
*/
{
std::ostringstream cx;
for (size_t i = 0; i < m_numRows; i++) {
for (size_t j = 0; j < m_numColumns; j++) {
cx << std::setprecision(6) << m_rawData[i][j] << " ";
}
}
return cx.str();
}
/**
* Write an object to a stream. Format will be
* Matrix(nrows,ncols)x_00,x_01...,x_10,x_11
* @param os :: output stream
* @param matrix :: Matrix to write out
* @return The output stream (of)
*/
template <typename T>
std::ostream &operator<<(std::ostream &os, const Matrix<T> &matrix) {
dumpToStream(os, matrix, ',');
return os;
}
/**
* Write a Matrix to a stream. Format will be
* Matrix(nrowsSEPncols)x_00SEPx_01...SEPx_10SEPx_11
* @param os :: output stream
* @param matrix :: Matrix to write out
* @param delimiter :: A character to use as delimiter for the string
*/
template <typename T>
void dumpToStream(std::ostream &os, const Kernel::Matrix<T> &matrix,
const char delimiter) {
size_t nrows(matrix.numRows()), ncols(matrix.numCols());
os << "Matrix(" << nrows << delimiter << ncols << ")";
for (size_t i = 0; i < nrows; ++i) {
for (size_t j = 0; j < ncols; ++j) {
os << matrix[i][j];
if (i < nrows - 1 || j < ncols - 1)
os << delimiter;
}
}
}
/**
* Fill an object from a stream. Format should be
* Matrix(nrows,ncols)x_00,x_01...,x_10,x_11
* @param is :: A stream object
* @param in :: An object to fill
* @returns A reference to the stream
*/
template <typename T>
std::istream &operator>>(std::istream &is, Kernel::Matrix<T> &in) {
fillFromStream(is, in, ',');
return is;
}
/**
* Fill a Matrix from a stream using the given separator. Format should be
* Matrix(nrowsSEPncols)x_00SEPx_01...SEPx_10SEPx_11
* where SEP is replaced by the given separator
* @param is :: A stream object
* @param in :: An Matrix object to fill
* @param delimiter :: A single character separator that delimits the entries
*/
template <typename T>
void fillFromStream(std::istream &is, Kernel::Matrix<T> &in,
const char delimiter) {
// Stream should start with Matrix(
char dump;
std::string start(7, ' ');
for (int i = 0; i < 7; ++i) {
is >> dump;
start[i] = dump;
if (!is)
throw std::invalid_argument(
"Unexpected character when reading Matrix from stream.");
}
if (start != "Matrix(")
throw std::invalid_argument("Incorrect input format for Matrix stream.");
// Now read a nrows,ncols and )
size_t nrows(0), ncols(0);
is >> nrows;
if (!is)
throw std::invalid_argument("Expected number of rows when reading Matrix "
"from stream, found something else.");
is >> dump;
is >> ncols;
if (!is)
throw std::invalid_argument("Expected number of columns when reading "
"Matrix from stream, found something else.");
is >> dump;
if (dump != ')')
throw std::invalid_argument("Expected closing parenthesis after ncols when "
"reading Matrix from stream, found something "
"else.");
// Resize the matrix
in.setMem(nrows, ncols);
// Use getline with the delimiter set to "," to read
std::string value_str;
size_t row(0), col(0);
while (!is.eof() && std::getline(is, value_str, delimiter)) {
try {
T value = boost::lexical_cast<T>(value_str);
in.m_rawData[row][col] = value;
} catch (boost::bad_lexical_cast &) {
throw std::invalid_argument(
"Unexpected type found while reading Matrix from stream: \"" +
value_str + "\"");
}
++col;
if (col == ncols) // New row
{
col = 0;
++row;
}
}
}
///\cond TEMPLATE
// Symbol definitions for common types
template class MANTID_KERNEL_DLL Matrix<double>;
// The explicit template instantiation for int does not have an export macro
// since this produces a warning on "gcc: warning: type attributes ignored after
// type is already define" The reason for this is the use of Matrix<int>
// in a template specialization above, causing an implicit sepcialization.
// This, most likely, obtains a visibility setting from the general template
// definition.
template class Matrix<int>;
template class MANTID_KERNEL_DLL Matrix<float>;
template MANTID_KERNEL_DLL std::ostream &operator<<(std::ostream &,
const DblMatrix &);
template MANTID_KERNEL_DLL void dumpToStream(std::ostream &, const DblMatrix &,
const char);
template MANTID_KERNEL_DLL std::istream &operator>>(std::istream &,
DblMatrix &);
template MANTID_KERNEL_DLL void fillFromStream(std::istream &, DblMatrix &,
const char);
template MANTID_KERNEL_DLL std::ostream &operator<<(std::ostream &,
const Matrix<float> &);
template MANTID_KERNEL_DLL void dumpToStream(std::ostream &,
const Matrix<float> &, const char);
template MANTID_KERNEL_DLL std::istream &operator>>(std::istream &,
Matrix<float> &);
template MANTID_KERNEL_DLL void fillFromStream(std::istream &, Matrix<float> &,
const char);
template MANTID_KERNEL_DLL std::ostream &operator<<(std::ostream &,
const IntMatrix &);
template MANTID_KERNEL_DLL void dumpToStream(std::ostream &, const IntMatrix &,
const char);
template MANTID_KERNEL_DLL std::istream &operator>>(std::istream &,
IntMatrix &);
template MANTID_KERNEL_DLL void fillFromStream(std::istream &, IntMatrix &,
const char);
///\endcond TEMPLATE
} // namespace Kernel
} // namespace