用C++写的矩阵处理函数 包括求逆、转置、乘积等等
最近,无论是大学还是小学,都放暑假了。
我们本来也应该有暑假的,可是悲催地被老师给残忍剥夺了,只能继续呆在实验室里面苦逼地干活。
最近南方天气太热,室外的温度老是挑战40度大关,中午出去吃个饭,回来时身上的衣服都会彻底湿透,在这样的天气里,干活的效率可想而知了。
最近在看机器学习和动态贝叶斯网络的书,顺便找了一些源代码来看,看的过程实在痛苦,书上全是多元统计分析的东西,神马狄利克雷分布,贝塔分布,伽马分布,高斯分布...,看一会儿脑袋就大了,这时索性看点代码换换脑子。在众多的动态贝叶斯源码中,有一种是由C++代码写成的,我们知道,在机器学习领域,编码时需要处理很多的高维矩阵运算,这让人(尤其是数学学得不好的人)很是头疼,所幸的是现在有很多语言提供了对这些公式的编码支持,只需要写很少的代码就可以实现复杂的运算,比如Matlab、
Python等,同时,我们也知道如果用C系列的语言,比如C、C++之类的,写这种代码,会写死人的,因为一些矩阵的处理和运算实在是太麻烦。但是,有时候由于各种原因,用这些语言写矩阵运算是不可避免的,所以,掌握一些C系列语言的矩阵运算操作是有必要的。
好了,言归正传,我下面贴出源代码,希望对某些人能起到一点的帮助~~
后期我会把我写好的注释贴出来,并提供一些可运行的例子,让大家能够直接运行,在编写程序的过程中进行学习。
/*
* mdarray.h
*/
#ifndef MDARRAY_H_
#define MDARRAY_H_
// For Boost serialization
#include
#include
#include
#include
#include
#include
#include
#include
#include "../framework/mocapyexceptions.h"
#include "randomgen.h"
#include "utils.h"
namespace mocapy {
// Forwards
template
class MDArray;
template std::ostream& operator<<(std::ostream&, const MDArray&);
template T sumvect(std::vector & v);
#define __CLPK_integer int
#define __CLPK_doublereal double
extern "C" void dgeev_(char *jobvl, char *jobvr, int *n, double *a, int *lda, double *wr, double *wi, double *vl, int *ldvl, double *vr, int *ldvr, double *work, int *lwork, int *info);
extern "C" void dgetrf_(const int*, const int*, double*, const int*, int*, int*);
extern "C" void dgetri_(const int*, double*, const int*, const int*, double*, const int*, int*);
template
class MDArray {
public:
// Constructors
MDArray() {};
MDArray(const MDArray& a) {
copy(a);
}
MDArray(const std::vector & shape) {
set_shape(shape);
}
MDArray(const std::vector & shape) {
std::vector sh;
for (uint i=0; i get_shape() const {
return shape;
}
void set_shape(const std::vector & new_shape, bool reset = true);
void set_shape(uint a, uint b = INF, uint c = INF, uint d = INF);
// Getters
std::vector& get_values() {
return values;
}
std::vector get_values_flat();
const std::vector get_own_index() {
return ownIndex;
}
MDArray & get_view(const std::vector & indices);
MDArray& get_view(uint a);
inline T & get(uint a, uint b);
inline T & get(uint a, uint b, uint c);
T get_max();
void get_max(T & m);
std::vector get_slice(uint from, uint to);
// Setters
void set(uint a, T new_value);
void set(uint a, uint b, T new_value);
void set(uint a, uint b, uint c, T new_value);
void set(uint a, MDArray & new_value);
void set_values(const std::vector & a);
void set_values(T * a);
// set_wildcard assigns specified value to the elements in the index vector.
// The index vector can have -1 valued indices corresponding to wildcards.
// e.g. set_wildcard with index=[3,-1,0], value=5, assigns integer value 5
// to [3,0,0], [3,1,0], [3,2,0], ...
// There can be an arbitrary number of wildcards.
void set_wildcard(const std::vector & index, T value, int depth=0);
// In a 2-dim matrix M, repeat copies 'a' to M[i] for all i
void repeat(uint s, const std::vector & a);
void div_inplace(T a);
void div_inplace(MDArray & a);
void add_inplace(T a);
void add_inplace(MDArray & a);
void add_inplace(std::vector > & a);
void add_inplace(std::vector & a);
void sub_inplace(MDArray & a);
void log_all(); //natural log
void exp_all(); //natural exp
void sqr_all(); //square the elements (x*x)
void sqrt_all(); //square root of the elements (x^.5)
uint bisect(double b);
MDArray sumlast();
void cumsum(bool safe=true);
void normalize();
void transpose();
MDArray swapaxes(uint axis1, uint axis2);
MDArray moveaxis(uint from, uint to);
void rnd_cov(uint dim, RandomGen * rg);
void multiply_inplace(T m);
void multiply_inplace(MDArray & m);
inline T dot(MDArray & m);
// inline vector dot(vector & v);
inline void mult3(MDArray & m);
double det();
double det_old();
// double determinant2();
void inv();
void inv_old();
//Operators
friend std::ostream& operator<< (std::ostream& output, const MDArray& a);
MDArray& operator=(const MDArray& a);
inline T & operator[](std::vector & indices);
inline T & operator[](uint a);
MDArray operator/(T a);
MDArray operator+(MDArray & a);
MDArray operator-(MDArray & a);
std::vector operator*(std::vector & v);
MDArray operator*(MDArray & v);
// Misc.
void clear();
uint size();
bool empty() const;
void randomize( RandomGen * rg, double maxrnd=1.0, bool thin=false);
std::vector flat_iterator();
void flat_array(T* v);
void clip(double minimum, double maximum);
void copy(const MDArray & array);
void copy_cast(const MDArray& array);
void eye(uint dim, T r = 1);
void keep_eye();
void makeRotationMatrix(double angle, MDArray & v);
void makeRefMatrix(MDArray & u, MDArray & v);
void makeRotationMatrix(MDArray & u, MDArray & v);
// Calculate eigen values and vectors of a symmetric matrix
std::pair, std::vector > > eigen();
// String representation
std::string tostring(
const uint precision=2,
const uint width=0,
const std::string sep_field=" ",
const std::string sep_record="\n",
const std::string sep_record_outer="\n"
) const;
// Persistence
friend class boost::serialization::access;
template
void serialize(Archive & ar, const unsigned int version);
friend class MDArray;
protected:
void initialize(bool reset = true);
void flat_iterator(std::vector & v);
void set(std::vector & indices, T new_value, uint index);
inline T & get(std::vector & indices, uint index);
MDArray& get_view(const std::vector & indices, uint index);
void sumlast(MDArray & sumArray, uint sumDim, uint currentDim);
void cumsum(uint currentDim, uint dimension, bool safe);
void transpose(double **a, int n);
void permuteaxes(const std::vector & permutation, std::vector & new_index, MDArray & new_array);
void CoFactor(double **a, int n, double **b);
double determinant(double **a, int n);
MDArray LU(bool & neg, __CLPK_integer *pivots=NULL);
std::vector*> A;
std::vector values;
std::vector ownIndex;
std::vector shape;
};
// String representation
template
std::string MDArray::tostring(
const uint precision,
const uint width,
const std::string sep_field,
const std::string sep_record,
const std::string sep_record_outer
) const
{
// Create local recursive print function
struct Local {
static void recursive_print(
MDArray a,
std::ostringstream & os,
std::string sep_field,
std::string sep_record,
std::string sep_record_outer
)
{
std::vector shape = a.get_shape();
int len = shape.size();
if (len == 1)
{
// We've reached the last dimension
int dim = shape[0];
for (int i=0; i0) { os << std::setw(width); }
os << std::setprecision(precision); // max precision digits after .
os << std::fixed; // force use of precision digits after .
if (empty())
os << "Empty" << sep_record;
else
Local::recursive_print(*this, os, sep_field, sep_record, sep_record_outer);
return os.str();
}
// *** Getting/setting shape ***
template
void MDArray::set_shape(const std::vector & new_shape, bool reset) {
clear();
shape = new_shape;
initialize(reset);
}
// Syntactic sugar
template
void MDArray::set_shape(uint a, uint b, uint c, uint d) {
std::vector sh;
sh.push_back(a);
if (b < INF) {
sh.push_back(b);
if (c < INF)
sh.push_back(c);
if (d < INF)
sh.push_back(d);
}
set_shape(sh);
}
//*** Getters ***
template
std::vector MDArray::get_slice(uint from, uint to) {
std::vector ret;
for(uint i = from; i != to; i++) {
ret.push_back(values[i]);
}
return ret;
}
template
std::vector MDArray::get_values_flat() {
std::vector b = flat_iterator();
std::vector v;
for (uint i=0; i
inline T & MDArray::operator[](uint a) {
assert(get_shape().size() == 1); // Otherwise use the other operators or get_view
assert(a < values.size());
return values[a];
}
template
inline T & MDArray::get(uint a, uint b) {
assert(a < A.size());
assert(b < A[a]->values.size());
T & ref = A[a]->values[b];
return ref;
}
template
inline T & MDArray::get(uint a, uint b, uint c) {
return this->get_view(a).get(b,c);
}
template
inline T & MDArray::operator[](std::vector & indices) {
if (indices.size() == 2) {
return A[indices.front()]->values[indices.back()];
}
else if (indices.empty()) {
assert(!values.empty());
return values[0];
}
else if (indices.size() == 1) {
assert(indices.front() < values.size() );
return values[indices.front()];
}
else {
return get(indices, 0);
}
}
template
inline T & MDArray::get(std::vector & indices, uint index) {
if ((index + 1) == indices.size()) {
assert(index < indices.size());
assert(indices[index] < values.size());
return values[indices[index]];
} else {
assert(indexget(indices, index + 1);
}
}
template
MDArray& MDArray::get_view(const std::vector & indices) {
assert(indices.size() <= shape.size());
if(indices.empty())
return *this;
else
return get_view(indices, 0);
}
template
MDArray& MDArray::get_view(const std::vector & indices, uint index) {
if (index == indices.size()) {
return *this;
} else {
assert(index < indices.size());
assert(indices[index] < A.size());
return A[indices[index]]->get_view(indices, index + 1);
}
}
template
MDArray& MDArray::get_view(uint a) {
assert(a
void MDArray::set(uint a, T new_value) {
assert(a < values.size());
values[a] = new_value;
}
template
void MDArray::set(uint a, uint b, T new_value) {
assert(a < A.size());
assert(b < A[a]->size());
A[a]->values[b] = new_value;
}
template
void MDArray::set(uint a, uint b, uint c, T new_value) {
this->get_view(a).set(b,c,new_value);
}
template
void MDArray::set(uint a, MDArray & new_value) {
assert(a < A.size());
A[a]->copy(new_value);
}
template
void MDArray::set(std::vector & indices, T new_value, uint index) {
if (index + 1 == indices.size()) {
assert(!values.empty());
assert(index < indices.size());
assert(values.size()> indices[index]);
values[indices[index]] = new_value;
} else {
assert(index < indices.size());
assert(indices[index] < A.size());
A[indices[index]]->set(indices, new_value, index + 1);
}
}
template
void MDArray::set_values(const std::vector & a) {
std::vector b = flat_iterator();
assert(b.size() == a.size());
for (uint i=0; i
void MDArray::set_values(T * a) {
std::vector b = flat_iterator();
for (uint i=0; i
void MDArray::cumsum(bool safe) {
uint d = shape.size();
cumsum(0, d, safe);
}
template
void MDArray::cumsum(uint currentDim, uint dimension, bool safe) {
if (dimension == currentDim + 1) {
for (uint i = 1; i < values.size(); i++) {
values[i] += values[i - 1];
}
if (safe) {
assert(!values.empty());
uint i=values.size()-1;
values[i] = INF;
}
} else {
for (uint i = 0; i < A.size(); i++) {
A[i]->cumsum(currentDim + 1, dimension, safe);
}
}
}
// I'm not sure that this works...
// I need to test this! /MP
template
MDArray MDArray::sumlast() {
assert(!shape.empty());
uint sumDim = shape.size() - 1;
MDArray sumArray;
std::vector sh = shape;
assert(!sh.empty());
sh.pop_back();
sumArray.set_shape(sh);
sumlast(sumArray, sumDim, 0);
return sumArray;
}
template
void MDArray::sumlast(MDArray & sumArray, uint sumDim, uint currentDim) {
if (sumDim == currentDim) {
assert(!values.empty());
T sum(0);
for (uint i = 0; i < values.size(); i++) {
sum += values[i];
}
std::vector indx = ownIndex;
sumArray.set(indx, sum);
}
for (uint i = 0; i < A.size(); i++) {
A[i]->sumlast(sumArray, sumDim, currentDim + 1);
}
}
template
void MDArray::log_all() {
if (shape.size() == 1) {
for (uint i = 0; i < values.size(); i++) {
double v = values[i];
if (v>0)
values[i] = log(v);
else
values[i] = -INF;
}
} else {
for (uint i = 0; i < A.size(); i++) {
A[i]->log_all();
}
}
}
template
void MDArray::exp_all() {
if (shape.size() == 1) {
for (uint i = 0; i < values.size(); i++) {
values[i] = exp(values[i]);
}
} else {
for (uint i = 0; i < A.size(); i++) {
A[i]->exp_all();
}
}
}
template
void MDArray::sqr_all() {
if (shape.size() == 1) {
for (uint i = 0; i < values.size(); i++) {
values[i] = values[i] * values[i];
}
} else {
for (uint i = 0; i < A.size(); i++) {
A[i]->sqr_all();
}
}
}
template
void MDArray::sqrt_all() {
if (shape.size() == 1) {
for (uint i = 0; i < values.size(); i++) {
values[i] = srqt(values[i]);
}
} else {
for (uint i = 0; i < A.size(); i++) {
A[i]->sqrt_all();
}
}
}
template
void MDArray::normalize() {
if (shape.size() == 1) {
T sum(0);
for (uint i = 0; i < values.size(); i++) {
sum += values[i];
}
for (uint i = 0; i < values.size(); i++) {
if (sum != 0)
values[i] /= sum;
}
} else {
for (uint i = 0; i < A.size(); i++) {
A[i]->normalize();
}
}
}
template
void MDArray::div_inplace(T m) {
if (shape.size() == 1) {
for (uint i = 0; i < values.size(); i++) {
values[i] /= m;
}
} else {
for (uint i = 0; i < A.size(); i++) {
A[i]->div_inplace(m);
}
}
}
template
void MDArray::div_inplace(MDArray & denum) {
if (shape.size() == 1) {
std::vector & indx = ownIndex;
for (uint i = 0; i < values.size(); i++) {
T d = denum.get(indx);
values[i] /= d;
}
} else {
for (uint i = 0; i < A.size(); i++) {
A[i]->div_inplace(denum);
}
}
}
template
void MDArray::add_inplace(MDArray & a) {
if (shape.size() == 1) {
for (uint i = 0; i < values.size(); i++) {
values[i] += a.values[i];
}
} else {
for (uint i = 0; i < A.size(); i++) {
MDArray * new_a = a.A[i];
A[i]->add_inplace(*new_a);
}
}
}
template
void MDArray::add_inplace(std::vector > & a) {
for (uint i = 0; i < A.size(); i++) {
MDArray * ref = A[i];
for (uint j = 0; j < ref->values.size(); j++) {
ref->values[j] += a[i][j];
}
}
}
template
void MDArray::add_inplace(std::vector & a) {
for (uint i = 0; i < values.size(); i++) {
values[i] += a[i];
}
}
template
void MDArray::sub_inplace(MDArray & a) {
if (shape.size() == 1) {
for (uint i = 0; i < values.size(); i++) {
values[i] -= a.values[i];
}
} else {
for (uint i = 0; i < A.size(); i++) {
MDArray * new_a = a.A[i];
A[i]->sub_inplace(*new_a);
}
}
}
template
MDArray MDArray::operator+(MDArray & a) {
MDArray r(*this);
r.add_inplace(a);
return r;
}
template
MDArray MDArray::operator-(MDArray & a) {
MDArray r(*this);
r.sub_inplace(a);
return r;
}
template
MDArray MDArray::operator/(T a) {
MDArray r(*this);
r.div_inplace(a);
return r;
}
template
void MDArray::add_inplace(T s) {
if (shape.size() == 1) {
for (uint i = 0; i < values.size(); i++) {
values[i] += s;
}
} else {
for (uint i = 0; i < A.size(); i++) {
A[i]->add_inplace(s);
}
}
}
template
void MDArray::rnd_cov(uint dim, RandomGen * rg) {
/*
Return a random covariance matrix with shape (dim x dim).
From U{http://www.sci.wsu.edu/math/faculty/genz/papers/mvn/node5.html}:
These random covariance matrices were generated with a method described in an
article by Marsaglia and Olkin, for m = 3, 4, ..., 10. This method
consists of the following steps. First, a random lower triangular matrix is
generated with entries chosen uniformly from [-1,1]. Then, the rows of this
matrix are normalized so that the sum of the squares of the elements in each
row add to one. Finally this lower triangular matrix is multiplied by it's
transpose, to produce a random covariance matrix.
Note:
Lower triangular matrix:
A matrix which is only defined at (i,j) when i greater than or equal to j.
Reference:
Marsaglia, G. and Olkin, I. (1984), Generating Correlation Matrices, SIAM
Journal of Scientific and Statistical Computing 5, pp. 470-475.
*/
std::vector sh_c;
sh_c.push_back(dim);
sh_c.push_back(dim);
MDArray c(sh_c);
for (uint i = 0; i < dim; i++) {
for (uint j = 0; j < dim; j++) {
if (i >= j) {
double d = rg->get_rand();
double r = 2.0 * (d - 0.5);
c.set(i, j, r);
}
}
}
MDArray d(c);
d.sqr_all();
for (uint i = 0; i < dim; i++) {
MDArray & values = d.get_view(i);
std::vector & v_values = values.get_values();
double s = sumvect(v_values);
MDArray view = c.get_view(i);
view.multiply_inplace(1.0 / sqrt(s));
c.set(i, view);
}
MDArray cov(c);
c.transpose();
cov.multiply_inplace(c);
copy(cov);
}
template
void MDArray::multiply_inplace(T m) {
if (shape.size() == 1) {
for (uint i = 0; i < values.size(); i++) {
values[i] *= m;
}
} else {
for (uint i = 0; i < A.size(); i++) {
A[i]->multiply_inplace(m);
}
}
}
template
void MDArray::multiply_inplace(MDArray & m) {
assert(shape.size() == 2);
assert(m.get_shape().size() == 2);
uint dim = shape.front();
MDArray cp(shape);
for (uint i = 0; i < dim; i++) {
for (uint j = 0; j < dim; j++) {
T v = 0;
for (uint k = 0; k < dim; k++) {
v += get(i, k) * m.get(k, j);
}
cp.set(i, j, v);
}
}
copy(cp);
}
template
void MDArray::transpose() {
assert(shape.size() == 2);
uint dim = shape.front();
for (uint i = 0; i < dim; i++) {
for (uint j = i + 1; j < dim; j++) {
T temp = get(i, j);
set(i, j, get(j, i));
set(j, i, temp);
}
}
}
template
void printv(std::vector v) {
std::cout << "{";
for(uint i=0; i
MDArray MDArray::swapaxes(uint axis1, uint axis2) {
const uint dims = shape.size();
assert(axis1 < dims && axis2 < dims);
if(axis1==axis2) {
MDArray new_array(*this);
return new_array;
}
// Calculate the new shape vector
std::vector new_shape(shape);
new_shape[axis1] = shape[axis2];
new_shape[axis2] = shape[axis1];
// Construct the new MDArray
MDArray new_array(new_shape);
// Calculate the permutation of the axis
std::vector permutation;
for(uint i=0; i new_index(dims);
permuteaxes(permutation, new_index, new_array);
return new_array;
}
template
MDArray MDArray::moveaxis(uint from, uint to) {
const uint dims = shape.size();
assert(from < dims && to < dims);
if(from==to) {
MDArray new_array(*this);
return new_array;
}
// Calculate the inverse permutation of the axis
std::vector inv_permutation;
for(uint i=0; i permutation(dims);
for(uint i=0; i new_shape;
for(std::vector::iterator axis=inv_permutation.begin(); axis!=inv_permutation.end(); axis++) {
new_shape.push_back(shape[*axis]);
}
// Construct the new MDArray
MDArray new_array(new_shape);
// Swap the axis
std::vector new_index(dims);
permuteaxes(permutation, new_index, new_array);
return new_array;
}
// Recursive method to permute the axes of this and return the result in new_array
// It assumes that permutation that 'permutation' is a correct permutation!
template
void MDArray::permuteaxes(const std::vector & permutation, std::vector & new_index, MDArray & new_array) {
uint depth = ownIndex.size();
uint new_depth = permutation[depth];
// If we are at full depth, copy the value to the new index in the new array
if (shape.size() == 1) {
for (uint i = 0; i < values.size(); i++) {
new_index[new_depth] = i;
new_array[new_index] = values[i];
}
}
// If we are not at a full depth, set the new index correct and call permute on all sub arrays
else {
for (uint i = 0; i < A.size(); i++) {
new_index[new_depth] = i;
A[i]->permuteaxes(permutation, new_index, new_array);
}
}
}
template