Matrix.cpp
#include "Matrix.h"
#include "Vector.h"
using namespace Robotics;
using namespace System;
using namespace System::Diagnostics;
/// 指定された大きさで生成し 零行列で初期化する [ Constructor ]
Matrix::Matrix( int row, int column )
{
// 領域を確保する
SecureDomain( row );
for( int m=0; m<row; m++ )
{
// ... 空のベクトルを生成する
m_rowVectors[ m ] = gcnew Vector( column );
}
#ifdef _DEBUG
for( int m=0; m<Row; m++ )
{
Debug::Assert(
m_rowVectors[ m ]->IsZeroVector(),
"零行列に初期化されている"
);
}
/// @note
/// 零行列の判定を行う関数は、比較用に零行列を作成するとき
/// このコンストラクタを呼び出す。よって、ここでその関数を呼び出すと
/// 再帰してしまう。
#endif
}
/// コピー元と同じ大きさで生成し それの行ベクトルで初期化する [ Copy constructor ]
Matrix::Matrix( Matrix^ other )
{
// 領域を確保する
SecureDomain( other->Row );
// 列数(行ベクトルの次元)を設定する
for( int m=0; m < other->Row; m++ )
{
// ... 行ベクトルのコピーで生成する
m_rowVectors[ m ] = gcnew Vector( other->m_rowVectors[ m ] );
}
}
/// 指定された行数で領域を確保する
void Matrix::SecureDomain( int row )
{
/// @note
/// ここでは、行ベクトルの領域を確保するだけである。
/// この後に行ベクトルのインスタンスを生成する必要がある。
Debug::Assert( 1 <= row , "行数は1以上である" );
// メモリ領域を行ベクトルとして確保する
m_rowVectors = gcnew array< Vector^ >( row );
/// @note
/// C++ の文法上、配列の初期化でデフォルトコンストラクタ以外を
/// 呼び出すことはできないため、行と列を分けて初期化している。
}
/// 行列の大きさを設定する
void Matrix::SetSize( int row, int column )
{
Debug::Assert( ( row != Row ) || ( column != Column ), "行数または列数は変更される" );
// 領域を解放する
delete m_rowVectors;
// ... 領域を確保する
SecureDomain( row );
for( int m=0; m<row; m++ )
{
// ... 空のベクトルを生成する
m_rowVectors[ m ] = gcnew Vector( column );
}
}
/// Indexer [ Property ]
Vector^ Matrix::default::get( int index )
{
Debug::Assert( ( 0 <= index ) && ( index <= Row ), "添え字は行数の範囲内である" );
// 添え字を1からとするために、補正して指定する
return m_rowVectors[ index - 1 ];
}
/// 列数を取得する [ Property ]
int Matrix::Column::get()
{
// 行ベクトルの次元数を取得する
return m_rowVectors[ 0 ]->Dimension;
}
/// 和 += [ Operator Overloading ]
Matrix^ Matrix::operator+=( Matrix^ obj )
{
Debug::Assert( IsEqualSize( obj ), "引数の行列と大きさは等しい" );
for( int m=0; m<Row; m++ )
{
m_rowVectors[ m ] += obj->m_rowVectors[ m ];
}
return this;
}
/// 差 -= [ Operator Overloading ]
Matrix^ Matrix::operator-=( Matrix^ obj )
{
Debug::Assert( IsEqualSize( obj ), "引数の行列と大きさは等しい" );
for( int m=0; m<Row; m++ )
{
m_rowVectors[ m ] -= obj->m_rowVectors[ m ];
}
return this;
}
/// スカラー積 *= [ Operator Overloading ]
Matrix^ Matrix::operator*=( double val )
{
for(int m=0; m<Row; m++ )
{
m_rowVectors[ m ] *= val;
}
return this;
}
/// 行列積 *= [ Operator Overloading ]
Matrix^ Matrix::operator*=( Matrix^ obj )
{
// 行列積 (operator*)を呼び出す
Matrix^ result = this * obj;
for( int m=0; m<Row; m++ )
{
// 行ベクトルを、インスタンスとしてコピーする
m_rowVectors[ m ] = gcnew Vector( result->m_rowVectors[ m ] );
}
return this;
}
/// 行列の和 [ Operator Overloading ]
Matrix^ Matrix::operator+( Matrix^ left, Matrix^ right )
{
Matrix^ result = gcnew Matrix( left );
result += right;
return result;
}
/// 行列の差 [ Operator Overloading ]
Matrix^ Matrix::operator-( Matrix^ left, Matrix^ right )
{
Matrix^ result = gcnew Matrix( left );
result -= right;
return result;
}
/// 行列とベクトルの積 [ Operator Overloading ]
Vector^ Matrix::operator*( Matrix^ matrix, Vector^ vector )
{
/// @note
/// ここでは、行ベクトルを列ベクトルと見なして計算している
Debug::Assert(
matrix->Column == vector->Dimension,
"行列の列数とベクトルの次数は等しい"
);
Vector^ result = gcnew Vector( matrix->Row );
for( int m=1; m<=matrix->Row; m++ )
{
// 行ごとに、ベクトルの成分との積の総和を求める
double sum = 0.0;
for( int n=1; n<=matrix->Column; n++ )
{
sum += matrix[ m ][ n ] * vector[ n ];
}
result[ m ] = sum;
}
// 誤差成分を除去する
result->RemoveErrorComponent();
return result;
}
/// ベクトルと行列の積 [ Operator Overloading ]
Vector^ Matrix::operator*( Vector^ vector, Matrix^ matrix )
{
Debug::Assert(
vector->Dimension == matrix->Column,
"ベクトルの次数と行列の列数は等しい"
);
Vector^ result = gcnew Vector( matrix->Column );
for( int n=1; n<=matrix->Column; n++ )
{
// 列ごとに、ベクトルの成分との積の総和を求める
double sum = 0.0;
for( int m=1; m<=matrix->Row; m++ )
{
sum += vector[ m ] * matrix[ m ][ n ];
}
result[ n ] = sum;
}
// 誤差成分を除去する
result->RemoveErrorComponent();
return result;
}
/// 行列積 [ Operator Overloading ]
Matrix^ Matrix::operator*( Matrix^ left, Matrix^ right )
{
Debug::Assert(
left->Column == right->Row,
"行列left の列数と行列right の行数は等しい"
);
Matrix^ result = gcnew Matrix( left->Row, right->Column );
// 行ベクトル x 行列
for( int m=1; m <= left->Row; m++ )
{
for( int r=1; r <= right->Column; r++ )
{
double sum = 0.0;
for( int n=1; n <= left->Column; n++ )
{
sum += left[ m ][ n ] * right[ n ][ r ];
}
result[ m ][ r ] = sum;
}
}
// 誤差成分を除去する
result->RemoveErrorComponent();
return result;
}
/// 等価演算子 == [ Operator Overloading ]
bool Matrix::operator==( Matrix^ left, Matrix^ right )
{
// 精度を 計算機イプシロンとして比較する
return left->Equals( right, Double::Epsilon );
}
/// 不等価演算子 != [ Operator Overloading ]
bool Matrix::operator!=( Matrix^ left, Matrix^ right )
{
// 値の等価の否定を返す
return !left->Equals( right, Double::Epsilon );
}
/// 値の等価を確認する
bool Matrix::Equals( Matrix^ obj )
{
// 精度を 計算機イプシロンとして比較する
return Equals( obj, Double::Epsilon );
}
/// 値の等価を確認する
bool Matrix::Equals( Matrix^ obj, double precision )
{
// NULLとは等価ではない
if( safe_cast< System::Object^ >( obj ) == nullptr ) return false;
Debug::Assert( IsEqualSize( obj ), "引数の行列と大きさは等しい" );
for( int m=0; m<Row; m++ )
{
// 行ベクトルごとに、精度を指定して比較する
if( !m_rowVectors[ m ]->Equals( obj->m_rowVectors[ m ], precision ) )
{
return false;
}
}
return true;
}
/// 零行列にする
void Matrix::ZeroMatrix()
{
// 全ての行ベクトルを零ベクトルにする
for( int m=0; m<Row; m++ )
{
m_rowVectors[ m ]->ZeroVector();
}
}
/// 単位行列にする
void Matrix::UnitMatrix()
{
Debug::Assert( IsSquareMatrix(), "正方行列である" );
for( int n=1; n<=Column; n++ )
{
for( int m=1; m<=Row; m++ )
{
// 対角成分ならば1.0 それ以外は0.0 とする
this[ m ][ n ] = ( m == n )? 1.0 : 0.0;
}
}
}
/// 転置行列を返す
Matrix^ Matrix::TransposedMatrix()
{
// 行数と列数を反転させた行列を作成する
Matrix^ result = gcnew Matrix( Column, Row );
for( int n=1; n<=Column; n++ )
{
for( int m=1; m<=Row; m++ )
{
// 行と列を反転させて代入する
result[ n ][ m ] = this[ m ][ n ];
}
}
return result;
}
/// 逆行列を求める
bool Matrix::InverseMatrix( Matrix^% result )
{
Debug::Assert( IsSquareMatrix(), "正方行列である" );
// 演算用の行列を生成する
Matrix^ matrix = gcnew Matrix( this );
array< int >^ informationToChangeRow; // 行交換の情報
// LU分解をする
if( !matrix->LuDecomposition( informationToChangeRow ) )
{
// LU分解できないならば 処理を中止する
return false;
}
// 自身と同じ大きさで 結果格納用の行列を生成する
result = gcnew Matrix( Row, Column );
for( int n=1; n<=Row; n++ )
{
// 最初の行から 末尾の行まで
for( int m=1; m<=Row; m++ )
{
const int row = informationToChangeRow[ m ];
double component = ( row == n )? 1.0 : 0.0;
for( int i=1; i<m; i++ )
{
// 元の行列の列と結果となる行列の行の 成分の積を減ずる
component -= matrix[ row ][ i ] * result[ i ][ n ];
}
result[ m ][ n ] = component;
}
// 末尾から 最初の行まで
for( int m=Row; m>=1; m-- )
{
const int row = informationToChangeRow[ m ];
double component = result[ m ][ n ];
for( int i=m+1; i<=Row; i++ )
{
// 元の行列の列と結果となる行列の行の 成分の積を減ずる
component -= matrix[ row ][ i ] * result[ i ][ n ];
}
result[ m ][ n ] = component / matrix[ row ][ m ];
}
}
return true;
// [ Reference ]「C言語による最新アルゴリズム事典」奥村晴彦(技術評論社) P40
}
/// LU分解 (行列を下三角行列(Lower)と上三角行列(Upper) に分解する)
bool Matrix::LuDecomposition( array< int >^% informationToChangeRow )
{
Debug::Assert( informationToChangeRow == nullptr, "結果を格納する引数は 生成されていないものである" );
Debug::Assert( IsSquareMatrix(), "正方行列である" );
// 行交換の情報を生成する
informationToChangeRow = gcnew array< int >( Row + 1 );
// ... それを初期化する
for( int n=1; n<=Row; n++ )
{
informationToChangeRow[ n ] = n;
}
// 各行について 絶対値が最大の成分を取得する
array< double >^ maxAbsolutes = GetComponentWithMaximumAbsoluteValue();
for( int n=1; n<=Row; n++ )
{
if( maxAbsolutes[ n ] == 0.0 )
{
// 最大の成分がゼロの行(零ベクトル)を含むならば LU分解はできない
return false;
}
}
for( int n=1; n<=Row; n++ )
{
int maxRow = Row; // 最大の行
double max = Double::MinValue;
for( int m=n; m<=Row; m++ )
{
// ある行より下において 絶対値の最大の逆数 x 絶対値 が最大の行を求める
const int row = informationToChangeRow[ m ];
double component = maxAbsolutes[ row ] * Math::Abs( this[ row ][ n ] );
if( max < component )
{
max = component;
maxRow = m;
}
}
const int currentRow = informationToChangeRow[ maxRow ]; // 現在の行
if( maxRow != n )
{
// 現在の行が最大でなければ 行番号を交換する
informationToChangeRow[ maxRow ] = informationToChangeRow[ n ];
informationToChangeRow[ n ] = currentRow;
}
const double diagonalComponent = this[ currentRow ][ n ]; // 対角成分
if( Math::Abs( diagonalComponent ) < Double::Epsilon )
{
// 対角成分がゼロならば LU分解はできない
return false;
}
// Gauss 消去法
for( int m=n+1; m<=Row; m++ )
{
const int row = informationToChangeRow[ m ];
this[ row ][ n ] /= diagonalComponent;
for( maxRow=n+1; maxRow<=Row; maxRow++ )
{
this[ row ][ maxRow ] -= this[ row ][ n ] * this[ currentRow ][ maxRow ];
}
}
}
return true;
// [ Reference ]「C言語による最新アルゴリズム事典」奥村晴彦(技術評論社) P387
}
/// 絶対値が最大の成分を取得する
array< double >^ Matrix::GetComponentWithMaximumAbsoluteValue()
{
array< double >^ result = gcnew array< double >( Row + 1 );
// 各行について調べる
for( int n=1; n<=Row; n++ )
{
double max = 0.0;
// 絶対値が最大の成分を探索する
for( int m=1; m<=Row; m++ )
{
const double absolute = Math::Abs( this[ n ][ m ] );
if( max < absolute )
{
// 最大値を更新する
max = absolute;
}
}
// 絶対値が最大の成分の逆数を 結果に格納する(ゼロ除算しないようにしている)
result[ n ] = ( max == 0.0 )? 0.0 : 1.0 / max;
}
return result;
}
/// 大きさは等しいか?
bool Matrix::IsEqualSize( Matrix^ obj )
{
return ( Row == obj->Row ) && ( Column == obj->Column);
}
/// 正方行列か?
bool Matrix::IsSquareMatrix()
{
return Row == Column;
}
/// 直交行列か?
bool Matrix::IsOrthogonalMatrix( double precision )
{
Debug::Assert( IsSquareMatrix(), "正方行列である" );
// 比較用の転置行列を作成する
Matrix^ matrix = TransposedMatrix();
// ... それとの行列積を求める
matrix *= this;
// ... それが単位行列となっているか?
return matrix->IsUnitMatrix( precision );
}
/// 単位行列か?
bool Matrix::IsUnitMatrix( double precision )
{
Debug::Assert( IsSquareMatrix(), "正方行列である" );
// 比較用の行列を作成する
Matrix^ compare = gcnew Matrix( Row, Column );
// ... それを単位行列とする
compare->UnitMatrix();
// ... それと等しいか比較する
return Equals( compare, precision );
}
/// 零行列か?
bool Matrix::IsZeroMatrix()
{
// 比較用の零行列を作成する
Matrix^ compare = gcnew Matrix( Row, Column );
#ifdef _DEBUG
for( int m=1; m<=compare->Row; m++ )
{
Debug::Assert(
compare[ m ]->IsZeroVector(),
"零行列に初期化されている"
);
}
#endif
return ( this == compare );
}
/// 誤差成分を除去する (微少成分の消去)
void Matrix::RemoveErrorComponent()
{
double maxOfAbsoluteValue = 0.0;
for( int m=1; m<=Row; m++ )
{
// 絶対値が最大の成分を見つける
for( int n=1; n<Column; n++ )
{
double compare = Math::Abs( this[ m ][ n ] );
if( maxOfAbsoluteValue < compare )
{
maxOfAbsoluteValue = compare;
}
}
}
if( Double::Epsilon < maxOfAbsoluteValue )
{
// 最大の成分が 計算機イプシロンより大きければ修正する
for( int m=1; m<=Row; m++ )
{
for( int n=1; n<Column; n++ )
{
// 絶対値が最大の成分と比較して相対的に小さい値を ゼロとする
if( ( Math::Abs( this[ m ][ n ] ) / maxOfAbsoluteValue ) < Tolerance )
{
this[ m ][ n ] = 0.0;
}
}
}
}
// [ Reference ]「数値計算以前」Yamada.K ( http://www.asahi-net.or.jp/~uc3k-ymd/Lesson/Section03/section03_13.html )
}
/// インスタンスの説明を文字列で返す
System::String^ Matrix::ToString()
{
String^ result;
// 全てのベクトルを 文字列として連結する
for each( Vector^ vector in m_rowVectors )
{
result += vector->ToString() + Environment::NewLine;
}
return result;
}