2021-05-07 22:05:11 -04:00

1980 lines
58 KiB
C#

//
// Matrix.cs - provides various matrix operations that work with Matrix objects or two-dimensional double arrays
//
// Author:
// Michael Becker <alcexhim@gmail.com>
// Anas Abidi (2004-12-18 "Matrix Operations Library" v2.0)
//
// Copyright (c) 2004-2020 Mike Becker's Software
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using System.Text;
namespace Neo
{
/// <summary>
/// Provides various matrix operations that work with Matrix objects or two-dimensional double arrays. The
/// '+','-','*' operators are overloaded to work with the Matrix objects.
/// </summary>
public class Matrix
{
private double[,] m_Array;
private bool mvarSafeMode = true;
/// <summary>
/// When true, prevents the <see cref="Matrix" /> from being cleared during an attempt to change the <see cref="RowCount" /> or <see cref="ColumnCount" /> properties.
/// </summary>
public bool SafeMode { get { return mvarSafeMode; } set { mvarSafeMode = value; } }
#region Class Constructor and Indexer
/// <summary>
/// Constructs an empty <see cref="Matrix" /> with the specified dimensions.
/// </summary>
/// <param name="rows">Number of rows in this matrix.</param>
/// <param name="columns">Number of columns in this matrix.</param>
public Matrix(int rows, int columns)
{
m_Array = new double[rows, columns];
}
/// <summary>
/// Constructs a matrix from the specified array object.
/// </summary>
/// <param name="array">The array from which to generate the matrix.</param>
public Matrix(double[,] array)
{
m_Array = (double[,])array.Clone();
}
/// <summary>
/// Gets or sets the element at the specified position in the matrix.
/// </summary>
/// <param name="row">The row of the element to get or set.</param>
/// <param name="column">The column of the element to get or set.</param>
public double this[int row, int column]
{
get { return m_Array[row, column]; }
set { m_Array[row, column] = value; }
}
#endregion
#region Public Properties
/// <summary>
/// The number of rows in the matrix.
/// </summary>
/// <remarks>Setting this property will delete all of the elements of the matrix and set them to zero, if <see cref="SafeMode" /> is disabled.</remarks>
public int RowCount
{
get { return m_Array.GetUpperBound(0) + 1; }
set
{
if (!mvarSafeMode)
{
m_Array = new double[value, m_Array.GetUpperBound(0)];
}
}
}
/// <summary>
/// The number of columns in the matrix.
/// </summary>
/// <remarks>Setting this property will delete all of the elements of the matrix and set them to zero, if <see cref="SafeMode" /> is disabled.</remarks>
public int ColumnCount
{
get { return m_Array.GetUpperBound(1) + 1; }
set
{
if (!mvarSafeMode)
{
m_Array = new double[m_Array.GetUpperBound(0), value];
}
}
}
/// <summary>
/// Creates a two-dimensional <see cref="System.Double" /> array from this <see cref="Matrix" />.
/// </summary>
/// <returns>A two-dimensional <see cref="System.Double" /> array populated with the contents of this <see cref="Matrix" />.</returns>
public double[,] ToArray()
{
return (double[,])m_Array.Clone();
}
#endregion
/// <summary>
/// Gets the next array index for a one-dimensional <see cref="System.Double" /> array.
/// </summary>
/// <param name="array">The <see cref="System.Double" /> array for which to find the array index.</param>
/// <param name="row">The next available row index.</param>
private static void GetNextArrayIndex(double[] array, out int row)
{
row = array.GetUpperBound(0);
}
/// <summary>
/// Gets the next array indices for a two-dimensional <see cref="System.Double" /> array.
/// </summary>
/// <param name="array">The <see cref="System.Double" /> array for which to find the array indices.</param>
/// <param name="row">The next available row index (upper bound of dimension 0).</param>
/// <param name="column">The next available column index (upper bound of dimension 1).</param>
private static void GetNextArrayIndex(double[,] array, out int row, out int column)
{
row = array.GetUpperBound(0);
column = array.GetUpperBound(1);
}
#region "Change 1D array ([n]) to/from 2D array [n,1]"
/// <summary>
/// Returns the 2D form of a 1D array. i.e. array with dimension[n] is returned as an array with dimension [n,1].
/// </summary>
/// <param name="Mat">the array to convert, with dimesion [n]</param>
/// <returns>the same array but with [n,1] dimension</returns>
public static double[,] ConvertArray(double[] Mat)
{
int Rows;
try
{
GetNextArrayIndex(Mat, out Rows);
}
catch
{
throw new MatrixNullException();
}
double[,] Sol = new double[Rows + 1, 1];
for (int i = 0; i <= Rows; i++)
{
Sol[i, 0] = Mat[i];
}
return Sol;
}
/// <summary>
/// Returns the one-dimensional form of a two-dimensional array.
/// </summary>
/// <param name="Mat">The two-dimensional array to convert</param>
/// <returns>The one-dimensional form of the given two-dimensional <see cref="System.Double" /> array</returns>
public static double[] ConvertArray(double[,] Mat)
{
int Rows;
int Cols;
//Find The dimensions !!
try { GetNextArrayIndex(Mat, out Rows, out Cols); }
catch { throw new MatrixNullException(); }
if (Cols != 0) throw new MatrixDimensionException();
double[] Sol = new double[Rows + 1];
for (int i = 0; i <= Rows; i++)
{
Sol[i] = Mat[i, 0];
}
return Sol;
}
#endregion
#region "Identity Matrix"
/// <summary>
/// Returns an Identity matrix with dimensions [n,n] in the from of an array.
/// </summary>
/// <param name="n">the no. of rows or no. cols in the matrix</param>
/// <returns>An identity Matrix with dimensions [n,n] in the form of an array</returns>
public static double[,] Identity(int n)
{
double[,] temp = new double[n, n];
for (int i = 0; i < n; i++) temp[i, i] = 1;
return temp;
}
#endregion
#region Add Matrices
/// <summary>
/// Returns the summation of two matrices represented as two-dimensional <see cref="System.Double" /> arrays. The arrays must have compatible dimensions.
/// </summary>
/// <param name="array1">First <see cref="System.Double" /> array in the summation.</param>
/// <param name="array2">Second <see cref="System.Double" /> array in the summation.</param>
/// <returns>The sum of the two <see cref="System.Double" /> arrays.</returns>
/// <exception cref="MatrixNullException"></exception>
/// <exception cref="MatrixDimensionException"></exception>
public static double[,] Add(double[,] array1, double[,] array2)
{
double[,] sum;
int i, j;
int Rows1, Cols1;
int Rows2, Cols2;
try
{
GetNextArrayIndex(array1, out Rows1, out Cols1);
GetNextArrayIndex(array2, out Rows2, out Cols2);
}
catch
{
throw new MatrixNullException();
}
if (Rows1 != Rows2 || Cols1 != Cols2) throw new MatrixDimensionException();
sum = new double[Rows1 + 1, Cols1 + 1];
for (i = 0; i <= Rows1; i++)
{
for (j = 0; j <= Cols1; j++)
{
sum[i, j] = array1[i, j] + array2[i, j];
}
}
return sum;
}
/// <summary>
/// Returns the summation of two <see cref="Matrix" /> objects with compatible dimensions.
/// </summary>
/// <param name="matrix1">First matrix in the summation</param>
/// <param name="matrix2">Second matrix in the summation</param>
/// <returns>A <see cref="Matrix" /> object representing the sum of the two <see cref="Matrix" /> objects.</returns>
public static Matrix Add(Matrix matrix1, Matrix matrix2)
{
return new Matrix(Add(matrix1.m_Array, matrix2.m_Array));
}
/// <summary>
/// Returns the summation of two matrices with compatible
/// dimensions.
/// In case of an error the error is raised as an exception.
/// </summary>
/// <param name="Mat1">First Matrix object in the summation</param>
/// <param name="Mat2">Second Matrix object in the summation</param>
/// <returns>Sum of Mat1 and Mat2 as a Matrix object</returns>
public static Matrix operator +(Matrix Mat1, Matrix Mat2)
{
return new Matrix(Add(Mat1.m_Array, Mat2.m_Array));
}
#endregion
#region Subtract Matrices
/// <summary>
/// Returns the difference of two matrices represented as two-dimensional <see cref="System.Double" /> arrays. The arrays must have compatible dimensions.
/// </summary>
/// <param name="array1">First array in the subtraction</param>
/// <param name="array2">Second array in the subtraction</param>
/// <returns>Difference of Mat1 and Mat2 as an array</returns>
/// <exception cref="MatrixNullException">Occurs when the given matrix array is null.</exception>
/// <exception cref="MatrixDimensionException">Occurs when the dimensions of the two matrix arrays do not match.</exception>
public static double[,] Subtract(double[,] array1, double[,] array2)
{
double[,] sol;
int i, j;
int Rows1, Cols1;
int Rows2, Cols2;
try
{
GetNextArrayIndex(array1, out Rows1, out Cols1);
GetNextArrayIndex(array2, out Rows2, out Cols2);
}
catch
{
throw new MatrixNullException();
}
if (Rows1 != Rows2 || Cols1 != Cols2) throw new MatrixDimensionException();
sol = new double[Rows1 + 1, Cols1 + 1];
for (i = 0; i <= Rows1; i++)
for (j = 0; j <= Cols1; j++)
{
sol[i, j] = array1[i, j] - array2[i, j];
}
return sol;
}
/// <summary>
/// Subtracts the given <see cref="Matrix" /> from the current <see cref="Matrix" />. The matrices must have compatible dimensions.
/// </summary>
/// <param name="Mat2">The <see cref="Matrix" /> to subtract from the current <see cref="Matrix" />.</param>
/// <returns>A <see cref="Matrix" /> representing the difference of <see cref="Mat2" /> and the current <see cref="Matrix" />.</returns>
public Matrix Subtract(Matrix Mat2)
{
return new Matrix(Matrix.Subtract(m_Array, Mat2.m_Array));
}
/// <summary>
/// Returns the difference of two <see cref="Matrix" /> objects with compatible dimensions.
/// </summary>
/// <param name="left"></param>
/// <param name="right"></param>
/// <returns></returns>
public static Matrix operator -(Matrix left, Matrix right)
{
return new Matrix(Subtract(left.m_Array, right.m_Array));
}
#endregion
#region Multiply Matrices
/// <summary>
/// Returns the multiplication of two matrices represented as two-dimensional <see cref="System.Double" /> arrays. The arrays must have compatible dimensions.
/// </summary>
/// <param name="array1">The first array to multiply.</param>
/// <param name="array2">The second array to multiply.</param>
/// <returns>A two-dimensional <see cref="System.Double" /> array representing the product of <see cref="array1" /> and <see cref="array2" />.</returns>
public static double[,] Multiply(double[,] array1, double[,] array2)
{
int Rows1, Cols1;
int Rows2, Cols2;
double MulAdd = 0;
try
{
GetNextArrayIndex(array1, out Rows1, out Cols1);
GetNextArrayIndex(array2, out Rows2, out Cols2);
}
catch
{
throw new MatrixNullException();
}
if (Cols1 != Rows2) throw new MatrixDimensionException();
double[,] product = new double[Rows1 + 1, Cols2 + 1];
for (int i = 0; i <= Rows1; i++)
{
for (int j = 0; j <= Cols2; j++)
{
for (int l = 0; l <= Cols1; l++)
{
MulAdd = MulAdd + array1[i, l] * array2[l, j];
}
product[i, j] = MulAdd;
MulAdd = 0;
}
}
return product;
}
/// <summary>
/// Returns the multiplication of two matrices with compatible dimensions OR the cross-product of two vectors.
/// </summary>
/// <param name="Mat1">First matrix or vector (i.e: dimension [3,1]) object in multiplication</param>
/// <param name="Mat2">Second matrix or vector (i.e: dimension [3,1]) object in multiplication</param>
/// <returns>Mat1 multiplied by Mat2 as a Matrix object</returns>
public static Matrix Multiply(Matrix Mat1, Matrix Mat2)
{
if ((Mat1.RowCount == 3) && (Mat2.RowCount == 3) &&
(Mat1.ColumnCount == 1) && (Mat1.ColumnCount == 1))
{ return new Matrix(CrossProduct(Mat1.m_Array, Mat2.m_Array)); }
else
{ return new Matrix(Multiply(Mat1.m_Array, Mat2.m_Array)); }
}
/// <summary>
/// Returns the multiplication of two matrices with compatible
/// dimensions OR the cross-product of two vectors.
/// In case of an error the error is raised as an exception.
/// </summary>
/// <param name="Mat1">
/// First matrix or vector (i.e: dimension [3,1]) object in
/// multiplication
/// </param>
/// <param name="Mat2">
/// Second matrix or vector (i.e: dimension [3,1]) object in
/// multiplication
/// </param>
/// <returns>Mat1 multiplied by Mat2 as a Matrix object</returns>
public static Matrix operator *(Matrix Mat1, Matrix Mat2)
{
/*
if ((Mat1.RowCount==3) && (Mat2.RowCount==3) &&
(Mat1.ColumnCount==1) && (Mat1.ColumnCount==1))
{
return new Matrix(CrossProduct(Mat1.m_Array,Mat2.m_Array));
}
else
{
return new Matrix(Multiply(Mat1.m_Array,Mat2.m_Array));
}
*/
Matrix matrix = new Matrix(Mat1.RowCount, Mat1.ColumnCount);
for (int i = 0; i < Mat1.RowCount; i++)
{
matrix[i, 0] = Mat1[i, 0] * Mat2[0, 0] + Mat1[i, 1] * Mat2[1, 0] + Mat1[i, 2] * Mat2[2, 0] + Mat1[i, 3] * Mat2[3, 0];
matrix[i, 1] = Mat1[i, 0] * Mat2[0, 1] + Mat1[i, 1] * Mat2[1, 1] + Mat1[i, 2] * Mat2[2, 1] + Mat1[i, 3] * Mat2[3, 1];
matrix[i, 2] = Mat1[i, 0] * Mat2[0, 2] + Mat1[i, 1] * Mat2[1, 2] + Mat1[i, 2] * Mat2[2, 2] + Mat1[i, 3] * Mat2[3, 2];
matrix[i, 3] = Mat1[i, 0] * Mat2[0, 3] + Mat1[i, 1] * Mat2[1, 3] + Mat1[i, 2] * Mat2[2, 3] + Mat1[i, 3] * Mat2[3, 3];
}
return matrix;
}
#endregion
#region Determinant of a Matrix
/// <summary>
/// Returns the determinant of a matrix represented as a two-dimensional <see cref="System.Double" /> array.
/// </summary>
/// <param name="array">The two-dimensional <see cref="System.Double" /> array whose determinant is to be found.</param>
/// <returns>A <see cref="System.Double" /> representing the determinant of the array.</returns>
public static double GetDeterminant(double[,] array)
{
int S, k, k1, i, j;
double[,] DArray;
double save, ArrayK, tmpDet;
int Rows, Cols;
try
{
DArray = (double[,])array.Clone();
GetNextArrayIndex(array, out Rows, out Cols);
}
catch
{
throw new MatrixNullException();
}
if (Rows != Cols) throw new MatrixNotSquareException();
S = Rows;
tmpDet = 1;
for (k = 0; k <= S; k++)
{
if (DArray[k, k] == 0)
{
j = k;
while ((j < S) && (DArray[k, j] == 0)) j = j + 1;
if (DArray[k, j] == 0) return 0;
else
{
for (i = k; i <= S; i++)
{
save = DArray[i, j];
DArray[i, j] = DArray[i, k];
DArray[i, k] = save;
}
}
tmpDet = -tmpDet;
}
ArrayK = DArray[k, k];
tmpDet = tmpDet * ArrayK;
if (k < S)
{
k1 = k + 1;
for (i = k1; i <= S; i++)
{
for (j = k1; j <= S; j++)
DArray[i, j] = DArray[i, j] - DArray[i, k] * (DArray[k, j] / ArrayK);
}
}
}
return tmpDet;
}
/// <summary>
/// Returns the determinant of the current <see cref="Matrix"/>.
/// </summary>
/// <returns>A <see cref="System.Double" /> representing the determinant of the current <see cref="Matrix" />.</returns>
public double GetDeterminant()
{
return GetDeterminant(m_Array);
}
#endregion
#region "Inverse of a Matrix"
/// <summary>
/// Returns the inverse of a matrix with [n,n] dimension
/// and whose determinant is not zero.
/// In case of an error the error is raised as an exception.
/// </summary>
/// <param name="Mat">
/// Array with [n,n] dimension whose inverse is to be found
/// </param>
/// <returns>Inverse of the array as an array</returns>
public static double[,] Inverse(double[,] Mat)
{
double[,] AI, Mat1;
double AIN, AF;
int Rows, Cols;
int LL, LLM, L1, L2, LC, LCA, LCB;
try
{
GetNextArrayIndex(Mat, out Rows, out Cols);
Mat1 = (double[,])Mat.Clone();
}
catch { throw new MatrixNullException(); }
if (Rows != Cols) throw new MatrixNotSquareException();
if (GetDeterminant(Mat) == 0) throw new MatrixDeterminentZeroException();
LL = Rows;
LLM = Cols;
AI = new double[LL + 1, LL + 1];
for (L2 = 0; L2 <= LL; L2++)
{
for (L1 = 0; L1 <= LL; L1++) AI[L1, L2] = 0;
AI[L2, L2] = 1;
}
for (LC = 0; LC <= LL; LC++)
{
if (Math.Abs(Mat1[LC, LC]) < 0.0000000001)
{
for (LCA = LC + 1; LCA <= LL; LCA++)
{
if (LCA == LC) continue;
if (Math.Abs(Mat1[LC, LCA]) > 0.0000000001)
{
for (LCB = 0; LCB <= LL; LCB++)
{
Mat1[LCB, LC] = Mat1[LCB, LC] + Mat1[LCB, LCA];
AI[LCB, LC] = AI[LCB, LC] + AI[LCB, LCA];
}
break;
}
}
}
AIN = 1 / Mat1[LC, LC];
for (LCA = 0; LCA <= LL; LCA++)
{
Mat1[LCA, LC] = AIN * Mat1[LCA, LC];
AI[LCA, LC] = AIN * AI[LCA, LC];
}
for (LCA = 0; LCA <= LL; LCA++)
{
if (LCA == LC) continue;
AF = Mat1[LC, LCA];
for (LCB = 0; LCB <= LL; LCB++)
{
Mat1[LCB, LCA] = Mat1[LCB, LCA] - AF * Mat1[LCB, LC];
AI[LCB, LCA] = AI[LCB, LCA] - AF * AI[LCB, LC];
}
}
}
return AI;
}
/// <summary>
/// Returns the inverse of a matrix with [n,n] dimension
/// and whose determinant is not zero.
/// In case of an error the error is raised as an exception.
/// </summary>
/// <param name="Mat">
/// Matrix object with [n,n] dimension whose inverse is to be found
/// </param>
/// <returns>Inverse of the matrix as a Matrix object</returns>
public static Matrix Inverse(Matrix Mat)
{ return new Matrix(Inverse(Mat.m_Array)); }
#endregion
#region "Transpose of a Matrix"
/// <summary>
/// Returns the transpose of a matrix.
/// In case of an error the error is raised as an exception.
/// </summary>
/// <param name="Mat">Array whose transpose is to be found</param>
/// <returns>Transpose of the array as an array</returns>
public static double[,] Transpose(double[,] Mat)
{
double[,] Tr_Mat;
int i, j, Rows, Cols;
try { GetNextArrayIndex(Mat, out Rows, out Cols); }
catch { throw new MatrixNullException(); }
Tr_Mat = new double[Cols + 1, Rows + 1];
for (i = 0; i <= Rows; i++)
for (j = 0; j <= Cols; j++) Tr_Mat[j, i] = Mat[i, j];
return Tr_Mat;
}
/// <summary>
/// Returns the transpose of a matrix.
/// In case of an error the error is raised as an exception.
/// </summary>
/// <param name="Mat">Matrix object whose transpose is to be found</param>
/// <returns>Transpose of the Matrix object as a Matrix object</returns>
public static Matrix Transpose(Matrix Mat)
{ return new Matrix(Transpose(Mat.m_Array)); }
#endregion
#region Singular Value Decomposition of a Matrix
/// <summary>
/// Evaluates the Singular Value Decomposition of a matrix,
/// returns the matrices S, U and V. Such that a given
/// Matrix = U x S x V'.
/// In case of an error the error is raised as an exception.
/// Note: This method is based on the 'Singular Value Decomposition'
/// section of Numerical Recipes in C by William H. Press,
/// Saul A. Teukolsky, William T. Vetterling and Brian P. Flannery,
/// University of Cambridge Press 1992.
/// </summary>
/// <param name="array">Array whose SVD is to be computed</param>
/// <param name="S_">An array where the S matrix is returned</param>
/// <param name="U_">An array where the U matrix is returned</param>
/// <param name="V_">An array where the V matrix is returned</param>
public static void GetSingularValueDecomposition(double[,] array, out double[,] S_, out double[,] U_, out double[,] V_)
{
int Rows, Cols;
int m, MP, n, NP;
double[] w;
double[,] A, v;
try { GetNextArrayIndex(array, out Rows, out Cols); }
catch { throw new MatrixNullException(); }
m = Rows + 1;
n = Cols + 1;
if (m < n)
{
m = n;
MP = NP = n;
}
else if (m > n)
{
n = m;
NP = MP = m;
}
else
{
MP = m;
NP = n;
}
A = new double[m + 1, n + 1];
for (int row = 1; row <= Rows + 1; row++)
for (int col = 1; col <= Cols + 1; col++)
{ A[row, col] = array[row - 1, col - 1]; }
const int NMAX = 100;
v = new double[NP + 1, NP + 1];
w = new double[NP + 1];
int k, l, nm;
int flag, i, its, j, jj;
double[,] U_temp, S_temp, V_temp;
double anorm, c, f, g, h, s, scale, x, y, z;
double[] rv1 = new double[NMAX];
l = 0;
nm = 0;
g = 0.0;
scale = 0.0;
anorm = 0.0;
for (i = 1; i <= n; i++)
{
l = i + 1;
rv1[i] = scale * g;
g = s = scale = 0.0;
if (i <= m)
{
for (k = i; k <= m; k++) scale += Math.Abs(A[k, i]);
if (scale != 0)
{
for (k = i; k <= m; k++)
{
A[k, i] /= scale;
s += A[k, i] * A[k, i];
}
f = A[i, i];
g = -Sign(Math.Sqrt(s), f);
h = f * g - s;
A[i, i] = f - g;
if (i != n)
{
for (j = l; j <= n; j++)
{
for (s = 0, k = i; k <= m; k++) s += A[k, i] * A[k, j];
f = s / h;
for (k = i; k <= m; k++) A[k, j] += f * A[k, i];
}
}
for (k = i; k <= m; k++) A[k, i] *= scale;
}
}
w[i] = scale * g;
g = s = scale = 0.0;
if (i <= m && i != n)
{
for (k = l; k <= n; k++) scale += Math.Abs(A[i, k]);
if (scale != 0)
{
for (k = l; k <= n; k++)
{
A[i, k] /= scale;
s += A[i, k] * A[i, k];
}
f = A[i, l];
g = -Sign(Math.Sqrt(s), f);
h = f * g - s;
A[i, l] = f - g;
for (k = l; k <= n; k++) rv1[k] = A[i, k] / h;
if (i != m)
{
for (j = l; j <= m; j++)
{
for (s = 0.0, k = l; k <= n; k++) s += A[j, k] * A[i, k];
for (k = l; k <= n; k++) A[j, k] += s * rv1[k];
}
}
for (k = l; k <= n; k++) A[i, k] *= scale;
}
}
anorm = Math.Max(anorm, (Math.Abs(w[i]) + Math.Abs(rv1[i])));
}
for (i = n; i >= 1; i--)
{
if (i < n)
{
if (g != 0)
{
for (j = l; j <= n; j++)
v[j, i] = (A[i, j] / A[i, l]) / g;
for (j = l; j <= n; j++)
{
for (s = 0.0, k = l; k <= n; k++) s += A[i, k] * v[k, j];
for (k = l; k <= n; k++) v[k, j] += s * v[k, i];
}
}
for (j = l; j <= n; j++) v[i, j] = v[j, i] = 0.0;
}
v[i, i] = 1.0;
g = rv1[i];
l = i;
}
for (i = n; i >= 1; i--)
{
l = i + 1;
g = w[i];
if (i < n)
for (j = l; j <= n; j++) A[i, j] = 0.0;
if (g != 0)
{
g = 1.0 / g;
if (i != n)
{
for (j = l; j <= n; j++)
{
for (s = 0.0, k = l; k <= m; k++) s += A[k, i] * A[k, j];
f = (s / A[i, i]) * g;
for (k = i; k <= m; k++) A[k, j] += f * A[k, i];
}
}
for (j = i; j <= m; j++) A[j, i] *= g;
}
else
{
for (j = i; j <= m; j++) A[j, i] = 0.0;
}
++A[i, i];
}
for (k = n; k >= 1; k--)
{
for (its = 1; its <= 30; its++)
{
flag = 1;
for (l = k; l >= 1; l--)
{
nm = l - 1;
if (Math.Abs(rv1[l]) + anorm == anorm)
{
flag = 0;
break;
}
if (Math.Abs(w[nm]) + anorm == anorm) break;
}
if (flag != 0)
{
c = 0.0;
s = 1.0;
for (i = l; i <= k; i++)
{
f = s * rv1[i];
if (Math.Abs(f) + anorm != anorm)
{
g = w[i];
h = Pythagorean(f, g);
w[i] = h;
h = 1.0 / h;
c = g * h;
s = (-f * h);
for (j = 1; j <= m; j++)
{
y = A[j, nm];
z = A[j, i];
A[j, nm] = y * c + z * s;
A[j, i] = z * c - y * s;
}
}
}
}
z = w[k];
if (l == k)
{
if (z < 0.0)
{
w[k] = -z;
for (j = 1; j <= n; j++) v[j, k] = (-v[j, k]);
}
break;
}
if (its == 30) Console.WriteLine("No convergence in 30 SVDCMP iterations");
x = w[l];
nm = k - 1;
y = w[nm];
g = rv1[nm];
h = rv1[k];
f = ((y - z) * (y + z) + (g - h) * (g + h)) / (2.0 * h * y);
g = Pythagorean(f, 1.0);
f = ((x - z) * (x + z) + h * ((y / (f + Sign(g, f))) - h)) / x;
c = s = 1.0;
for (j = l; j <= nm; j++)
{
i = j + 1;
g = rv1[i];
y = w[i];
h = s * g;
g = c * g;
z = Pythagorean(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 = 1; jj <= n; jj++)
{
x = v[jj, j];
z = v[jj, i];
v[jj, j] = x * c + z * s;
v[jj, i] = z * c - x * s;
}
z = Pythagorean(f, h);
w[j] = z;
if (z != 0)
{
z = 1.0 / z;
c = f * z;
s = h * z;
}
f = (c * g) + (s * y);
x = (c * y) - (s * g);
for (jj = 1; jj <= m; jj++)
{
y = A[jj, j];
z = A[jj, i];
A[jj, j] = y * c + z * s;
A[jj, i] = z * c - y * s;
}
}
rv1[l] = 0.0;
rv1[k] = f;
w[k] = x;
}
}
S_temp = new double[NP, NP];
V_temp = new double[NP, NP];
U_temp = new double[MP, NP];
for (i = 1; i <= NP; i++) S_temp[i - 1, i - 1] = w[i];
S_ = S_temp;
for (i = 1; i <= NP; i++)
for (j = 1; j <= NP; j++) V_temp[i - 1, j - 1] = v[i, j];
V_ = V_temp;
for (i = 1; i <= MP; i++)
for (j = 1; j <= NP; j++) U_temp[i - 1, j - 1] = A[i, j];
U_ = U_temp;
}
private static double Sign(double a, double b)
{
if (b >= 0.0) { return Math.Abs(a); }
else { return -Math.Abs(a); }
}
private static double Pythagorean(double a, double b)
{
double absa, absb;
absa = Math.Abs(a);
absb = Math.Abs(b);
if (absa > absb) return absa * Math.Sqrt(1.0 + Math.Pow((absb / absa), 2));
else return (absb == 0.0 ? 0.0 : absb * Math.Sqrt(1.0 + Math.Pow((absa / absb), 2)));
}
/// <summary>
/// Evaluates the Singular Value Decomposition of a matrix,
/// returns the matrices S, U and V. Such that a given
/// Matrix = U x S x V'.
/// In case of an error the error is raised as an exception.
/// Note: This method is based on the 'Singular Value Decomposition'
/// section of Numerical Recipes in C by William H. Press,
/// Saul A. Teukolsky, William T. Vetterling and Brian P. Flannery,
/// University of Cambridge Press 1992.
/// </summary>
/// <param name="Mat">Matrix object whose SVD is to be computed</param>
/// <param name="S">A Matrix object where the S matrix is returned</param>
/// <param name="U">A Matrix object where the U matrix is returned</param>
/// <param name="V">A Matrix object where the V matrix is returned</param>
public static void SVD(Matrix Mat, out Matrix S, out Matrix U, out Matrix V)
{
double[,] s, u, v;
GetSingularValueDecomposition(Mat.m_Array, out s, out u, out v);
S = new Matrix(s);
U = new Matrix(u);
V = new Matrix(v);
}
#endregion
#region LU Decomposition of a matrix
/// <summary>
/// Returns the LU Decomposition of a matrix.
/// the output is: lower triangular matrix L, upper
/// triangular matrix U, and permutation matrix P so that
/// P*X = L*U.
/// In case of an error the error is raised as an exception.
/// Note: This method is based on the 'LU Decomposition and Its Applications'
/// section of Numerical Recipes in C by William H. Press,
/// Saul A. Teukolsky, William T. Vetterling and Brian P. Flannery,
/// University of Cambridge Press 1992.
/// </summary>
/// <param name="Mat">Array which will be LU Decomposed</param>
/// <param name="L">An array where the lower traingular matrix is returned</param>
/// <param name="U">An array where the upper traingular matrix is returned</param>
/// <param name="P">An array where the permutation matrix is returned</param>
public static void GetLUDecomposition(double[,] Mat, out double[,] L, out double[,] U, out double[,] P)
{
double[,] A;
int i, j, k, Rows, Cols;
try
{
A = (double[,])Mat.Clone();
GetNextArrayIndex(Mat, out Rows, out Cols);
}
catch { throw new MatrixNullException(); }
if (Rows != Cols) throw new MatrixNotSquareException();
int IMAX = 0, N = Rows;
double AAMAX, Sum, Dum, TINY = 1E-20;
int[] INDX = new int[N + 1];
double[] VV = new double[N * 10];
double D = 1.0;
for (i = 0; i <= N; i++)
{
AAMAX = 0.0;
for (j = 0; j <= N; j++)
if (Math.Abs(A[i, j]) > AAMAX) AAMAX = Math.Abs(A[i, j]);
if (AAMAX == 0.0) throw new MatrixSingularException();
VV[i] = 1.0 / AAMAX;
}
for (j = 0; j <= N; j++)
{
if (j > 0)
{
for (i = 0; i <= (j - 1); i++)
{
Sum = A[i, j];
if (i > 0)
{
for (k = 0; k <= (i - 1); k++)
Sum = Sum - A[i, k] * A[k, j];
A[i, j] = Sum;
}
}
}
AAMAX = 0.0;
for (i = j; i <= N; i++)
{
Sum = A[i, j];
if (j > 0)
{
for (k = 0; k <= (j - 1); k++)
Sum = Sum - A[i, k] * A[k, j];
A[i, j] = Sum;
}
Dum = VV[i] * Math.Abs(Sum);
if (Dum >= AAMAX)
{
IMAX = i;
AAMAX = Dum;
}
}
if (j != IMAX)
{
for (k = 0; k <= N; k++)
{
Dum = A[IMAX, k];
A[IMAX, k] = A[j, k];
A[j, k] = Dum;
}
D = -D;
VV[IMAX] = VV[j];
}
INDX[j] = IMAX;
if (j != N)
{
if (A[j, j] == 0.0) A[j, j] = TINY;
Dum = 1.0 / A[j, j];
for (i = j + 1; i <= N; i++)
A[i, j] = A[i, j] * Dum;
}
}
if (A[N, N] == 0.0) A[N, N] = TINY;
int count = 0;
double[,] l = new double[N + 1, N + 1];
double[,] u = new double[N + 1, N + 1];
for (i = 0; i <= N; i++)
{
for (j = 0; j <= count; j++)
{
if (i != 0) l[i, j] = A[i, j];
if (i == j) l[i, j] = 1.0;
u[N - i, N - j] = A[N - i, N - j];
}
count++;
}
L = l;
U = u;
P = Identity(N + 1);
for (i = 0; i <= N; i++)
{
SwapRows(P, i, INDX[i]);
}
}
private static void SwapRows(double[,] Mat, int Row, int toRow)
{
int N = Mat.GetUpperBound(0);
double[,] dumArray = new double[1, N + 1];
for (int i = 0; i <= N; i++)
{
dumArray[0, i] = Mat[Row, i];
Mat[Row, i] = Mat[toRow, i];
Mat[toRow, i] = dumArray[0, i];
}
}
/// <summary>
/// Returns the LU Decomposition of a matrix.
/// the output is: lower triangular matrix L, upper
/// triangular matrix U, and permutation matrix P so that
/// P*X = L*U.
/// In case of an error the error is raised as an exception.
/// Note: This method is based on the 'LU Decomposition and Its Applications'
/// section of Numerical Recipes in C by William H. Press,
/// Saul A. Teukolsky, William T. Vetterling and Brian P. Flannery,
/// University of Cambridge Press 1992.
/// </summary>
/// <param name="Mat">Matrix object which will be LU Decomposed</param>
/// <param name="L">A Matrix object where the lower traingular matrix is returned</param>
/// <param name="U">A Matrix object where the upper traingular matrix is returned</param>
/// <param name="P">A Matrix object where the permutation matrix is returned</param>
public static void GetLUDecomposition(Matrix Mat, out Matrix L, out Matrix U, out Matrix P)
{
double[,] l, u, p;
GetLUDecomposition(Mat.m_Array, out l, out u, out p);
L = new Matrix(l);
U = new Matrix(u);
P = new Matrix(p);
}
#endregion
#region Solve system of linear equations
/// <summary>
/// Solves a set of n linear equations A.X = B, and returns X, where A is [n,n] and B is [n,1].
/// </summary>
/// <param name="arrayA">The array 'A' on the left side of the equations A.X = B</param>
/// <param name="arrayB">The array 'B' on the right side of the equations A.X = B</param>
/// <returns>Array 'X' in the system of equations A.X = B</returns>
/// <remarks>In the same manner if you need to compute: inverse(A).B, it is better to use this method instead, as it is much faster. This method is based on the 'LU Decomposition and Its Applications' section of Numerical Recipes in C by William H. Press, Saul A. Teukolsky, William T. Vetterling and Brian P. Flannery, University of Cambridge Press 1992.</remarks>
public static double[,] SolveLinearEquationSystem(double[,] arrayA, double[,] arrayB)
{
double[,] A;
double[,] B;
double SUM;
int i, ii, j, k, ll, Rows, Cols;
#region "LU Decompose"
try
{
A = (double[,])arrayA.Clone();
B = (double[,])arrayB.Clone();
GetNextArrayIndex(A, out Rows, out Cols);
}
catch { throw new MatrixNullException(); }
if (Rows != Cols) throw new MatrixNotSquareException();
if ((B.GetUpperBound(0) != Rows) || (B.GetUpperBound(1) != 0))
throw new MatrixDimensionException();
int IMAX = 0, N = Rows;
double AAMAX, Sum, Dum, TINY = 1E-20;
int[] INDX = new int[N + 1];
double[] VV = new double[N * 10];
double D = 1.0;
for (i = 0; i <= N; i++)
{
AAMAX = 0.0;
for (j = 0; j <= N; j++)
if (Math.Abs(A[i, j]) > AAMAX) AAMAX = Math.Abs(A[i, j]);
if (AAMAX == 0.0) throw new MatrixSingularException();
VV[i] = 1.0 / AAMAX;
}
for (j = 0; j <= N; j++)
{
if (j > 0)
{
for (i = 0; i <= (j - 1); i++)
{
Sum = A[i, j];
if (i > 0)
{
for (k = 0; k <= (i - 1); k++)
Sum = Sum - A[i, k] * A[k, j];
A[i, j] = Sum;
}
}
}
AAMAX = 0.0;
for (i = j; i <= N; i++)
{
Sum = A[i, j];
if (j > 0)
{
for (k = 0; k <= (j - 1); k++)
Sum = Sum - A[i, k] * A[k, j];
A[i, j] = Sum;
}
Dum = VV[i] * Math.Abs(Sum);
if (Dum >= AAMAX)
{
IMAX = i;
AAMAX = Dum;
}
}
if (j != IMAX)
{
for (k = 0; k <= N; k++)
{
Dum = A[IMAX, k];
A[IMAX, k] = A[j, k];
A[j, k] = Dum;
}
D = -D;
VV[IMAX] = VV[j];
}
INDX[j] = IMAX;
if (j != N)
{
if (A[j, j] == 0.0) A[j, j] = TINY;
Dum = 1.0 / A[j, j];
for (i = j + 1; i <= N; i++)
A[i, j] = A[i, j] * Dum;
}
}
if (A[N, N] == 0.0) A[N, N] = TINY;
#endregion
ii = -1;
for (i = 0; i <= N; i++)
{
ll = INDX[i];
SUM = B[ll, 0];
B[ll, 0] = B[i, 0];
if (ii != -1)
{
for (j = ii; j <= i - 1; j++) SUM = SUM - A[i, j] * B[j, 0];
}
else if (SUM != 0) ii = i;
B[i, 0] = SUM;
}
for (i = N; i >= 0; i--)
{
SUM = B[i, 0];
if (i < N)
{
for (j = i + 1; j <= N; j++) SUM = SUM - A[i, j] * B[j, 0];
}
B[i, 0] = SUM / A[i, i];
}
return B;
}
/// <summary>
/// Solves a set of n linear equations A.X = B, and returns X, where A is [n,n] and B is [n,1].
/// </summary>
/// <param name="matrixA">The <see cref="Matrix" /> object 'A' on the left side of the equations A.X = B</param>
/// <param name="matrixB">The <see cref="Matrix" /> object 'B' on the right side of the equations A.X = B</param>
/// <returns>Matrix object 'X' in the system of equations A.X = B</returns>
/// <remarks>In the same manner if you need to compute: inverse(A).B, it is better to use this method instead, as it is much faster. This method is based on the 'LU Decomposition and Its Applications' section of Numerical Recipes in C by William H. Press, Saul A. Teukolsky, William T. Vetterling and Brian P. Flannery, University of Cambridge Press 1992.</remarks>
public static Matrix SolveLinearEquationSystem(Matrix matrixA, Matrix matrixB)
{
return new Matrix(Matrix.SolveLinearEquationSystem(matrixA.m_Array, matrixB.m_Array));
}
#endregion
#region "Rank of a matrix"
/// <summary>
/// Returns the rank of a matrix.
/// </summary>
/// <param name="Mat">The two-dimensional <see cref="System.Double" /> array whose rank is to be found</param>
/// <returns>The rank of the array</returns>
public static int Rank(double[,] Mat)
{
int r = 0;
double[,] S, U, V;
try
{
int Rows, Cols;
GetNextArrayIndex(Mat, out Rows, out Cols);
}
catch { throw new MatrixNullException(); }
double EPS = 2.2204E-16;
GetSingularValueDecomposition(Mat, out S, out U, out V);
for (int i = 0; i <= S.GetUpperBound(0); i++)
{ if (Math.Abs(S[i, i]) > EPS) r++; }
return r;
}
/// <summary>
/// Returns the rank of a matrix.
/// In case of an error the error is raised as an exception.
/// </summary>
/// <param name="Mat">a Matrix object whose rank is to be found</param>
/// <returns>The rank of the Matrix object</returns>
public static int Rank(Matrix Mat)
{ return Rank(Mat.m_Array); }
#endregion
#region "Pseudoinverse of a matrix"
/// <summary>
/// Returns the pseudoinverse of a matrix, such that
/// X = PINV(A) produces a matrix 'X' of the same dimensions
/// as A' so that A*X*A = A, X*A*X = X.
/// In case of an error the error is raised as an exception.
/// </summary>
/// <param name="Mat">An array whose pseudoinverse is to be found</param>
/// <returns>The pseudoinverse of the array as an array</returns>
public static double[,] PINV(double[,] Mat)
{
double[,] Matrix;
int i, m, n, j, l;
double[,] S, Part_I, Part_II;
double EPS, MulAdd, Tol, Largest_Item = 0;
double[,] svdU, svdS, svdV;
try
{
Matrix = (double[,])Mat.Clone();
GetNextArrayIndex(Mat, out m, out n);
}
catch { throw new MatrixNullException(); }
GetSingularValueDecomposition(Mat, out svdS, out svdU, out svdV);
EPS = 2.2204E-16;
m++;
n++;
Part_I = new double[m, n];
Part_II = new double[m, n];
S = new Double[n, n];
MulAdd = 0;
for (i = 0; i <= svdS.GetUpperBound(0); i++)
{
if (i == 0) Largest_Item = svdS[0, 0];
if (Largest_Item < svdS[i, i]) Largest_Item = svdS[i, i];
}
if (m > n) Tol = m * Largest_Item * EPS;
else Tol = n * Largest_Item * EPS;
for (i = 0; i < n; i++) S[i, i] = svdS[i, i];
for (i = 0; i < m; i++)
{
for (j = 0; j < n; j++)
{
for (l = 0; l < n; l++)
{
if (S[l, j] > Tol) MulAdd += svdU[i, l] * (1 / S[l, j]);
}
Part_I[i, j] = MulAdd;
MulAdd = 0;
}
}
for (i = 0; i < m; i++)
{
for (j = 0; j < n; j++)
{
for (l = 0; l < n; l++)
{
MulAdd += Part_I[i, l] * svdV[j, l];
}
Part_II[i, j] = MulAdd;
MulAdd = 0;
}
}
return Transpose(Part_II);
}
/// <summary>
/// Returns the pseudoinverse of a matrix, such that
/// X = PINV(A) produces a matrix 'X' of the same dimensions
/// as A' so that A*X*A = A, X*A*X = X.
/// In case of an error the error is raised as an exception.
/// </summary>
/// <param name="Mat">a Matrix object whose pseudoinverse is to be found</param>
/// <returns>The pseudoinverse of the Matrix object as a Matrix Object</returns>
public static Matrix PINV(Matrix Mat)
{ return new Matrix(PINV(Mat.m_Array)); }
#endregion
#region Eigen Values and Vectors of Symmetric Matrix
/// <summary>
/// Returns the eigenvalues and eigenvectors of a real symmetric matrix defined as a two-dimensional <see cref="System.Double" /> array.
/// </summary>
/// <param name="array">The array whose eigenvalues and eigenvectors are to be found</param>
/// <param name="eigenValues">An array where the eigenvalues are returned</param>
/// <param name="eigenVectors">An array where the eigenvectors are returned</param>
/// <remarks>This method is based on the 'Eigenvalues and Eigenvectors of a TridiagonalMatrix' section of "Numerical Recipes in C" by William H. Press, Saul A. Teukolsky, William T. Vetterling and Brian P. Flannery, University of Cambridge Press 1992.</remarks>
public static void GetEigen(double[,] array, out double[,] eigenValues, out double[,] eigenVectors)
{
double[,] a;
int Rows, Cols;
try
{
GetNextArrayIndex(array, out Rows, out Cols);
a = (double[,])array.Clone();
}
catch { throw new MatrixNullException(); }
if (Rows != Cols) throw new MatrixNotSquareException();
int j, iq, ip, i, n, nrot;
double tresh, theta, tau, t, sm, s, h, g, c;
double[] b, z;
n = Rows;
eigenValues = new double[n + 1, 1];
eigenVectors = new double[n + 1, n + 1];
b = new double[n + 1];
z = new double[n + 1];
for (ip = 0; ip <= n; ip++)
{
for (iq = 0; iq <= n; iq++) eigenVectors[ip, iq] = 0.0;
eigenVectors[ip, ip] = 1.0;
}
for (ip = 0; ip <= n; ip++)
{
b[ip] = eigenValues[ip, 0] = a[ip, ip];
z[ip] = 0.0;
}
nrot = 0;
for (i = 0; i <= 50; i++)
{
sm = 0.0;
for (ip = 0; ip <= n - 1; ip++)
{
for (iq = ip + 1; iq <= n; iq++)
sm += Math.Abs(a[ip, iq]);
}
if (sm == 0.0)
{
return;
}
if (i < 4)
tresh = 0.2 * sm / (n * n);
else
tresh = 0.0;
for (ip = 0; ip <= n - 1; ip++)
{
for (iq = ip + 1; iq <= n; iq++)
{
g = 100.0 * Math.Abs(a[ip, iq]);
if (i > 4 && (double)(Math.Abs(eigenValues[ip, 0]) + g) == (double)Math.Abs(eigenValues[ip, 0])
&& (double)(Math.Abs(eigenValues[iq, 0]) + g) == (double)Math.Abs(eigenValues[iq, 0]))
a[ip, iq] = 0.0;
else if (Math.Abs(a[ip, iq]) > tresh)
{
h = eigenValues[iq, 0] - eigenValues[ip, 0];
if ((double)(Math.Abs(h) + g) == (double)Math.Abs(h))
t = (a[ip, iq]) / h;
else
{
theta = 0.5 * h / (a[ip, iq]);
t = 1.0 / (Math.Abs(theta) + Math.Sqrt(1.0 + theta * theta));
if (theta < 0.0) t = -t;
}
c = 1.0 / Math.Sqrt(1 + t * t);
s = t * c;
tau = s / (1.0 + c);
h = t * a[ip, iq];
z[ip] -= h;
z[iq] += h;
eigenValues[ip, 0] -= h;
eigenValues[iq, 0] += h;
a[ip, iq] = 0.0;
for (j = 0; j <= ip - 1; j++)
{
ROT(g, h, s, tau, a, j, ip, j, iq);
}
for (j = ip + 1; j <= iq - 1; j++)
{
ROT(g, h, s, tau, a, ip, j, j, iq);
}
for (j = iq + 1; j <= n; j++)
{
ROT(g, h, s, tau, a, ip, j, iq, j);
}
for (j = 0; j <= n; j++)
{
ROT(g, h, s, tau, eigenVectors, j, ip, j, iq);
}
++(nrot);
}
}
}
for (ip = 0; ip <= n; ip++)
{
b[ip] += z[ip];
eigenValues[ip, 0] = b[ip];
z[ip] = 0.0;
}
}
throw new OutOfMemoryException("Too many iterations");
}
private static void ROT(double g, double h, double s, double tau,
double[,] a, int i, int j, int k, int l)
{
g = a[i, j]; h = a[k, l];
a[i, j] = g - s * (h + g * tau);
a[k, l] = h + s * (g - h * tau);
}
/// <summary>
/// Returns the eigenvalues and eigenvectors of the current real symmetric <see cref="Matrix" /> object.
/// </summary>
/// <param name="eigenValues">A <see cref="Matrix" /> object where the eigenvalues are returned</param>
/// <param name="eigenVectors">A <see cref="Matrix" /> object where the eigenvectors are returned</param>
/// <remarks>This method is based on the 'Eigenvalues and Eigenvectors of a TridiagonalMatrix' section of Numerical Recipes in C by William H. Press, Saul A. Teukolsky, William T. Vetterling and Brian P. Flannery, University of Cambridge Press 1992.</remarks>
public void GetEigen(out Matrix eigenValues, out Matrix eigenVectors)
{
double[,] values, vectors;
GetEigen(m_Array, out values, out vectors);
eigenValues = new Matrix(values);
eigenVectors = new Matrix(vectors);
}
#endregion
#region "Multiply a matrix or a vector with a scalar quantity"
/// <summary>
/// Returns the multiplication of a matrix or a vector (i.e
/// dimension [3,1]) with a scalar quantity.
/// In case of an error the error is raised as an exception.
/// </summary>
/// <param name="Value">The scalar value to multiply the array</param>
/// <param name="Mat">Array which is to be multiplied by a scalar</param>
/// <returns>The multiplication of the scalar and the array as an array</returns>
public static double[,] ScalarMultiply(double Value, double[,] Mat)
{
int i, j, Rows, Cols;
double[,] sol;
try { GetNextArrayIndex(Mat, out Rows, out Cols); }
catch { throw new MatrixNullException(); }
sol = new double[Rows + 1, Cols + 1];
for (i = 0; i <= Rows; i++)
for (j = 0; j <= Cols; j++)
sol[i, j] = Mat[i, j] * Value;
return sol;
}
/// <summary>
/// Returns the multiplication of a matrix or a vector (i.e
/// dimension [3,1]) with a scalar quantity.
/// In case of an error the error is raised as an exception.
/// </summary>
/// <param name="Value">The scalar value to multiply the array</param>
/// <param name="Mat">Matrix which is to be multiplied by a scalar</param>
/// <returns>The multiplication of the scalar and the array as an array</returns>
public static Matrix ScalarMultiply(double Value, Matrix Mat)
{ return new Matrix(ScalarMultiply(Value, Mat.m_Array)); }
/// <summary>
/// Returns the multiplication of a matrix or a vector (i.e
/// dimension [3,1]) with a scalar quantity.
/// In case of an error the error is raised as an exception.
/// </summary>
/// <param name="Mat">Matrix object which is to be multiplied by a scalar</param>
/// <param name="Value">The scalar value to multiply the Matrix object</param>
/// <returns>
/// The multiplication of the scalar and the Matrix object as a
/// Matrix object
/// </returns>
public static Matrix operator *(Matrix Mat, double Value)
{ return new Matrix(ScalarMultiply(Value, Mat.m_Array)); }
/// <summary>
/// Returns the multiplication of a matrix or a vector (i.e
/// dimension [3,1]) with a scalar quantity.
/// In case of an error the error is raised as an exception.
/// </summary>
/// <param name="Value">The scalar value to multiply the Matrix object</param>
/// <param name="Mat">Matrix object which is to be multiplied by a scalar</param>
/// <returns>
/// The multiplication of the scalar and the Matrix object as a
/// Matrix object
/// </returns>
public static Matrix operator *(double Value, Matrix Mat)
{ return new Matrix(ScalarMultiply(Value, Mat.m_Array)); }
#endregion
#region "Divide a matrix or a vector with a scalar quantity"
/// <summary>
/// Returns the division of a matrix or a vector (i.e
/// dimension [3,1]) by a scalar quantity.
/// In case of an error the error is raised as an exception.
/// </summary>
/// <param name="Value">The scalar value to divide the array with</param>
/// <param name="Mat">Array which is to be divided by a scalar</param>
/// <returns>The division of the array and the scalar as an array</returns>
public static double[,] ScalarDivide(double Value, double[,] Mat)
{
int i, j, Rows, Cols;
double[,] sol;
try { GetNextArrayIndex(Mat, out Rows, out Cols); }
catch { throw new MatrixNullException(); }
sol = new double[Rows + 1, Cols + 1];
for (i = 0; i <= Rows; i++)
for (j = 0; j <= Cols; j++)
sol[i, j] = Mat[i, j] / Value;
return sol;
}
/// <summary>
/// Returns the division of a matrix or a vector (i.e
/// dimension [3,1]) by a scalar quantity.
/// In case of an error the error is raised as an exception.
/// </summary>
/// <param name="Value">The scalar value to divide the array with</param>
/// <param name="Mat">Matrix which is to be divided by a scalar</param>
/// <returns>The division of the array and the scalar as an array</returns>
public static Matrix ScalarDivide(double Value, Matrix Mat)
{ return new Matrix(ScalarDivide(Value, Mat.m_Array)); }
/// <summary>
/// Returns the division of a matrix or a vector (i.e
/// dimension [3,1]) by a scalar quantity.
/// In case of an error the error is raised as an exception.
/// </summary>
/// <param name="Value">The scalar value to divide the Matrix object with</param>
/// <param name="Mat">Matrix object which is to be divided by a scalar</param>
/// <returns>
/// The division of the Matrix object and the scalar as a Matrix object
/// </returns>
public static Matrix operator /(Matrix Mat, double Value)
{ return new Matrix(ScalarDivide(Value, Mat.m_Array)); }
#endregion
#region "Vectors Cross Product"
/// <summary>
/// Returns the cross product of two vectors whose
/// dimensions should be [3] or [3,1].
/// In case of an error the error is raised as an exception.
/// </summary>
/// <param name="V1">First vector array (dimension [3]) in the cross product</param>
/// <param name="V2">Second vector array (dimension [3]) in the cross product</param>
/// <returns>Cross product of V1 and V2 as an array (dimension [3])</returns>
public static double[] CrossProduct(double[] V1, double[] V2)
{
double i, j, k;
double[] sol = new double[2];
int Rows1;
int Rows2;
try
{
GetNextArrayIndex(V1, out Rows1);
GetNextArrayIndex(V2, out Rows2);
}
catch
{
throw new MatrixNullException();
}
if (Rows1 != 2) throw new MatrixDimensionException();
if (Rows2 != 2) throw new MatrixDimensionException();
i = V1[1] * V2[2] - V1[2] * V2[1];
j = V1[2] * V2[0] - V1[0] * V2[2];
k = V1[0] * V2[1] - V1[1] * V2[0];
sol[0] = i; sol[1] = j; sol[2] = k;
return sol;
}
/// <summary>
/// Returns the cross product of two vectors whose
/// dimensions should be [3] or [3x1].
/// In case of an error the error is raised as an exception.
/// </summary>
/// <param name="V1">First vector array (dimensions [3,1]) in the cross product</param>
/// <param name="V2">Second vector array (dimensions [3,1]) in the cross product</param>
/// <returns>Cross product of V1 and V2 as an array (dimension [3,1])</returns>
public static double[,] CrossProduct(double[,] V1, double[,] V2)
{
double i, j, k;
double[,] sol = new double[3, 1];
int Rows1, Cols1;
int Rows2, Cols2;
try
{
GetNextArrayIndex(V1, out Rows1, out Cols1);
GetNextArrayIndex(V2, out Rows2, out Cols2);
}
catch { throw new MatrixNullException(); }
if (Rows1 != 2 || Cols1 != 0) throw new MatrixDimensionException();
if (Rows2 != 2 || Cols2 != 0) throw new MatrixDimensionException();
i = V1[1, 0] * V2[2, 0] - V1[2, 0] * V2[1, 0];
j = V1[2, 0] * V2[0, 0] - V1[0, 0] * V2[2, 0];
k = V1[0, 0] * V2[1, 0] - V1[1, 0] * V2[0, 0];
sol[0, 0] = i; sol[1, 0] = j; sol[2, 0] = k;
return sol;
}
/// <summary>
/// Returns the cross product of two vectors whose
/// dimensions should be [3] or [3x1].
/// In case of an error the error is raised as an exception.
/// </summary>
/// <param name="V1">First Matrix (dimensions [3,1]) in the cross product</param>
/// <param name="V2">Second Matrix (dimensions [3,1]) in the cross product</param>
/// <returns>Cross product of V1 and V2 as a matrix (dimension [3,1])</returns>
public static Matrix CrossProduct(Matrix V1, Matrix V2)
{ return (new Matrix((CrossProduct(V1.m_Array, V2.m_Array)))); }
#endregion
#region Vectors Dot Product
/// <summary>
/// Returns the dot product of two vectors whose
/// dimensions should be [3] or [3,1].
/// </summary>
/// <param name="V1">First vector array (dimension [3]) in the dot product</param>
/// <param name="V2">Second vector array (dimension [3]) in the dot product</param>
/// <returns>Dot product of V1 and V2</returns>
public static double DotProduct(double[] V1, double[] V2)
{
int Rows1;
int Rows2;
int Cols1;
int Cols2;
try
{
GetNextArrayIndex(V1, out Rows1);
GetNextArrayIndex(V2, out Rows2);
}
catch { throw new MatrixNullException(); }
if (Rows1 != 2) throw new MatrixDimensionException();
if (Rows2 != 2) throw new MatrixDimensionException();
return (V1[0] * V2[0] + V1[1] * V2[1] + V1[2] * V2[2]);
}
/// <summary>
/// Returns the dot product of two vectors whose
/// dimensions should be [3] or [3,1].
/// In case of an error the error is raised as an exception.
/// </summary>
/// <param name="V1">First vector array (dimension [3,1]) in the dot product</param>
/// <param name="V2">Second vector array (dimension [3,1]) in the dot product</param>
/// <returns>Dot product of V1 and V2</returns>
public static double DotProduct(double[,] V1, double[,] V2)
{
int Rows1, Cols1;
int Rows2, Cols2;
try
{
GetNextArrayIndex(V1, out Rows1, out Cols1);
GetNextArrayIndex(V2, out Rows2, out Cols2);
}
catch { throw new MatrixNullException(); }
if (Rows1 != 2 || Cols1 != 0) throw new MatrixVectorDimensionException();
if (Rows2 != 2 || Cols2 != 0) throw new MatrixVectorDimensionException();
return (V1[0, 0] * V2[0, 0] + V1[1, 0] * V2[1, 0] + V1[2, 0] * V2[2, 0]);
}
/// <summary>
/// Returns the dot product of two vectors whose
/// dimensions should be [3] or [3,1].
/// In case of an error the error is raised as an exception.
/// </summary>
/// <param name="V1">First Matrix object (dimension [3,1]) in the dot product</param>
/// <param name="V2">Second Matrix object (dimension [3,1]) in the dot product</param>
/// <returns>Dot product of V1 and V2</returns>
public static double DotProduct(Matrix V1, Matrix V2)
{ return (DotProduct(V1.m_Array, V2.m_Array)); }
#endregion
#region Vector Magnitude Calculation
/// <summary>
/// Returns the magnitude of a vector whose dimension is [3] or [3,1].
/// </summary>
/// <param name="V">The vector array (dimension [3]) whose magnitude is to be found</param>
/// <returns>The magnitude of the vector array</returns>
public static double GetVectorMagnitude(double[] V)
{
int Rows;
try
{
GetNextArrayIndex(V, out Rows);
}
catch
{
throw new MatrixNullException();
}
if (Rows != 2) throw new MatrixDimensionException();
return Math.Sqrt(V[0] * V[0] + V[1] * V[1] + V[2] * V[2]);
}
/// <summary>
/// Returns the magnitude of a <see cref="Matrix" /> vector represented as a two-dimensional <see cref="System.Double" /> array whose dimension is [3] or [3,1].
/// </summary>
/// <param name="array">The two-dimensional <see cref="System.Double" /> array representing the vector of dimension [3,1] whose magnitude is to be found.</param>
/// <returns>The magnitude of the <see cref="System.Double" /> vector array</returns>
public static double GetVectorMagnitude(double[,] array)
{
int Rows, Cols;
try
{
GetNextArrayIndex(array, out Rows, out Cols);
}
catch
{
throw new MatrixNullException();
}
if (Rows != 2 || Cols != 0) throw new MatrixVectorDimensionException();
return Math.Sqrt(array[0, 0] * array[0, 0] + array[1, 0] * array[1, 0] + array[2, 0] * array[2, 0]);
}
/// <summary>
/// Returns the magnitude of a <see cref="Matrix" /> vector whose dimension is [3] or [3,1].
/// </summary>
/// <returns>The magnitude of the <see cref="Matrix" /> vector</returns>
public double GetVectorMagnitude()
{
return (Matrix.GetVectorMagnitude(m_Array));
}
#endregion
#region Comparison
/// <summary>
/// Determines whether two specified two-dimensional <see cref="System.Double" /> arrays are equal in value.
/// </summary>
/// <param name="Mat1">First array in equality check</param>
/// <param name="Mat2">Second array in equality check</param>
/// <returns>True if both two-dimensional <see cref="System.Double" /> arrays are equal; false otherwise.</returns>
public static bool IsEqual(double[,] Mat1, double[,] Mat2)
{
double eps = 1E-14;
int Rows1, Cols1;
int Rows2, Cols2;
try
{
GetNextArrayIndex(Mat1, out Rows1, out Cols1);
GetNextArrayIndex(Mat2, out Rows2, out Cols2);
}
catch
{
throw new MatrixNullException();
}
if (Rows1 != Rows2 || Cols1 != Cols2) throw new MatrixDimensionException();
for (int i = 0; i <= Rows1; i++)
{
for (int j = 0; j <= Cols1; j++)
{
if (Math.Abs(Mat1[i, j] - Mat2[i, j]) > eps) return false;
}
}
return true;
}
/// <summary>
/// Determines whether two <see cref="Matrix" /> objects of equal dimensions are equal in value.
/// </summary>
/// <param name="Mat1">First Matrix object in equality check</param>
/// <param name="Mat2">Second Matrix object in equality check</param>
/// <returns>True if the two <see cref="Matrix" /> objects are equal; false otherwise.</returns>
public static bool operator ==(Matrix Mat1, Matrix Mat2)
{
return IsEqual(Mat1.m_Array, Mat2.m_Array);
}
/// <summary>
/// Determines whether two <see cref="Matrix" /> objects of equal dimensions are not equal in value.
/// </summary>
/// <param name="Mat1">First Matrix object in equality check</param>
/// <param name="Mat2">Second Matrix object in equality check</param>
/// <returns>True if the two <see cref="Matrix" /> objects are not equal; false otherwise.</returns>
public static bool operator !=(Matrix Mat1, Matrix Mat2)
{
return (!IsEqual(Mat1.m_Array, Mat2.m_Array));
}
/// <summary>
/// Tests whether the specified object is a <see cref="Matrix" /> object and, if so, whether it is identical to the current <see cref="Matrix" /> object.
/// </summary>
/// <param name="obj">The <see cref="System.Object" /> to compare with the current <see cref="Matrix" /> object</param>
/// <returns>True if <see cref="obj" /> is a <see cref="Matrix" /> object identical to the current <see cref="Matrix" /> object; otherwise, false.</returns>
public override bool Equals(Object obj)
{
try
{
return (this == (Matrix)obj);
}
catch
{
return false;
}
}
#endregion
public static Matrix Identity()
{
Matrix matOut = new Matrix(4, 4);
matOut[0, 1] = 0.0;
matOut[0, 2] = 0.0;
matOut[0, 3] = 0.0;
matOut[1, 0] = 0.0;
matOut[1, 2] = 0.0;
matOut[1, 3] = 0.0;
matOut[2, 0] = 0.0;
matOut[2, 1] = 0.0;
matOut[2, 3] = 0.0;
matOut[3, 0] = 0.0;
matOut[3, 1] = 0.0;
matOut[3, 2] = 0.0;
matOut[0, 0] = 1.0;
matOut[1, 1] = 1.0;
matOut[2, 2] = 1.0;
matOut[3, 3] = 1.0;
return matOut;
}
public static Matrix Lerp(Matrix input1, Matrix input2, float lerpValue)
{
Matrix matOut = new Matrix(input1.RowCount, input1.ColumnCount);
float fT = 1.0f - lerpValue;
for (int i = 0; i < matOut.RowCount; i++)
{
for (int j = 0; j < matOut.ColumnCount; j++)
{
matOut[i, j] = input1[i, j] * lerpValue + input2[i, j] * fT;
}
}
return matOut;
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.Append("{ ");
for (int i = 0; i < RowCount; i++)
{
sb.Append("{ ");
for (int j = 0; j < ColumnCount; j++)
{
sb.Append(this[i, j].ToString());
if (j < ColumnCount - 1)
{
sb.Append(", ");
}
}
sb.Append("}");
if (i < RowCount - 1)
{
sb.Append(", ");
}
}
sb.Append(" }");
return sb.ToString();
}
}
}