1804 lines
45 KiB
C++
1804 lines
45 KiB
C++
|
/**
|
||
|
@file Matrix.cpp
|
||
|
@author Morgan McGuire, http://graphics.cs.williams.edu
|
||
|
*/
|
||
|
#include "G3D/Matrix.h"
|
||
|
#include "G3D/TextOutput.h"
|
||
|
|
||
|
static inline G3D::Matrix::T negate(G3D::Matrix::T x) {
|
||
|
return -x;
|
||
|
}
|
||
|
|
||
|
namespace G3D {
|
||
|
|
||
|
int Matrix::debugNumCopyOps = 0;
|
||
|
int Matrix::debugNumAllocOps = 0;
|
||
|
|
||
|
void Matrix::serialize(TextOutput& t) const {
|
||
|
t.writeSymbol("%");
|
||
|
t.writeNumber(rows());
|
||
|
t.writeSymbol("x");
|
||
|
t.writeNumber(cols());
|
||
|
t.pushIndent();
|
||
|
t.writeNewline();
|
||
|
|
||
|
t.writeSymbol("[");
|
||
|
for (int r = 0; r < rows(); ++r) {
|
||
|
for (int c = 0; c < cols(); ++c) {
|
||
|
t.writeNumber(impl->get(r, c));
|
||
|
if (c < cols() - 1) {
|
||
|
t.writeSymbol(",");
|
||
|
} else {
|
||
|
if (r < rows() - 1) {
|
||
|
t.writeSymbol(";");
|
||
|
t.writeNewline();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
t.writeSymbol("]");
|
||
|
t.popIndent();
|
||
|
t.writeNewline();
|
||
|
}
|
||
|
|
||
|
|
||
|
std::string Matrix::toString(const std::string& name) const {
|
||
|
std::string s;
|
||
|
|
||
|
if (name != "") {
|
||
|
s += format("%s = \n", name.c_str());
|
||
|
}
|
||
|
|
||
|
s += "[";
|
||
|
for (int r = 0; r < rows(); ++r) {
|
||
|
for (int c = 0; c < cols(); ++c) {
|
||
|
double v = impl->get(r, c);
|
||
|
|
||
|
if (::fabs(v) < 0.00001) {
|
||
|
// Don't print "negative zero"
|
||
|
s += format("% 10.04g", 0.0);
|
||
|
} else if (v == iRound(v)) {
|
||
|
// Print integers nicely
|
||
|
s += format("% 10.04g", v);
|
||
|
} else {
|
||
|
s += format("% 10.04f", v);
|
||
|
}
|
||
|
|
||
|
if (c < cols() - 1) {
|
||
|
s += ",";
|
||
|
} else if (r < rows() - 1) {
|
||
|
s += ";\n ";
|
||
|
} else {
|
||
|
s += "]\n";
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
|
||
|
#define INPLACE(OP)\
|
||
|
ImplRef A = impl;\
|
||
|
\
|
||
|
if (! A.unique()) {\
|
||
|
impl.reset(new Impl(A->R, A->C));\
|
||
|
}\
|
||
|
\
|
||
|
A->OP(B, *impl);
|
||
|
|
||
|
Matrix& Matrix::operator*=(const T& B) {
|
||
|
INPLACE(mul)
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
|
||
|
Matrix& Matrix::operator-=(const T& B) {
|
||
|
INPLACE(sub)
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
|
||
|
Matrix& Matrix::operator+=(const T& B) {
|
||
|
INPLACE(add)
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
|
||
|
Matrix& Matrix::operator/=(const T& B) {
|
||
|
INPLACE(div)
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
|
||
|
Matrix& Matrix::operator*=(const Matrix& B) {
|
||
|
// We can't optimize this one
|
||
|
*this = *this * B;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
|
||
|
Matrix& Matrix::operator-=(const Matrix& _B) {
|
||
|
const Impl& B = *_B.impl;
|
||
|
INPLACE(sub)
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
|
||
|
Matrix& Matrix::operator+=(const Matrix& _B) {
|
||
|
const Impl& B = *_B.impl;
|
||
|
INPLACE(add)
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
|
||
|
void Matrix::arrayMulInPlace(const Matrix& _B) {
|
||
|
const Impl& B = *_B.impl;
|
||
|
INPLACE(arrayMul)
|
||
|
}
|
||
|
|
||
|
|
||
|
void Matrix::arrayDivInPlace(const Matrix& _B) {
|
||
|
const Impl& B = *_B.impl;
|
||
|
INPLACE(arrayDiv)
|
||
|
}
|
||
|
|
||
|
#undef INPLACE
|
||
|
|
||
|
Matrix Matrix::fromDiagonal(const Matrix& d) {
|
||
|
debugAssert((d.rows() == 1) || (d.cols() == 1));
|
||
|
|
||
|
int n = d.numElements();
|
||
|
Matrix D = zero(n, n);
|
||
|
for (int i = 0; i < n; ++i) {
|
||
|
D.set(i, i, d.impl->data[i]);
|
||
|
}
|
||
|
|
||
|
return D;
|
||
|
}
|
||
|
|
||
|
void Matrix::set(int r, int c, T v) {
|
||
|
if (! impl.unique()) {
|
||
|
// Copy the data before mutating; this object is shared
|
||
|
impl.reset(new Impl(*impl));
|
||
|
}
|
||
|
impl->set(r, c, v);
|
||
|
}
|
||
|
|
||
|
|
||
|
void Matrix::setRow(int r, const Matrix& vec) {
|
||
|
debugAssertM(vec.cols() == cols(),
|
||
|
"A row must be set to a vector of the same size.");
|
||
|
debugAssertM(vec.rows() == 1,
|
||
|
"A row must be set to a row vector.");
|
||
|
|
||
|
debugAssert(r >= 0);
|
||
|
debugAssert(r < rows());
|
||
|
|
||
|
if (! impl.unique()) {
|
||
|
// Copy the data before mutating; this object is shared
|
||
|
impl.reset(new Impl(*impl));
|
||
|
}
|
||
|
impl->setRow(r, vec.impl->data);
|
||
|
}
|
||
|
|
||
|
|
||
|
void Matrix::setCol(int c, const Matrix& vec) {
|
||
|
debugAssertM(vec.rows() == rows(),
|
||
|
"A column must be set to a vector of the same size.");
|
||
|
debugAssertM(vec.cols() == 1,
|
||
|
"A column must be set to a column vector.");
|
||
|
|
||
|
debugAssert(c >= 0);
|
||
|
|
||
|
debugAssert(c < cols());
|
||
|
|
||
|
if (! impl.unique()) {
|
||
|
// Copy the data before mutating; this object is shared
|
||
|
impl.reset(new Impl(*impl));
|
||
|
}
|
||
|
impl->setCol(c, vec.impl->data);
|
||
|
}
|
||
|
|
||
|
|
||
|
Matrix::T Matrix::get(int r, int c) const {
|
||
|
return impl->get(r, c);
|
||
|
}
|
||
|
|
||
|
|
||
|
Matrix Matrix::row(int r) const {
|
||
|
debugAssert(r >= 0);
|
||
|
debugAssert(r < rows());
|
||
|
Matrix out(1, cols());
|
||
|
out.impl->setRow(1, impl->elt[r]);
|
||
|
return out;
|
||
|
}
|
||
|
|
||
|
|
||
|
Matrix Matrix::col(int c) const {
|
||
|
debugAssert(c >= 0);
|
||
|
debugAssert(c < cols());
|
||
|
Matrix out(rows(), 1);
|
||
|
|
||
|
T* outData = out.impl->data;
|
||
|
// Get a pointer to the first element in the column
|
||
|
const T* inElt = &(impl->elt[0][c]);
|
||
|
int R = rows();
|
||
|
int C = cols();
|
||
|
for (int r = 0; r < R; ++r) {
|
||
|
outData[r] = *inElt;
|
||
|
// Skip around to the next row
|
||
|
inElt += C;
|
||
|
}
|
||
|
|
||
|
return out;
|
||
|
}
|
||
|
|
||
|
|
||
|
Matrix Matrix::zero(int R, int C) {
|
||
|
Impl* A = new Impl(R, C);
|
||
|
A->setZero();
|
||
|
return Matrix(A);
|
||
|
}
|
||
|
|
||
|
|
||
|
Matrix Matrix::one(int R, int C) {
|
||
|
Impl* A = new Impl(R, C);
|
||
|
for (int i = R * C - 1; i >= 0; --i) {
|
||
|
A->data[i] = 1.0;
|
||
|
}
|
||
|
return Matrix(A);
|
||
|
}
|
||
|
|
||
|
|
||
|
Matrix Matrix::random(int R, int C) {
|
||
|
Impl* A = new Impl(R, C);
|
||
|
for (int i = R * C - 1; i >= 0; --i) {
|
||
|
A->data[i] = G3D::uniformRandom(0.0, 1.0);
|
||
|
}
|
||
|
return Matrix(A);
|
||
|
}
|
||
|
|
||
|
|
||
|
Matrix Matrix::identity(int N) {
|
||
|
Impl* m = new Impl(N, N);
|
||
|
m->setZero();
|
||
|
for (int i = 0; i < N; ++i) {
|
||
|
m->elt[i][i] = 1.0;
|
||
|
}
|
||
|
return Matrix(m);
|
||
|
}
|
||
|
|
||
|
|
||
|
// Implement an explicit-output unary method by trampolining to the impl
|
||
|
#define TRAMPOLINE_EXPLICIT_1(method)\
|
||
|
void Matrix::method(Matrix& out) const {\
|
||
|
if ((out.impl == impl) && impl.unique()) {\
|
||
|
impl->method(*out.impl);\
|
||
|
} else {\
|
||
|
out = this->method();\
|
||
|
}\
|
||
|
}
|
||
|
|
||
|
TRAMPOLINE_EXPLICIT_1(abs)
|
||
|
TRAMPOLINE_EXPLICIT_1(negate)
|
||
|
TRAMPOLINE_EXPLICIT_1(arrayLog)
|
||
|
TRAMPOLINE_EXPLICIT_1(arrayExp)
|
||
|
TRAMPOLINE_EXPLICIT_1(arrayCos)
|
||
|
TRAMPOLINE_EXPLICIT_1(arraySin)
|
||
|
|
||
|
void Matrix::mulRow(int r, const T& v) {
|
||
|
debugAssert(r >= 0 && r < rows());
|
||
|
|
||
|
if (! impl.unique()) {
|
||
|
impl.reset(new Impl(*impl));
|
||
|
}
|
||
|
|
||
|
impl->mulRow(r, v);
|
||
|
}
|
||
|
|
||
|
|
||
|
void Matrix::transpose(Matrix& out) const {
|
||
|
if ((out.impl == impl) && impl.unique() && (impl->R == impl->C)) {
|
||
|
// In place
|
||
|
impl->transpose(*out.impl);
|
||
|
} else {
|
||
|
out = this->transpose();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
Matrix3 Matrix::toMatrix3() const {
|
||
|
debugAssert(impl->R == 3);
|
||
|
debugAssert(impl->C == 3);
|
||
|
return Matrix3(
|
||
|
impl->get(0,0), impl->get(0,1), impl->get(0,2),
|
||
|
impl->get(1,0), impl->get(1,1), impl->get(1,2),
|
||
|
impl->get(2,0), impl->get(2,1), impl->get(2,2));
|
||
|
}
|
||
|
|
||
|
|
||
|
Matrix4 Matrix::toMatrix4() const {
|
||
|
debugAssert(impl->R == 4);
|
||
|
debugAssert(impl->C == 4);
|
||
|
return Matrix4(
|
||
|
impl->get(0,0), impl->get(0,1), impl->get(0,2), impl->get(0,3),
|
||
|
impl->get(1,0), impl->get(1,1), impl->get(1,2), impl->get(1,3),
|
||
|
impl->get(2,0), impl->get(2,1), impl->get(2,2), impl->get(2,3),
|
||
|
impl->get(3,0), impl->get(3,1), impl->get(3,2), impl->get(3,3));
|
||
|
}
|
||
|
|
||
|
|
||
|
Vector2 Matrix::toVector2() const {
|
||
|
debugAssert(impl->R * impl->C == 2);
|
||
|
if (impl->R > impl->C) {
|
||
|
return Vector2(impl->get(0,0), impl->get(1,0));
|
||
|
} else {
|
||
|
return Vector2(impl->get(0,0), impl->get(0,1));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
Vector3 Matrix::toVector3() const {
|
||
|
debugAssert(impl->R * impl->C == 3);
|
||
|
if (impl->R > impl->C) {
|
||
|
return Vector3(impl->get(0,0), impl->get(1,0), impl->get(2, 0));
|
||
|
} else {
|
||
|
return Vector3(impl->get(0,0), impl->get(0,1), impl->get(0, 2));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
Vector4 Matrix::toVector4() const {
|
||
|
debugAssert(
|
||
|
((impl->R == 4) && (impl->C == 1)) ||
|
||
|
((impl->R == 1) && (impl->C == 4)));
|
||
|
|
||
|
if (impl->R > impl->C) {
|
||
|
return Vector4(impl->get(0,0), impl->get(1,0), impl->get(2, 0), impl->get(3,0));
|
||
|
} else {
|
||
|
return Vector4(impl->get(0,0), impl->get(0,1), impl->get(0, 2), impl->get(0,3));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void Matrix::swapRows(int r0, int r1) {
|
||
|
debugAssert(r0 >= 0 && r0 < rows());
|
||
|
debugAssert(r1 >= 0 && r1 < rows());
|
||
|
|
||
|
if (r0 == r1) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (! impl.unique()) {
|
||
|
impl.reset(new Impl(*impl));
|
||
|
}
|
||
|
|
||
|
impl->swapRows(r0, r1);
|
||
|
}
|
||
|
|
||
|
|
||
|
void Matrix::swapAndNegateCols(int c0, int c1) {
|
||
|
debugAssert(c0 >= 0 && c0 < cols());
|
||
|
debugAssert(c1 >= 0 && c1 < cols());
|
||
|
|
||
|
if (c0 == c1) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (! impl.unique()) {
|
||
|
impl.reset(new Impl(*impl));
|
||
|
}
|
||
|
|
||
|
impl->swapAndNegateCols(c0, c1);
|
||
|
}
|
||
|
|
||
|
Matrix Matrix::subMatrix(int r1, int r2, int c1, int c2) const {
|
||
|
debugAssert(r2>=r1);
|
||
|
debugAssert(c2>=c1);
|
||
|
debugAssert(c2<cols());
|
||
|
debugAssert(r2<rows());
|
||
|
debugAssert(r1>=0);
|
||
|
debugAssert(c1>=0);
|
||
|
|
||
|
Matrix X(r2 - r1 + 1, c2 - c1 + 1);
|
||
|
|
||
|
for (int r = 0; r < X.rows(); ++r) {
|
||
|
for (int c = 0; c < X.cols(); ++c) {
|
||
|
X.set(r, c, get(r + r1, c + c1));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return X;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool Matrix::anyNonZero() const {
|
||
|
return impl->anyNonZero();
|
||
|
}
|
||
|
|
||
|
|
||
|
bool Matrix::allNonZero() const {
|
||
|
return impl->allNonZero();
|
||
|
}
|
||
|
|
||
|
|
||
|
void Matrix::svd(Matrix& U, Array<T>& d, Matrix& V, bool sort) const {
|
||
|
debugAssert(rows() >= cols());
|
||
|
debugAssertM(&U != &V, "Arguments to SVD must be different matrices");
|
||
|
debugAssertM(&U != this, "Arguments to SVD must be different matrices");
|
||
|
debugAssertM(&V != this, "Arguments to SVD must be different matrices");
|
||
|
|
||
|
int R = rows();
|
||
|
int C = cols();
|
||
|
|
||
|
// Make sure we don't overwrite a shared matrix
|
||
|
if (! V.impl.unique()) {
|
||
|
V = Matrix::zero(C, C);
|
||
|
} else {
|
||
|
V.impl->setSize(C, C);
|
||
|
}
|
||
|
|
||
|
if (&U != this || ! impl.unique()) {
|
||
|
// Make a copy of this for in-place SVD
|
||
|
U.impl.reset(new Impl(*impl));
|
||
|
}
|
||
|
|
||
|
d.resize(C);
|
||
|
const char* ret = svdCore(U.impl->elt, R, C, d.getCArray(), V.impl->elt);
|
||
|
|
||
|
debugAssertM(ret == NULL, ret);
|
||
|
(void)ret;
|
||
|
|
||
|
if (sort) {
|
||
|
// Sort the singular values from greatest to least
|
||
|
|
||
|
Array<SortRank> rank;
|
||
|
rank.resize(C);
|
||
|
for (int c = 0; c < C; ++c) {
|
||
|
rank[c].col = c;
|
||
|
rank[c].value = d[c];
|
||
|
}
|
||
|
|
||
|
rank.sort(SORT_INCREASING);
|
||
|
|
||
|
Matrix Uold = U;
|
||
|
Matrix Vold = V;
|
||
|
|
||
|
U = Matrix(U.rows(), U.cols());
|
||
|
V = Matrix(V.rows(), V.cols());
|
||
|
|
||
|
// Now permute U, d, and V appropriately
|
||
|
for (int c0 = 0; c0 < C; ++c0) {
|
||
|
const int c1 = rank[c0].col;
|
||
|
|
||
|
d[c0] = rank[c0].value;
|
||
|
U.setCol(c0, Uold.col(c1));
|
||
|
V.setCol(c0, Vold.col(c1));
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
#define COMPARE_SCALAR(OP)\
|
||
|
Matrix Matrix::operator OP (const T& scalar) const {\
|
||
|
int R = rows();\
|
||
|
int C = cols();\
|
||
|
int N = R * C;\
|
||
|
Matrix out = Matrix::zero(R, C);\
|
||
|
\
|
||
|
const T* raw = impl->data;\
|
||
|
T* outRaw = out.impl->data;\
|
||
|
for (int i = 0; i < N; ++i) {\
|
||
|
outRaw[i] = raw[i] OP scalar;\
|
||
|
}\
|
||
|
\
|
||
|
return out;\
|
||
|
}
|
||
|
|
||
|
COMPARE_SCALAR(<)
|
||
|
COMPARE_SCALAR(<=)
|
||
|
COMPARE_SCALAR(>)
|
||
|
COMPARE_SCALAR(>=)
|
||
|
COMPARE_SCALAR(==)
|
||
|
COMPARE_SCALAR(!=)
|
||
|
|
||
|
#undef COMPARE_SCALAR
|
||
|
|
||
|
double Matrix::normSquared() const {
|
||
|
int R = rows();
|
||
|
int C = cols();
|
||
|
int N = R * C;
|
||
|
|
||
|
double sum = 0.0;
|
||
|
|
||
|
const T* raw = impl->data;
|
||
|
for (int i = 0; i < N; ++i) {
|
||
|
sum += square(raw[i]);
|
||
|
}
|
||
|
|
||
|
return sum;
|
||
|
}
|
||
|
|
||
|
double Matrix::norm() const {
|
||
|
return sqrt(normSquared());
|
||
|
}
|
||
|
|
||
|
///////////////////////////////////////////////////////////
|
||
|
|
||
|
Matrix::Impl::Impl(const Matrix3& M) : elt(NULL), data(NULL), R(0), C(0), dataSize(0){
|
||
|
setSize(3, 3);
|
||
|
for (int r = 0; r < 3; ++r) {
|
||
|
for (int c = 0; c < 3; ++c) {
|
||
|
set(r, c, M[r][c]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
Matrix::Impl::Impl(const Matrix4& M): elt(NULL), data(NULL), R(0), C(0), dataSize(0) {
|
||
|
setSize(4, 4);
|
||
|
for (int r = 0; r < 4; ++r) {
|
||
|
for (int c = 0; c < 4; ++c) {
|
||
|
set(r, c, M[r][c]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void Matrix::Impl::setSize(int newRows, int newCols) {
|
||
|
if ((R == newRows) && (C == newCols)) {
|
||
|
// Nothing to do
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
int newSize = newRows * newCols;
|
||
|
|
||
|
R = newRows; C = newCols;
|
||
|
|
||
|
// Only allocate if we need more space
|
||
|
// or the size difference is ridiculous
|
||
|
if ((newSize > dataSize) || (newSize < dataSize / 4)) {
|
||
|
System::alignedFree(data);
|
||
|
data = (float*)System::alignedMalloc(R * C * sizeof(T), 16);
|
||
|
++Matrix::debugNumAllocOps;
|
||
|
dataSize = newSize;
|
||
|
}
|
||
|
|
||
|
// Construct the row pointers
|
||
|
//delete[] elt;
|
||
|
System::free(elt);
|
||
|
elt = (T**)System::malloc(R * sizeof(T*));// new T*[R];
|
||
|
|
||
|
for (int r = 0; r < R; ++ r) {
|
||
|
elt[r] = data + r * C;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
Matrix::Impl::~Impl() {
|
||
|
//delete[] elt;
|
||
|
System::free(elt);
|
||
|
System::alignedFree(data);
|
||
|
}
|
||
|
|
||
|
|
||
|
Matrix::Impl& Matrix::Impl::operator=(const Impl& m) {
|
||
|
setSize(m.R, m.C);
|
||
|
System::memcpy(data, m.data, R * C * sizeof(T));
|
||
|
++Matrix::debugNumCopyOps;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
|
||
|
void Matrix::Impl::setZero() {
|
||
|
System::memset(data, 0, R * C * sizeof(T));
|
||
|
}
|
||
|
|
||
|
|
||
|
void Matrix::Impl::swapRows(int r0, int r1) {
|
||
|
T* R0 = elt[r0];
|
||
|
T* R1 = elt[r1];
|
||
|
|
||
|
for (int c = 0; c < C; ++c) {
|
||
|
T temp = R0[c];
|
||
|
R0[c] = R1[c];
|
||
|
R1[c] = temp;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void Matrix::Impl::swapAndNegateCols(int c0, int c1) {
|
||
|
for (int r = 0; r < R; ++r) {
|
||
|
T* row = elt[r];
|
||
|
|
||
|
const T temp = -row[c0];
|
||
|
row[c0] = -row[c1];
|
||
|
row[c1] = temp;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void Matrix::Impl::mulRow(int r, const T& v) {
|
||
|
T* row = elt[r];
|
||
|
|
||
|
for (int c = 0; c < C; ++c) {
|
||
|
row[c] *= v;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void Matrix::Impl::mul(const Impl& B, Impl& out) const {
|
||
|
const Impl& A = *this;
|
||
|
|
||
|
debugAssertM(
|
||
|
(this != &out) && (&B != &out),
|
||
|
"Output argument to mul cannot be the same as an input argument.");
|
||
|
|
||
|
debugAssert(A.C == B.R);
|
||
|
debugAssert(A.R == out.R);
|
||
|
debugAssert(B.C == out.C);
|
||
|
|
||
|
for (int r = 0; r < out.R; ++r) {
|
||
|
for (int c = 0; c < out.C; ++c) {
|
||
|
T sum = 0.0;
|
||
|
for (int i = 0; i < A.C; ++i) {
|
||
|
sum += A.get(r, i) * B.get(i, c);
|
||
|
}
|
||
|
out.set(r, c, sum);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// We're about to define several similar methods,
|
||
|
// so use a macro to share implementations. This
|
||
|
// must be a macro because the difference between
|
||
|
// the macros is the operation in the inner loop.
|
||
|
#define IMPLEMENT_ARRAY_2(method, OP)\
|
||
|
void Matrix::Impl::method(const Impl& B, Impl& out) const {\
|
||
|
const Impl& A = *this;\
|
||
|
\
|
||
|
debugAssert(A.C == B.C);\
|
||
|
debugAssert(A.R == B.R);\
|
||
|
debugAssert(A.C == out.C);\
|
||
|
debugAssert(A.R == out.R);\
|
||
|
\
|
||
|
for (int i = R * C - 1; i >= 0; --i) {\
|
||
|
out.data[i] = A.data[i] OP B.data[i];\
|
||
|
}\
|
||
|
}
|
||
|
|
||
|
|
||
|
#define IMPLEMENT_ARRAY_1(method, f)\
|
||
|
void Matrix::Impl::method(Impl& out) const {\
|
||
|
const Impl& A = *this;\
|
||
|
\
|
||
|
debugAssert(A.C == out.C);\
|
||
|
debugAssert(A.R == out.R);\
|
||
|
\
|
||
|
for (int i = R * C - 1; i >= 0; --i) {\
|
||
|
out.data[i] = f(A.data[i]);\
|
||
|
}\
|
||
|
}
|
||
|
|
||
|
|
||
|
#define IMPLEMENT_ARRAY_SCALAR(method, OP)\
|
||
|
void Matrix::Impl::method(Matrix::T B, Impl& out) const {\
|
||
|
const Impl& A = *this;\
|
||
|
\
|
||
|
debugAssert(A.C == out.C);\
|
||
|
debugAssert(A.R == out.R);\
|
||
|
\
|
||
|
for (int i = R * C - 1; i >= 0; --i) {\
|
||
|
out.data[i] = A.data[i] OP B;\
|
||
|
}\
|
||
|
}
|
||
|
|
||
|
IMPLEMENT_ARRAY_2(add, +)
|
||
|
IMPLEMENT_ARRAY_2(sub, -)
|
||
|
IMPLEMENT_ARRAY_2(arrayMul, *)
|
||
|
IMPLEMENT_ARRAY_2(arrayDiv, /)
|
||
|
|
||
|
IMPLEMENT_ARRAY_SCALAR(add, +)
|
||
|
IMPLEMENT_ARRAY_SCALAR(sub, -)
|
||
|
IMPLEMENT_ARRAY_SCALAR(mul, *)
|
||
|
IMPLEMENT_ARRAY_SCALAR(div, /)
|
||
|
|
||
|
IMPLEMENT_ARRAY_1(abs, ::fabs)
|
||
|
IMPLEMENT_ARRAY_1(negate, ::negate)
|
||
|
IMPLEMENT_ARRAY_1(arrayLog, ::log)
|
||
|
IMPLEMENT_ARRAY_1(arraySqrt, ::sqrt)
|
||
|
IMPLEMENT_ARRAY_1(arrayExp, ::exp)
|
||
|
IMPLEMENT_ARRAY_1(arrayCos, ::cos)
|
||
|
IMPLEMENT_ARRAY_1(arraySin, ::sin)
|
||
|
|
||
|
#undef IMPLEMENT_ARRAY_SCALAR
|
||
|
#undef IMPLEMENT_ARRAY_1
|
||
|
#undef IMPLEMENT_ARRAY_2
|
||
|
|
||
|
// lsub is special because the argument order is reversed
|
||
|
void Matrix::Impl::lsub(Matrix::T B, Impl& out) const {
|
||
|
const Impl& A = *this;
|
||
|
|
||
|
debugAssert(A.C == out.C);
|
||
|
debugAssert(A.R == out.R);
|
||
|
|
||
|
for (int i = R * C - 1; i >= 0; --i) {
|
||
|
out.data[i] = B - A.data[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void Matrix::Impl::inverseViaAdjoint(Impl& out) const {
|
||
|
debugAssert(&out != this);
|
||
|
|
||
|
// Inverse = adjoint / determinant
|
||
|
|
||
|
adjoint(out);
|
||
|
|
||
|
// Don't call the determinant method when we already have an
|
||
|
// adjoint matrix; there's a faster way of computing it: the dot
|
||
|
// product of the first row and the adjoint's first col.
|
||
|
double det = 0.0;
|
||
|
for (int r = R - 1; r >= 0; --r) {
|
||
|
det += elt[0][r] * out.elt[r][0];
|
||
|
}
|
||
|
|
||
|
out.div(Matrix::T(det), out);
|
||
|
}
|
||
|
|
||
|
|
||
|
void Matrix::Impl::transpose(Impl& out) const {
|
||
|
debugAssert(out.R == C);
|
||
|
debugAssert(out.C == R);
|
||
|
|
||
|
if (&out == this) {
|
||
|
// Square matrix in place
|
||
|
for (int r = 0; r < R; ++r) {
|
||
|
for (int c = r + 1; c < C; ++c) {
|
||
|
T temp = get(r, c);
|
||
|
out.set(r, c, get(c, r));
|
||
|
out.set(c, r, temp);
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
for (int r = 0; r < R; ++r) {
|
||
|
for (int c = 0; c < C; ++c) {
|
||
|
out.set(c, r, get(r, c));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void Matrix::Impl::adjoint(Impl& out) const {
|
||
|
cofactor(out);
|
||
|
// Transpose is safe to perform in place
|
||
|
out.transpose(out);
|
||
|
}
|
||
|
|
||
|
|
||
|
void Matrix::Impl::cofactor(Impl& out) const {
|
||
|
debugAssert(&out != this);
|
||
|
for(int r = 0; r < R; ++r) {
|
||
|
for(int c = 0; c < C; ++c) {
|
||
|
out.set(r, c, cofactor(r, c));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
Matrix::T Matrix::Impl::cofactor(int r, int c) const {
|
||
|
// Strang p. 217
|
||
|
float s = isEven(r + c) ? 1.0f : -1.0f;
|
||
|
|
||
|
return s * determinant(r, c);
|
||
|
}
|
||
|
|
||
|
|
||
|
Matrix::T Matrix::Impl::determinant(int nr, int nc) const {
|
||
|
debugAssert(R > 0);
|
||
|
debugAssert(C > 0);
|
||
|
Impl A(R - 1, C - 1);
|
||
|
withoutRowAndCol(nr, nc, A);
|
||
|
return A.determinant();
|
||
|
}
|
||
|
|
||
|
|
||
|
void Matrix::Impl::setRow(int r, const T* vals) {
|
||
|
debugAssert(r >= 0);
|
||
|
System::memcpy(elt[r], vals, sizeof(T) * C);
|
||
|
}
|
||
|
|
||
|
|
||
|
void Matrix::Impl::setCol(int c, const T* vals) {
|
||
|
for (int r = 0; r < R; ++r) {
|
||
|
elt[r][c] = vals[r];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
Matrix::T Matrix::Impl::determinant() const {
|
||
|
|
||
|
debugAssert(R == C);
|
||
|
|
||
|
// Compute using cofactors
|
||
|
switch(R) {
|
||
|
case 0:
|
||
|
return 0;
|
||
|
|
||
|
case 1:
|
||
|
// Determinant of a 1x1 is the element
|
||
|
return elt[0][0];
|
||
|
|
||
|
case 2:
|
||
|
// Determinant of a 2x2 is ad-bc
|
||
|
return elt[0][0] * elt[1][1] - elt[0][1] * elt[1][0];
|
||
|
|
||
|
case 3:
|
||
|
{
|
||
|
// Determinant of an nxn matrix is the dot product of the first
|
||
|
// row with the first row of cofactors. The base cases of this
|
||
|
// method get called a lot, so we spell out the implementation
|
||
|
// for the 3x3 case.
|
||
|
|
||
|
float cofactor00 = elt[1][1] * elt[2][2] - elt[1][2] * elt[2][1];
|
||
|
float cofactor10 = elt[1][2] * elt[2][0] - elt[1][0] * elt[2][2];
|
||
|
float cofactor20 = elt[1][0] * elt[2][1] - elt[1][1] * elt[2][0];
|
||
|
|
||
|
return Matrix::T(
|
||
|
elt[0][0] * cofactor00 +
|
||
|
elt[0][1] * cofactor10 +
|
||
|
elt[0][2] * cofactor20);
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
// Determinant of an n x n matrix is the dot product of the first
|
||
|
// row with the first row of cofactors
|
||
|
T det = 0;
|
||
|
|
||
|
for (int c = 0; c < C; ++c) {
|
||
|
det += elt[0][c] * cofactor(0, c);
|
||
|
}
|
||
|
|
||
|
return det;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void Matrix::Impl::withoutRowAndCol(int excludeRow, int excludeCol, Impl& out) const {
|
||
|
debugAssert(out.R == R - 1);
|
||
|
debugAssert(out.C == C - 1);
|
||
|
|
||
|
for (int r = 0; r < out.R; ++r) {
|
||
|
for (int c = 0; c < out.C; ++c) {
|
||
|
out.elt[r][c] = elt[r + ((r >= excludeRow) ? 1 : 0)][c + ((c >= excludeCol) ? 1 : 0)];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
Matrix Matrix::pseudoInverse(float tolerance) const {
|
||
|
if ((cols() == 1) || (rows() == 1)) {
|
||
|
return vectorPseudoInverse();
|
||
|
} else if ((cols() <= 4) || (rows() <= 4)) {
|
||
|
return partitionPseudoInverse();
|
||
|
} else {
|
||
|
return svdPseudoInverse(tolerance);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
Public function for testing purposes only. Use pseudoInverse(), as it contains optimizations for
|
||
|
nonsingular matrices with at least one small (<5) dimension.
|
||
|
*/
|
||
|
// See http://en.wikipedia.org/wiki/Moore%E2%80%93Penrose_pseudoinverse
|
||
|
Matrix Matrix::svdPseudoInverse(float tolerance) const {
|
||
|
if (cols() > rows()) {
|
||
|
return transpose().svdPseudoInverse(tolerance).transpose();
|
||
|
}
|
||
|
|
||
|
// Matrices from SVD
|
||
|
Matrix U, V;
|
||
|
|
||
|
// Diagonal elements
|
||
|
Array<T> d;
|
||
|
|
||
|
svd(U, d, V);
|
||
|
|
||
|
if (rows() == 1) {
|
||
|
d.resize(1, false);
|
||
|
}
|
||
|
|
||
|
if (tolerance < 0) {
|
||
|
// TODO: Should be eps(d[0]), which is the largest diagonal
|
||
|
tolerance = G3D::max(rows(), cols()) * 0.0001f;
|
||
|
}
|
||
|
|
||
|
Matrix X;
|
||
|
|
||
|
int r = 0;
|
||
|
for (int i = 0; i < d.size(); ++i) {
|
||
|
if (d[i] > tolerance) {
|
||
|
d[i] = Matrix::T(1) / d[i];
|
||
|
++r;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (r == 0) {
|
||
|
// There were no non-zero elements
|
||
|
X = zero(cols(), rows());
|
||
|
} else {
|
||
|
// Use the first r columns
|
||
|
|
||
|
// Test code (the rest is below)
|
||
|
/*
|
||
|
d.resize(r);
|
||
|
Matrix testU = U.subMatrix(0, U.rows() - 1, 0, r - 1);
|
||
|
Matrix testV = V.subMatrix(0, V.rows() - 1, 0, r - 1);
|
||
|
Matrix testX = testV * Matrix::fromDiagonal(d) * testU.transpose();
|
||
|
X = testX;
|
||
|
*/
|
||
|
|
||
|
|
||
|
// We want to do this:
|
||
|
//
|
||
|
// d.resize(r);
|
||
|
// U = U.subMatrix(0, U.rows() - 1, 0, r - 1);
|
||
|
// X = V * Matrix::fromDiagonal(d) * U.transpose();
|
||
|
//
|
||
|
// but creating a large diagonal matrix and then
|
||
|
// multiplying by it is wasteful. So we instead
|
||
|
// explicitly perform A = (D * U')' = U * D, and
|
||
|
// then multiply X = V * A'.
|
||
|
|
||
|
Matrix A = Matrix(U.rows(), r);
|
||
|
|
||
|
const T* dPtr = d.getCArray();
|
||
|
for (int i = 0; i < A.rows(); ++i) {
|
||
|
const T* Urow = U.impl->elt[i];
|
||
|
T* Arow = A.impl->elt[i];
|
||
|
const int Acols = A.cols();
|
||
|
for (int j = 0; j < Acols; ++j) {
|
||
|
// A(i,j) = U(i,:) * D(:,j)
|
||
|
// This is non-zero only at j = i because D is diagonal
|
||
|
// A(i,j) = U(i,j) * D(j,j)
|
||
|
Arow[j] = Urow[j] * dPtr[j];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Compute X = V.subMatrix(0, V.rows() - 1, 0, r - 1) * A.transpose()
|
||
|
//
|
||
|
// Avoid the explicit subMatrix call, and by storing A' instead of A, avoid
|
||
|
// both the transpose and the memory incoherence of striding across memory
|
||
|
// in big steps.
|
||
|
|
||
|
alwaysAssertM(A.cols() == r,
|
||
|
"Internal dimension mismatch during pseudoInverse()");
|
||
|
alwaysAssertM(V.cols() >= r,
|
||
|
"Internal dimension mismatch during pseudoInverse()");
|
||
|
|
||
|
X = Matrix(V.rows(), A.rows());
|
||
|
T** Xelt = X.impl->elt;
|
||
|
for (int i = 0; i < X.rows(); ++i) {
|
||
|
const T* Vrow = V.impl->elt[i];
|
||
|
for (int j = 0; j < X.cols(); ++j) {
|
||
|
const T* Arow = A.impl->elt[j];
|
||
|
T sum = 0;
|
||
|
for (int k = 0; k < r; ++k) {
|
||
|
sum += Vrow[k] * Arow[k];
|
||
|
}
|
||
|
Xelt[i][j] = sum;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
// Test that results are the same after optimizations:
|
||
|
Matrix diff = X - testX;
|
||
|
T n = diff.norm();
|
||
|
debugAssert(n < 0.0001);
|
||
|
*/
|
||
|
}
|
||
|
|
||
|
return X;
|
||
|
}
|
||
|
|
||
|
// Computes pseudoinverse for a vector
|
||
|
Matrix Matrix::vectorPseudoInverse() const {
|
||
|
// If vector A has nonzero elements: transpose A, then divide each elt. by the squared norm
|
||
|
// If A is zero vector: transpose A
|
||
|
double x = 0.0;
|
||
|
|
||
|
if (anyNonZero()) {
|
||
|
x = 1.0 / normSquared();
|
||
|
}
|
||
|
|
||
|
Matrix A(cols(), rows());
|
||
|
T** Aelt = A.impl->elt;
|
||
|
for (int r = 0; r < rows(); ++r) {
|
||
|
const T* MyRow = impl->elt[r];
|
||
|
for (int c = 0; c < cols(); ++c) {
|
||
|
Aelt[c][r] = T(MyRow[c] * x);
|
||
|
}
|
||
|
}
|
||
|
return Matrix(A);
|
||
|
}
|
||
|
|
||
|
|
||
|
Matrix Matrix::rowPartPseudoInverse() const{
|
||
|
int m = rows();
|
||
|
int n = cols();
|
||
|
alwaysAssertM((m<=n),"Row-partitioned block matrix pseudoinverse requires R<C");
|
||
|
|
||
|
// B = A * A'
|
||
|
Matrix A = *this;
|
||
|
Matrix B = Matrix(m,m);
|
||
|
|
||
|
T** Aelt = A.impl->elt;
|
||
|
T** Belt = B.impl->elt;
|
||
|
for (int i = 0; i < m; ++i) {
|
||
|
const T* Arow = Aelt[i];
|
||
|
for (int j = 0; j < m; ++j) {
|
||
|
const T* Brow = Aelt[j];
|
||
|
T sum = 0;
|
||
|
for (int k = 0; k < n; ++k) {
|
||
|
sum += Arow[k] * Brow[k];
|
||
|
}
|
||
|
Belt[i][j] = sum;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// B has size m x m
|
||
|
switch (m) {
|
||
|
case 2:
|
||
|
return row2PseudoInverse(B);
|
||
|
|
||
|
case 3:
|
||
|
return row3PseudoInverse(B);
|
||
|
|
||
|
case 4:
|
||
|
return row4PseudoInverse(B);
|
||
|
|
||
|
default:
|
||
|
alwaysAssertM(false, "G3D internal error: Should have used the vector or general case!");
|
||
|
return Matrix();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Matrix Matrix::colPartPseudoInverse() const{
|
||
|
int m = rows();
|
||
|
int n = cols();
|
||
|
alwaysAssertM((m>=n),"Column-partitioned block matrix pseudoinverse requires R>C");
|
||
|
// TODO: Put each of the individual cases in its own helper function
|
||
|
// TODO: Push the B computation down into the individual cases
|
||
|
// B = A' * A
|
||
|
Matrix A = *this;
|
||
|
Matrix B = Matrix(n, n);
|
||
|
T** Aelt = A.impl->elt;
|
||
|
T** Belt = B.impl->elt;
|
||
|
for (int i = 0; i < n; ++i) {
|
||
|
for (int j = 0; j < n; ++j) {
|
||
|
T sum = 0;
|
||
|
for (int k = 0; k < m; ++k) {
|
||
|
sum += Aelt[k][i] * Aelt[k][j];
|
||
|
}
|
||
|
Belt[i][j] = sum;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// B has size n x n
|
||
|
switch (n) {
|
||
|
case 2:
|
||
|
return col2PseudoInverse(B);
|
||
|
|
||
|
case 3:
|
||
|
return col3PseudoInverse(B);
|
||
|
|
||
|
case 4:
|
||
|
return col4PseudoInverse(B);
|
||
|
|
||
|
default:
|
||
|
alwaysAssertM(false, "G3D internal error: Should have used the vector or general case!");
|
||
|
return Matrix();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Matrix Matrix::col2PseudoInverse(const Matrix& B) const {
|
||
|
|
||
|
Matrix A = *this;
|
||
|
int m = rows();
|
||
|
int n = cols();
|
||
|
(void)n;
|
||
|
|
||
|
// Row-major 2x2 matrix
|
||
|
const float B2[2][2] =
|
||
|
{{B.get(0,0), B.get(0,1)},
|
||
|
{B.get(1,0), B.get(1,1)}};
|
||
|
|
||
|
float det = (B2[0][0]*B2[1][1]) - (B2[0][1]*B2[1][0]);
|
||
|
|
||
|
if (fuzzyEq(det, T(0))) {
|
||
|
|
||
|
// Matrix was singular; the block matrix pseudo-inverse can't
|
||
|
// handle that, so fall back to the old case
|
||
|
return svdPseudoInverse();
|
||
|
|
||
|
} else {
|
||
|
// invert using formula at http://www.netsoc.tcd.ie/~jgilbert/maths_site/applets/algebra/matrix_inversion.html
|
||
|
|
||
|
// Multiply by Binv * A'
|
||
|
Matrix X(cols(), rows());
|
||
|
|
||
|
T** Xelt = X.impl->elt;
|
||
|
T** Aelt = A.impl->elt;
|
||
|
float binv00 = B2[1][1]/det, binv01 = -B2[1][0]/det;
|
||
|
float binv10 = -B2[0][1]/det, binv11 = B2[0][0]/det;
|
||
|
for (int j = 0; j < m; ++j) {
|
||
|
const T* Arow = Aelt[j];
|
||
|
float a0 = Arow[0];
|
||
|
float a1 = Arow[1];
|
||
|
Xelt[0][j] = binv00 * a0 + binv01 * a1;
|
||
|
Xelt[1][j] = binv10 * a0 + binv11 * a1;
|
||
|
}
|
||
|
return X;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Matrix Matrix::col3PseudoInverse(const Matrix& B) const {
|
||
|
Matrix A = *this;
|
||
|
int m = rows();
|
||
|
int n = cols();
|
||
|
|
||
|
Matrix3 B3 = B.toMatrix3();
|
||
|
if (fuzzyEq(B3.determinant(), (T)0.0)) {
|
||
|
|
||
|
// Matrix was singular; the block matrix pseudo-inverse can't
|
||
|
// handle that, so fall back to the old case
|
||
|
return svdPseudoInverse();
|
||
|
|
||
|
} else {
|
||
|
Matrix3 B3inv = B3.inverse();
|
||
|
|
||
|
// Multiply by Binv * A'
|
||
|
Matrix X(cols(), rows());
|
||
|
|
||
|
T** Xelt = X.impl->elt;
|
||
|
T** Aelt = A.impl->elt;
|
||
|
for (int i = 0; i < n; ++i) {
|
||
|
T* Xrow = Xelt[i];
|
||
|
for (int j = 0; j < m; ++j) {
|
||
|
const T* Arow = Aelt[j];
|
||
|
T sum = 0;
|
||
|
const float* Binvrow = B3inv[i];
|
||
|
for (int k = 0; k < n; ++k) {
|
||
|
sum += Binvrow[k] * Arow[k];
|
||
|
}
|
||
|
Xrow[j] = sum;
|
||
|
}
|
||
|
}
|
||
|
return X;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Matrix Matrix::col4PseudoInverse(const Matrix& B) const {
|
||
|
Matrix A = *this;
|
||
|
int m = rows();
|
||
|
int n = cols();
|
||
|
|
||
|
Matrix4 B4 = B.toMatrix4();
|
||
|
if (fuzzyEq(B4.determinant(), (T)0.0)) {
|
||
|
|
||
|
// Matrix was singular; the block matrix pseudo-inverse can't
|
||
|
// handle that, so fall back to the old case
|
||
|
return svdPseudoInverse();
|
||
|
|
||
|
} else {
|
||
|
Matrix4 B4inv = B4.inverse();
|
||
|
|
||
|
// Multiply by Binv * A'
|
||
|
Matrix X(cols(), rows());
|
||
|
|
||
|
T** Xelt = X.impl->elt;
|
||
|
T** Aelt = A.impl->elt;
|
||
|
for (int i = 0; i < n; ++i) {
|
||
|
T* Xrow = Xelt[i];
|
||
|
for (int j = 0; j < m; ++j) {
|
||
|
const T* Arow = Aelt[j];
|
||
|
T sum = 0;
|
||
|
const float* Binvrow = B4inv[i];
|
||
|
for (int k = 0; k < n; ++k) {
|
||
|
sum += Binvrow[k] * Arow[k];
|
||
|
}
|
||
|
Xrow[j] = sum;
|
||
|
}
|
||
|
}
|
||
|
return X;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Matrix Matrix::row2PseudoInverse(const Matrix& B) const {
|
||
|
|
||
|
Matrix A = *this;
|
||
|
int m = rows();
|
||
|
int n = cols();
|
||
|
(void)m;
|
||
|
|
||
|
// Row-major 2x2 matrix
|
||
|
const float B2[2][2] =
|
||
|
{{B.get(0,0), B.get(0,1)},
|
||
|
{B.get(1,0), B.get(1,1)}};
|
||
|
|
||
|
float det = (B2[0][0]*B2[1][1]) - (B2[0][1]*B2[1][0]);
|
||
|
|
||
|
if (fuzzyEq(det, T(0))) {
|
||
|
|
||
|
// Matrix was singular; the block matrix pseudo-inverse can't
|
||
|
// handle that, so fall back to the old case
|
||
|
return svdPseudoInverse();
|
||
|
|
||
|
} else {
|
||
|
// invert using formula at http://www.netsoc.tcd.ie/~jgilbert/maths_site/applets/algebra/matrix_inversion.html
|
||
|
|
||
|
// Multiply by Binv * A'
|
||
|
Matrix X(cols(), rows());
|
||
|
|
||
|
T** Xelt = X.impl->elt;
|
||
|
T** Aelt = A.impl->elt;
|
||
|
float binv00 = B2[1][1]/det, binv01 = -B2[1][0]/det;
|
||
|
float binv10 = -B2[0][1]/det, binv11 = B2[0][0]/det;
|
||
|
for (int j = 0; j < n; ++j) {
|
||
|
Xelt[j][0] = Aelt[0][j] * binv00 + Aelt[1][j] * binv10;
|
||
|
Xelt[j][1] = Aelt[0][j] * binv01 + Aelt[1][j] * binv11;
|
||
|
}
|
||
|
return X;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Matrix Matrix::row3PseudoInverse(const Matrix& B) const {
|
||
|
|
||
|
Matrix A = *this;
|
||
|
int m = rows();
|
||
|
int n = cols();
|
||
|
|
||
|
Matrix3 B3 = B.toMatrix3();
|
||
|
if (fuzzyEq(B3.determinant(), (T)0.0)) {
|
||
|
|
||
|
// Matrix was singular; the block matrix pseudo-inverse can't
|
||
|
// handle that, so fall back to the old case
|
||
|
return svdPseudoInverse();
|
||
|
|
||
|
} else {
|
||
|
Matrix3 B3inv = B3.inverse();
|
||
|
|
||
|
// Multiply by Binv * A'
|
||
|
Matrix X(cols(), rows());
|
||
|
|
||
|
T** Xelt = X.impl->elt;
|
||
|
T** Aelt = A.impl->elt;
|
||
|
for (int i = 0; i < n; ++i) {
|
||
|
T* Xrow = Xelt[i];
|
||
|
for (int j = 0; j < m; ++j) {
|
||
|
T sum = 0;
|
||
|
for (int k = 0; k < m; ++k) {
|
||
|
sum += Aelt[k][i] * B3inv[j][k];
|
||
|
}
|
||
|
Xrow[j] = sum;
|
||
|
}
|
||
|
}
|
||
|
return X;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Matrix Matrix::row4PseudoInverse(const Matrix& B) const {
|
||
|
|
||
|
Matrix A = *this;
|
||
|
int m = rows();
|
||
|
int n = cols();
|
||
|
|
||
|
Matrix4 B4 = B.toMatrix4();
|
||
|
if (fuzzyEq(B4.determinant(), (T)0.0)) {
|
||
|
|
||
|
// Matrix was singular; the block matrix pseudo-inverse can't
|
||
|
// handle that, so fall back to the old case
|
||
|
return svdPseudoInverse();
|
||
|
|
||
|
} else {
|
||
|
Matrix4 B4inv = B4.inverse();
|
||
|
|
||
|
// Multiply by Binv * A'
|
||
|
Matrix X(cols(), rows());
|
||
|
|
||
|
T** Xelt = X.impl->elt;
|
||
|
T** Aelt = A.impl->elt;
|
||
|
for (int i = 0; i < n; ++i) {
|
||
|
T* Xrow = Xelt[i];
|
||
|
for (int j = 0; j < m; ++j) {
|
||
|
T sum = 0;
|
||
|
for (int k = 0; k < m; ++k) {
|
||
|
sum += Aelt[k][i] * B4inv[j][k];
|
||
|
}
|
||
|
Xrow[j] = sum;
|
||
|
}
|
||
|
}
|
||
|
return X;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Uses the block matrix pseudoinverse to compute the pseudoinverse of a full-rank mxn matrix with m >= n
|
||
|
// http://en.wikipedia.org/wiki/Block_matrix_pseudoinverse
|
||
|
Matrix Matrix::partitionPseudoInverse() const {
|
||
|
|
||
|
// Logic:
|
||
|
// A^-1 = (A'A)^-1 A'
|
||
|
// A has few (n) columns, so A'A is small (n x n) and fast to invert
|
||
|
|
||
|
int m = rows();
|
||
|
int n = cols();
|
||
|
|
||
|
if (m < n) {
|
||
|
// TODO: optimize by pushing through the transpose
|
||
|
//return transpose().partitionPseudoInverse().transpose();
|
||
|
return rowPartPseudoInverse();
|
||
|
|
||
|
} else {
|
||
|
return colPartPseudoInverse();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Matrix::Impl::inverseInPlaceGaussJordan() {
|
||
|
debugAssertM(R == C,
|
||
|
format(
|
||
|
"Cannot perform Gauss-Jordan inverse on a non-square matrix."
|
||
|
" (Argument was %dx%d)",
|
||
|
R, C));
|
||
|
|
||
|
// Exchange to float elements
|
||
|
# define SWAP(x, y) {float temp = x; x = y; y = temp;}
|
||
|
|
||
|
// The integer arrays pivot, rowIndex, and colIndex are
|
||
|
// used for bookkeeping on the pivoting
|
||
|
static Array<int> colIndex, rowIndex, pivot;
|
||
|
|
||
|
int col = 0, row = 0;
|
||
|
|
||
|
colIndex.resize(R);
|
||
|
rowIndex.resize(R);
|
||
|
pivot.resize(R);
|
||
|
|
||
|
static const int NO_PIVOT = -1;
|
||
|
|
||
|
// Initialize the pivot array to default values.
|
||
|
for (int i = 0; i < R; ++i) {
|
||
|
pivot[i] = NO_PIVOT;
|
||
|
}
|
||
|
|
||
|
// This is the main loop over the columns to be reduced
|
||
|
// Loop over the columns.
|
||
|
for (int c = 0; c < R; ++c) {
|
||
|
|
||
|
// Find the largest element and use that as a pivot
|
||
|
float largestMagnitude = 0.0;
|
||
|
|
||
|
// This is the outer loop of the search for a pivot element
|
||
|
for (int r = 0; r < R; ++r) {
|
||
|
|
||
|
// Unless we've already found the pivot, keep going
|
||
|
if (pivot[r] != 0) {
|
||
|
|
||
|
// Find the largest pivot
|
||
|
for (int k = 0; k < R; ++k) {
|
||
|
if (pivot[k] == NO_PIVOT) {
|
||
|
const float mag = fabs(elt[r][k]);
|
||
|
|
||
|
if (mag >= largestMagnitude) {
|
||
|
largestMagnitude = mag;
|
||
|
row = r; col = k;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pivot[col] += 1;
|
||
|
|
||
|
// Interchange columns so that the pivot element is on the diagonal (we'll have to undo this
|
||
|
// at the end)
|
||
|
if (row != col) {
|
||
|
for (int k = 0; k < R; ++k) {
|
||
|
SWAP(elt[row][k], elt[col][k])
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// The pivot is now at [row, col]
|
||
|
rowIndex[c] = row;
|
||
|
colIndex[c] = col;
|
||
|
|
||
|
double piv = elt[col][col];
|
||
|
|
||
|
debugAssertM(piv != 0.0, "Matrix is singular");
|
||
|
|
||
|
// Divide everything by the pivot (avoid computing the division
|
||
|
// multiple times).
|
||
|
const double pivotInverse = 1.0 / piv;
|
||
|
elt[col][col] = 1.0;
|
||
|
|
||
|
for (int k = 0; k < R; ++k) {
|
||
|
elt[col][k] *= Matrix::T(pivotInverse);
|
||
|
}
|
||
|
|
||
|
// Reduce all rows
|
||
|
for (int r = 0; r < R; ++r) {
|
||
|
// Skip over the pivot row
|
||
|
if (r != col) {
|
||
|
|
||
|
double oldValue = elt[r][col];
|
||
|
elt[r][col] = 0.0;
|
||
|
|
||
|
for (int k = 0; k < R; ++k) {
|
||
|
elt[r][k] -= Matrix::T(elt[col][k] * oldValue);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// Put the columns back in the correct locations
|
||
|
for (int i = R - 1; i >= 0; --i) {
|
||
|
if (rowIndex[i] != colIndex[i]) {
|
||
|
for (int k = 0; k < R; ++k) {
|
||
|
SWAP(elt[k][rowIndex[i]], elt[k][colIndex[i]]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# undef SWAP
|
||
|
}
|
||
|
|
||
|
|
||
|
bool Matrix::Impl::anyNonZero() const {
|
||
|
int N = R * C;
|
||
|
for (int i = 0; i < N; ++i) {
|
||
|
if (data[i] != 0.0) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool Matrix::Impl::allNonZero() const {
|
||
|
int N = R * C;
|
||
|
for (int i = 0; i < N; ++i) {
|
||
|
if (data[i] == 0.0) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
/** Helper for svdCore */
|
||
|
static double pythag(double a, double b) {
|
||
|
|
||
|
double at = fabs(a), bt = fabs(b), ct, result;
|
||
|
|
||
|
if (at > bt) {
|
||
|
ct = bt / at;
|
||
|
result = at * sqrt(1.0 + square(ct));
|
||
|
} else if (bt > 0.0) {
|
||
|
ct = at / bt;
|
||
|
result = bt * sqrt(1.0 + square(ct));
|
||
|
} else {
|
||
|
result = 0.0;
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
#define SIGN(a, b) ((b) >= 0.0 ? fabs(a) : -fabs(a))
|
||
|
|
||
|
const char* Matrix::svdCore(float** U, int rows, int cols, float* D, float** V) {
|
||
|
const int MAX_ITERATIONS = 30;
|
||
|
|
||
|
int flag, i, its, j, jj, k, l = 0, nm = 0;
|
||
|
double c, f, h, s, x, y, z;
|
||
|
double anorm = 0.0, g = 0.0, scale = 0.0;
|
||
|
|
||
|
// Temp row vector
|
||
|
double* rv1;
|
||
|
|
||
|
debugAssertM(rows >= cols, "Must have more rows than columns");
|
||
|
|
||
|
rv1 = (double*)System::alignedMalloc(cols * sizeof(double), 16);
|
||
|
debugAssert(rv1);
|
||
|
|
||
|
// Householder reduction to bidiagonal form
|
||
|
for (i = 0; i < cols; ++i) {
|
||
|
|
||
|
// Left-hand reduction
|
||
|
l = i + 1;
|
||
|
rv1[i] = scale * g;
|
||
|
g = s = scale = 0.0;
|
||
|
|
||
|
if (i < rows) {
|
||
|
|
||
|
for (k = i; k < rows; ++k) {
|
||
|
scale += fabs((double)U[k][i]);
|
||
|
}
|
||
|
|
||
|
if (scale) {
|
||
|
for (k = i; k < rows; ++k) {
|
||
|
U[k][i] = (float)((double)U[k][i]/scale);
|
||
|
s += ((double)U[k][i] * (double)U[k][i]);
|
||
|
}
|
||
|
|
||
|
f = (double)U[i][i];
|
||
|
|
||
|
g = -sign(f)*(sqrt(s));
|
||
|
h = f * g - s;
|
||
|
U[i][i] = (float)(f - g);
|
||
|
|
||
|
if (i != cols - 1) {
|
||
|
for (j = l; j < cols; ++j) {
|
||
|
|
||
|
for (s = 0.0, k = i; k < rows; ++k) {
|
||
|
s += ((double)U[k][i] * (double)U[k][j]);
|
||
|
}
|
||
|
|
||
|
f = s / h;
|
||
|
for (k = i; k < rows; ++k) {
|
||
|
U[k][j] += (float)(f * (double)U[k][i]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
for (k = i; k < rows; ++k) {
|
||
|
U[k][i] = (float)((double)U[k][i]*scale);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
D[i] = (float)(scale * g);
|
||
|
|
||
|
// right-hand reduction
|
||
|
g = s = scale = 0.0;
|
||
|
if (i < rows && i != cols - 1) {
|
||
|
for (k = l; k < cols; ++k) {
|
||
|
scale += fabs((double)U[i][k]);
|
||
|
}
|
||
|
|
||
|
if (scale) {
|
||
|
for (k = l; k < cols; ++k) {
|
||
|
U[i][k] = (float)((double)U[i][k]/scale);
|
||
|
s += ((double)U[i][k] * (double)U[i][k]);
|
||
|
}
|
||
|
|
||
|
f = (double)U[i][l];
|
||
|
g = -SIGN(sqrt(s), f);
|
||
|
h = f * g - s;
|
||
|
U[i][l] = (float)(f - g);
|
||
|
|
||
|
for (k = l; k < cols; ++k) {
|
||
|
rv1[k] = (double)U[i][k] / h;
|
||
|
}
|
||
|
|
||
|
if (i != rows - 1) {
|
||
|
|
||
|
for (j = l; j < rows; ++j) {
|
||
|
for (s = 0.0, k = l; k < cols; ++k) {
|
||
|
s += ((double)U[j][k] * (double)U[i][k]);
|
||
|
}
|
||
|
|
||
|
for (k = l; k < cols; ++k) {
|
||
|
U[j][k] += (float)(s * rv1[k]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (k = l; k < cols; ++k) {
|
||
|
U[i][k] = (float)((double)U[i][k]*scale);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
anorm = max(anorm, fabs((double)D[i]) + fabs(rv1[i]));
|
||
|
}
|
||
|
|
||
|
// accumulate the right-hand transformation
|
||
|
for (i = cols - 1; i >= 0; --i) {
|
||
|
if (i < cols - 1) {
|
||
|
if (g) {
|
||
|
for (j = l; j < cols; ++j) {
|
||
|
V[j][i] = (float)(((double)U[i][j] / (double)U[i][l]) / g);
|
||
|
}
|
||
|
|
||
|
// double division to avoid underflow
|
||
|
for (j = l; j < cols; ++j) {
|
||
|
for (s = 0.0, k = l; k < cols; ++k) {
|
||
|
s += ((double)U[i][k] * (double)V[k][j]);
|
||
|
}
|
||
|
|
||
|
for (k = l; k < cols; ++k) {
|
||
|
V[k][j] += (float)(s * (double)V[k][i]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (j = l; j < cols; ++j) {
|
||
|
V[i][j] = V[j][i] = 0.0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
V[i][i] = 1.0;
|
||
|
g = rv1[i];
|
||
|
l = i;
|
||
|
}
|
||
|
|
||
|
// accumulate the left-hand transformation
|
||
|
for (i = cols - 1; i >= 0; --i) {
|
||
|
l = i + 1;
|
||
|
g = (double)D[i];
|
||
|
if (i < cols - 1) {
|
||
|
for (j = l; j < cols; ++j) {
|
||
|
U[i][j] = 0.0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (g) {
|
||
|
g = 1.0 / g;
|
||
|
if (i != cols - 1) {
|
||
|
for (j = l; j < cols; ++j) {
|
||
|
for (s = 0.0, k = l; k < rows; ++k) {
|
||
|
s += ((double)U[k][i] * (double)U[k][j]);
|
||
|
}
|
||
|
|
||
|
f = (s / (double)U[i][i]) * g;
|
||
|
|
||
|
for (k = i; k < rows; ++k) {
|
||
|
U[k][j] += (float)(f * (double)U[k][i]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (j = i; j < rows; ++j) {
|
||
|
U[j][i] = (float)((double)U[j][i]*g);
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
for (j = i; j < rows; ++j) {
|
||
|
U[j][i] = 0.0;
|
||
|
}
|
||
|
}
|
||
|
++U[i][i];
|
||
|
}
|
||
|
|
||
|
// diagonalize the bidiagonal form
|
||
|
for (k = cols - 1; k >= 0; --k) {
|
||
|
// loop over singular values
|
||
|
for (its = 0; its < MAX_ITERATIONS; ++its) {
|
||
|
// loop over allowed iterations
|
||
|
flag = 1;
|
||
|
|
||
|
for (l = k; l >= 0; --l) {
|
||
|
// test for splitting
|
||
|
nm = l - 1;
|
||
|
if (fabs(rv1[l]) + anorm == anorm) {
|
||
|
flag = 0;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (fabs((double)D[nm]) + anorm == anorm) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (flag) {
|
||
|
c = 0.0;
|
||
|
s = 1.0;
|
||
|
for (i = l; i <= k; ++i) {
|
||
|
f = s * rv1[i];
|
||
|
if (fabs(f) + anorm != anorm) {
|
||
|
g = (double)D[i];
|
||
|
h = pythag(f, g);
|
||
|
D[i] = (float)h;
|
||
|
h = 1.0 / h;
|
||
|
c = g * h;
|
||
|
s = (- f * h);
|
||
|
for (j = 0; j < rows; ++j) {
|
||
|
y = (double)U[j][nm];
|
||
|
z = (double)U[j][i];
|
||
|
U[j][nm] = (float)(y * c + z * s);
|
||
|
U[j][i] = (float)(z * c - y * s);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
z = (double)D[k];
|
||
|
if (l == k) {
|
||
|
// convergence
|
||
|
if (z < 0.0) {
|
||
|
// make singular value nonnegative
|
||
|
D[k] = (float)(-z);
|
||
|
|
||
|
for (j = 0; j < cols; ++j) {
|
||
|
V[j][k] = (-V[j][k]);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (its >= MAX_ITERATIONS) {
|
||
|
free(rv1);
|
||
|
rv1 = NULL;
|
||
|
return "Failed to converge.";
|
||
|
}
|
||
|
|
||
|
// shift from bottom 2 x 2 minor
|
||
|
x = (double)D[l];
|
||
|
nm = k - 1;
|
||
|
y = (double)D[nm];
|
||
|
g = rv1[nm];
|
||
|
h = rv1[k];
|
||
|
f = ((y - z) * (y + z) + (g - h) * (g + h)) / (2.0 * h * y);
|
||
|
g = pythag(f, 1.0);
|
||
|
f = ((x - z) * (x + z) + h * ((y / (f + SIGN(g, f))) - h)) / x;
|
||
|
|
||
|
// next QR transformation
|
||
|
c = s = 1.0;
|
||
|
for (j = l; j <= nm; ++j) {
|
||
|
i = j + 1;
|
||
|
g = rv1[i];
|
||
|
y = (double)D[i];
|
||
|
h = s * g;
|
||
|
g = c * g;
|
||
|
z = pythag(f, h);
|
||
|
rv1[j] = z;
|
||
|
c = f / z;
|
||
|
s = h / z;
|
||
|
f = x * c + g * s;
|
||
|
g = g * c - x * s;
|
||
|
h = y * s;
|
||
|
y = y * c;
|
||
|
|
||
|
for (jj = 0; jj < cols; ++jj) {
|
||
|
x = (double)V[jj][j];
|
||
|
z = (double)V[jj][i];
|
||
|
V[jj][j] = (float)(x * c + z * s);
|
||
|
V[jj][i] = (float)(z * c - x * s);
|
||
|
}
|
||
|
z = pythag(f, h);
|
||
|
D[j] = (float)z;
|
||
|
if (z) {
|
||
|
z = 1.0 / z;
|
||
|
c = f * z;
|
||
|
s = h * z;
|
||
|
}
|
||
|
f = (c * g) + (s * y);
|
||
|
x = (c * y) - (s * g);
|
||
|
for (jj = 0; jj < rows; ++jj) {
|
||
|
y = (double)U[jj][j];
|
||
|
z = (double)U[jj][i];
|
||
|
U[jj][j] = (float)(y * c + z * s);
|
||
|
U[jj][i] = (float)(z * c - y * s);
|
||
|
}
|
||
|
}
|
||
|
rv1[l] = 0.0;
|
||
|
rv1[k] = f;
|
||
|
D[k] = (float)x;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
System::alignedFree(rv1);
|
||
|
rv1 = NULL;
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
#undef SIGN
|
||
|
|
||
|
}
|