基於 intel MKL 的對稱矩陣特徵值求解器

        直接調用 intel Math Kernel Library 是比較麻煩的,而第三方的線性代數計算庫往往效率不佳。於是我模仿 Eigen 的風格對 intel MKL 函數做了簡單的封裝。以下代碼中的 EigenSolver 類能夠輕鬆愉快高效地求解實對稱矩陣的特徵值和特徵向量,也可以高效地求解逆矩陣。爲了效率優先,特徵值求解與逆矩陣求解都是直接在原矩陣上進行,不保存原來的矩陣。

/*
* Symmetric Dense Matrix Class
*/

#ifndef MATRIX_CLASS_H
#define MATRIX_CLASS_H

#include <iostream>
#include <iomanip>
#include "mkl_lapacke.h"

class EigenSolver;
class Matrix;

class Matrix
{
public:
	Matrix(std::size_t rVal = 0)
	{
		_Rows = rVal;
		_Cols = _Rows;
		_Data = new double[_Rows * _Cols];
	}
	Matrix(std::size_t rVal, std::size_t cVal)
	{
		_Rows = rVal;
		_Cols = cVal;
		_Data = new double[_Rows * _Cols];
	}
	Matrix(const Matrix& rhs)
	{
		_Rows = rhs._Rows;
		_Cols = rhs._Cols;
		_Data = new double[_Rows * _Cols];
		for(std::size_t ix = 0; ix != _Rows * _Cols; ++ix)
		{
			*(_Data + ix) = *(rhs._Data + ix);
		}
	}
	// the deconstructor function
	~Matrix()
	{
		delete[] _Data;
	}
	void resize(std::size_t rVal, std::size_t cVal)
	{
		delete[] _Data;
		_Rows = rVal;
		_Cols = cVal;
		_Data = new double[_Rows * _Cols];
	}
	// member functions
	std::size_t rows() const
	{
		return _Rows;
	}
	std::size_t cols() const
	{
		return _Cols;
	}
	// inverse matrix
	void inverse()
	{
		int *ipiv = new int[_Rows * _Cols];
		LAPACKE_dsytrf(LAPACK_ROW_MAJOR, 'U', _Rows, _Data, _Rows, ipiv);
		LAPACKE_dsytri(LAPACK_ROW_MAJOR, 'U', _Rows, _Data, _Rows, ipiv);
		delete[] ipiv;
		for(std::size_t j = 0; j < _Cols; ++j)
		{
			for(std::size_t i = j + 1; i < _Rows; ++i)
				_Data[i * _Cols + j] = _Data[j * _Cols + i];
		}
	}
	// overloaded operations
	Matrix& operator=(const Matrix& rhs)
	{
		resize(rhs._Rows, rhs._Cols);
		for(std::size_t ix = 0; ix != _Rows * _Cols; ++ix)
		{
			*(_Data + ix) = *(rhs._Data + ix);
		}
		return *this;
	}
	Matrix& operator*=(double scala)
	{
		for(std::size_t ix = 0; ix != _Rows * _Cols; ++ix)
		{
			*(_Data + ix) *= scala;
		}
		return *this;
	}
	Matrix& operator+=(const Matrix& mat)
	{
		for(std::size_t ix = 0; ix != _Rows * _Cols; ++ix)
		{
			*(_Data + ix) += *(mat._Data + ix);
		}
		return *this;
	}
	Matrix& operator-=(const Matrix& mat)
	{
		for(std::size_t ix = 0; ix != _Rows * _Cols; ++ix)
		{
			*(_Data + ix) -= *(mat._Data + ix);
		}
		return *this;
	}
	Matrix operator-() const
	{
		Matrix mat(_Rows, _Cols);
		for(std::size_t ix = 0; ix != _Rows * _Cols; ++ix)
		{
			*(mat._Data + ix) = 0 - *(_Data + ix);
		}
		return mat;
	}
	double& operator()(std::size_t rVal, std::size_t cVal)
	{
		return _Data[rVal*_Cols + cVal];
	}
	const double& operator()(std::size_t rVal, std::size_t cVal) const
	{
		return _Data[rVal*_Cols + cVal];
	}
	// friend classes and functions
	friend class EigenSolver;
	friend Matrix operator+(const Matrix&, const Matrix&);
	friend Matrix operator-(const Matrix&, const Matrix&);
	friend Matrix operator*(const Matrix&, double);
	friend Matrix operator*(double, const Matrix&);	
	// for test
	friend std::ostream& operator<<(std::ostream&, const Matrix&);
protected:
	std::size_t _Rows;
	std::size_t _Cols;
	double* _Data; // row major array
};

Matrix operator+(const Matrix& lhs, const Matrix& rhs)
{
	Matrix sum(lhs._Rows, lhs._Cols);
	for(std::size_t ix = 0; ix != lhs._Rows * lhs._Cols; ++ix)
	{
		*(sum._Data + ix) = *(lhs._Data + ix) + *(rhs._Data + ix);
	}
	return sum;
}

Matrix operator-(const Matrix& lhs, const Matrix& rhs)
{
	Matrix diff(lhs._Rows, lhs._Cols);
	for(std::size_t ix = 0; ix != lhs._Rows * lhs._Cols; ++ix)
	{
		*(diff._Data + ix) = *(lhs._Data + ix) - *(rhs._Data + ix);
	}
	return diff;
}

Matrix operator*(const Matrix& mat, double scala)
{
	Matrix prod(mat._Rows, mat._Cols);
	for(std::size_t ix = 0; ix != mat._Rows * mat._Cols; ++ix)
	{
		*(prod._Data + ix) = *(mat._Data + ix) * scala;
	}
	return prod;
}

Matrix operator*(double scala, const Matrix& mat)
{
	return mat * scala;
}

std::ostream& operator<<(std::ostream& os, const Matrix& mat)
{
	for(std::size_t i = 0; i != mat.rows(); ++i)
	{
		for(std::size_t j = 0; j != mat.cols(); ++j)
		{
			os << mat(i, j) << "\t";
		}
		os << std::endl;
	}
	return os;
}

class EigenSolver
{
public:
	EigenSolver(Matrix& mat) : _EigenVecs(mat)
	{
		_Size = mat.rows();
		_EigenVals.resize(_Size, 1);
		LAPACKE_dsyevd(LAPACK_ROW_MAJOR, 'V', 'U', _Size, _EigenVecs._Data, _Size, _EigenVals._Data);
	}
	const Matrix& eigenvalues() const
	{
		return _EigenVals;
	}
	const Matrix& eigenvectors() const
	{
		return _EigenVecs;
	}
	friend std::ostream& operator<<(std::ostream&, const EigenSolver&);
private:
	std::size_t _Size;
	Matrix _EigenVals;
	Matrix& _EigenVecs;
};

std::ostream& operator<<(std::ostream& os, const EigenSolver& eigen)
{
	for(std::size_t ix = 0; ix != eigen._Size; ++ix)
	{
		os << "eigen value: " << eigen._EigenVals(ix, 0) << std::endl;
		os << "corresponding eigen vector:" << std::endl;
		for(std::size_t j = 0; j != eigen._Size; ++j)
		{
			os << eigen._EigenVecs(j, ix) << std::endl;
		}
	}
	return os;
}

#endif

        輸出結果爲

------------------------------------
--> mat:
1	3	6	7	
3	2	5	2	
6	5	1	0	
7	2	0	4	
------------------------------------
eigen value: -7.71991
corresponding eigen vector:
-0.679611
-0.170638
0.565471
0.435033
eigen value: -1.9569
corresponding eigen vector:
-0.41358
0.759732
-0.445459
0.230925
eigen value: 3.92217
corresponding eigen vector:
-0.122992
0.457809
0.530801
-0.70252
eigen value: 13.7546
corresponding eigen vector:
0.593257
0.42907
0.44728
0.513698

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章