diff --git a/src/MathNet.Numerics.5.1.ReSharper b/src/MathNet.Numerics.5.1.ReSharper
index abc2b585..934aa504 100644
--- a/src/MathNet.Numerics.5.1.ReSharper
+++ b/src/MathNet.Numerics.5.1.ReSharper
@@ -13,7 +13,8 @@
Wishart
Wikipedia
Marsaglia
-Xorshift
+Xorshift
+λ
diff --git a/src/Numerics/Distributions/Discrete/Binomial.cs b/src/Numerics/Distributions/Discrete/Binomial.cs
index ce31e666..0e5cc658 100644
--- a/src/Numerics/Distributions/Discrete/Binomial.cs
+++ b/src/Numerics/Distributions/Discrete/Binomial.cs
@@ -414,7 +414,7 @@ namespace MathNet.Numerics.Distributions
}
///
- /// Samples an array of Bernoulli distributed random variables.
+ /// Samples an array of Binomially distributed random variables.
///
/// a sequence of successes in N trials.
public IEnumerable Samples()
diff --git a/src/Numerics/Distributions/Discrete/Poisson.cs b/src/Numerics/Distributions/Discrete/Poisson.cs
new file mode 100644
index 00000000..f3fc4b22
--- /dev/null
+++ b/src/Numerics/Distributions/Discrete/Poisson.cs
@@ -0,0 +1,399 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+// http://mathnetnumerics.codeplex.com
+// Copyright (c) 2009-2010 Math.NET
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+namespace MathNet.Numerics.Distributions
+{
+ using System;
+ using System.Collections.Generic;
+ using Properties;
+
+ ///
+ /// Pseudo-random generation of poisson distributed deviates.
+ ///
+ ///
+ /// Distribution is described at Wikipedia - Poisson distribution.
+ /// Knuth's method is used to generate Poisson distributed random variables.
+ /// f(x) = exp(-λ)*λ^x/x!;
+ ///
+ public class Poisson : IDiscreteDistribution
+ {
+ ///
+ /// The Poisson distribution parameter λ.
+ ///
+ private double _lambda;
+
+ ///
+ /// The distribution's random number generator.
+ ///
+ private Random _random;
+
+ ///
+ /// Gets or sets the Poisson distribution parameter λ.
+ ///
+ public double Lambda
+ {
+ get
+ {
+ return _lambda;
+ }
+
+ set
+ {
+ SetParameters(value);
+ }
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The Poisson distribution parameter λ.
+ /// If is equal or less then 0.0.
+ public Poisson(double lambda)
+ {
+ SetParameters(lambda);
+ RandomSource = new Random();
+ }
+
+ ///
+ /// Sets the parameters of the distribution after checking their validity.
+ ///
+ /// The mean (λ) of the distribution.
+ /// When the parameters don't pass the function.
+ private void SetParameters(double lambda)
+ {
+ if (Control.CheckDistributionParameters && !IsValidParameterSet(lambda))
+ {
+ throw new ArgumentOutOfRangeException(Resources.InvalidDistributionParameters);
+ }
+
+ _lambda = lambda;
+ }
+
+ ///
+ /// Checks whether the parameters of the distribution are valid.
+ ///
+ /// The mean (λ) of the distribution.
+ /// true when the parameters are valid, false otherwise.
+ private static bool IsValidParameterSet(double lambda)
+ {
+ return lambda > 0.00;
+ }
+
+ ///
+ /// Returns a that represents this instance.
+ ///
+ ///
+ /// A that represents this instance.
+ ///
+ public override string ToString()
+ {
+ return "Poisson(λ = " + _lambda + ")";
+ }
+
+ #region IDistribution Members
+
+ ///
+ /// Gets or sets the random number generator which is used to draw random samples.
+ ///
+ public Random RandomSource
+ {
+ get
+ {
+ return _random;
+ }
+
+ set
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException();
+ }
+
+ _random = value;
+ }
+ }
+
+ ///
+ /// Gets the mean of the distribution.
+ ///
+ public double Mean
+ {
+ get
+ {
+ return _lambda;
+ }
+ }
+
+ ///
+ /// Gets the variance of the distribution.
+ ///
+ public double Variance
+ {
+ get
+ {
+ return _lambda;
+ }
+ }
+
+ ///
+ /// Gets the standard deviation of the distribution.
+ ///
+ public double StdDev
+ {
+ get
+ {
+ return Math.Sqrt(_lambda);
+ }
+ }
+
+ ///
+ /// Gets the entropy of the distribution.
+ ///
+ /// Approximation, see Wikipedia Poisson distribution
+ public double Entropy
+ {
+ get
+ {
+ return (0.5 * Math.Log(2 * Constants.Pi * Constants.E * _lambda)) - (1.0 / (12.0 * _lambda)) - (1.0 / (24.0 * _lambda * _lambda)) - (19.0 / (360.0 * _lambda * _lambda * _lambda));
+ }
+ }
+
+ ///
+ /// Gets the skewness of the distribution.
+ ///
+ public double Skewness
+ {
+ get
+ {
+ return 1.0 / Math.Sqrt(_lambda);
+ }
+ }
+
+ ///
+ /// Gets the smallest element in the domain of the distributions which can be represented by an integer.
+ ///
+ public int Minimum
+ {
+ get
+ {
+ return 0;
+ }
+ }
+
+ ///
+ /// Gets the largest element in the domain of the distributions which can be represented by an integer.
+ ///
+ public int Maximum
+ {
+ get
+ {
+ return int.MaxValue;
+ }
+ }
+
+ ///
+ /// Computes the cumulative distribution function of the Poisson distribution.
+ ///
+ /// The location at which to compute the cumulative density.
+ /// the cumulative density at .
+ public double CumulativeDistribution(double x)
+ {
+ return 1.0 - SpecialFunctions.GammaLowerRegularized(x + 1, _lambda);
+ }
+
+ #endregion
+
+ #region IDiscreteDistribution Members
+
+ ///
+ /// Gets the mode of the distribution.
+ ///
+ public int Mode
+ {
+ get
+ {
+ return (int)Math.Floor(_lambda);
+ }
+ }
+
+ ///
+ /// Gets the median of the distribution.
+ ///
+ /// Approximation, see Wikipedia Poisson distribution
+ public int Median
+ {
+ get
+ {
+ return (int)Math.Floor(_lambda + (1.0 / 3.0) - (0.02 / _lambda));
+ }
+ }
+
+ ///
+ /// Computes values of the probability mass function.
+ ///
+ /// The location in the domain where we want to evaluate the probability mass function.
+ /// the probability mass at location .
+ public double Probability(int k)
+ {
+ return Math.Exp(-_lambda + (k * Math.Log(_lambda)) - SpecialFunctions.FactorialLn(k));
+ }
+
+ ///
+ /// Computes values of the log probability mass function.
+ ///
+ /// The location in the domain where we want to evaluate the log probability mass function.
+ /// the log probability mass at location .
+ public double ProbabilityLn(int k)
+ {
+ return -_lambda + (k * Math.Log(_lambda)) - SpecialFunctions.FactorialLn(k);
+ }
+
+ ///
+ /// Samples a Poisson distributed random variable.
+ ///
+ /// A sample from the Poisson distribution.
+ public int Sample()
+ {
+ return DoSample(RandomSource, _lambda);
+ }
+
+ ///
+ /// Samples an array of Poisson distributed random variables.
+ ///
+ /// a sequence of successes in N trials.
+ public IEnumerable Samples()
+ {
+ while (true)
+ {
+ yield return DoSample(RandomSource, _lambda);
+ }
+ }
+
+ #endregion
+
+ ///
+ /// Samples a Poisson distributed random variable.
+ ///
+ /// The random number generator to use.
+ /// The Poisson distribution parameter λ.
+ /// A sample from the Poisson distribution.
+ public static int Sample(Random rnd, double lambda)
+ {
+ if (Control.CheckDistributionParameters && !IsValidParameterSet(lambda))
+ {
+ throw new ArgumentOutOfRangeException(Resources.InvalidDistributionParameters);
+ }
+
+ return DoSample(rnd, lambda);
+ }
+
+ ///
+ /// Samples a sequence of Poisson distributed random variables.
+ ///
+ /// The random number generator to use.
+ /// The Poisson distribution parameter λ.
+ /// a sequence of samples from the distribution.
+ public static IEnumerable Samples(Random rnd, double lambda)
+ {
+ if (Control.CheckDistributionParameters && !IsValidParameterSet(lambda))
+ {
+ throw new ArgumentOutOfRangeException(Resources.InvalidDistributionParameters);
+ }
+
+ while (true)
+ {
+ yield return DoSample(rnd, lambda);
+ }
+ }
+
+ ///
+ /// Generates one sample from the Poisson distribution.
+ ///
+ /// The random source to use.
+ /// The Poisson distribution parameter λ.
+ /// A random sample from the Poisson distribution.
+ private static int DoSample(Random rnd, double lambda)
+ {
+ return (lambda < 30.0) ? DoSampleShort(rnd, lambda) : DoSampleLarge(rnd, lambda);
+ }
+
+ ///
+ /// Generates one sample from the Poisson distribution by Knuth's method.
+ ///
+ /// The random source to use.
+ /// The Poisson distribution parameter λ.
+ /// A random sample from the Poisson distribution.
+ private static int DoSampleShort(Random rnd, double lambda)
+ {
+ var limit = Math.Exp(-lambda);
+ var count = 0;
+ for (var product = rnd.NextDouble(); product >= limit; product *= rnd.NextDouble())
+ {
+ count++;
+ }
+
+ return count;
+ }
+
+ ///
+ /// Generates one sample from the Poisson distribution by "Rejection method PA".
+ ///
+ /// The random source to use.
+ /// The Poisson distribution parameter λ.
+ /// A random sample from the Poisson distribution.
+ /// "Rejection method PA" from "The Computer Generation of Poisson Random Variables" by A. C. Atkinson,
+ /// Journal of the Royal Statistical Society Series C (Applied Statistics) Vol. 28, No. 1. (1979)
+ /// The article is on pages 29-35. The algorithm given here is on page 32.
+ private static int DoSampleLarge(Random rnd, double lambda)
+ {
+ var c = 0.767 - (3.36 / lambda);
+ var beta = Math.PI / Math.Sqrt(3.0 * lambda);
+ var alpha = beta * lambda;
+ var k = Math.Log(c) - lambda - Math.Log(beta);
+
+ for (;;)
+ {
+ var u = rnd.NextDouble();
+ var x = (alpha - Math.Log((1.0 - u) / u)) / beta;
+ var n = (int)Math.Floor(x + 0.5);
+ if (n < 0)
+ {
+ continue;
+ }
+
+ var v = rnd.NextDouble();
+ var y = alpha - (beta * x);
+ var temp = 1.0 + Math.Exp(y);
+ var lhs = y + Math.Log(v / (temp * temp));
+ var rhs = k + (n * Math.Log(lambda)) - SpecialFunctions.FactorialLn(n);
+ if (lhs <= rhs)
+ {
+ return n;
+ }
+ }
+ }
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Complex/DenseVector.cs b/src/Numerics/LinearAlgebra/Complex/DenseVector.cs
index cb30a7b9..c6537664 100644
--- a/src/Numerics/LinearAlgebra/Complex/DenseVector.cs
+++ b/src/Numerics/LinearAlgebra/Complex/DenseVector.cs
@@ -28,6 +28,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex
{
using System;
using System.Collections.Generic;
+ using System.Linq;
using System.Numerics;
using Distributions;
using Generic;
@@ -1185,6 +1186,11 @@ namespace MathNet.Numerics.LinearAlgebra.Complex
index => Data[index].Magnitude);
}
+ if (2.0 == p)
+ {
+ return Data.Aggregate(Complex.Zero, SpecialFunctions.Hypotenuse).Magnitude;
+ }
+
if (Double.IsPositiveInfinity(p))
{
return CommonParallel.Select(
diff --git a/src/Numerics/LinearAlgebra/Complex/Factorization/DenseEvd.cs b/src/Numerics/LinearAlgebra/Complex/Factorization/DenseEvd.cs
new file mode 100644
index 00000000..94820088
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Complex/Factorization/DenseEvd.cs
@@ -0,0 +1,964 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+// http://mathnetnumerics.codeplex.com
+//
+// Copyright (c) 2009-2010 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization
+{
+ using System;
+ using System.Numerics;
+ using Generic;
+ using Generic.Factorization;
+ using Properties;
+
+ ///
+ /// Eigenvalues and eigenvectors of a complex matrix.
+ ///
+ ///
+ /// If A is hermitan, then A = V*D*V' where the eigenvalue matrix D is
+ /// diagonal and the eigenvector matrix V is hermitan.
+ /// I.e. A = V*D*V' and V*VH=I.
+ /// If A is not symmetric, then the eigenvalue matrix D is block diagonal
+ /// with the real eigenvalues in 1-by-1 blocks and any complex eigenvalues,
+ /// lambda + i*mu, in 2-by-2 blocks, [lambda, mu; -mu, lambda]. The
+ /// columns of V represent the eigenvectors in the sense that A*V = V*D,
+ /// i.e. A.Multiply(V) equals V.Multiply(D). The matrix V may be badly
+ /// conditioned, or even singular, so the validity of the equation
+ /// A = V*D*Inverse(V) depends upon V.cond().
+ ///
+ public class DenseEvd : Evd
+ {
+ ///
+ /// Initializes a new instance of the class. This object will compute the
+ /// the eigenvalue decomposition when the constructor is called and cache it's decomposition.
+ ///
+ /// The matrix to factor.
+ /// If is null.
+ /// If EVD algorithm failed to converge with matrix .
+ public DenseEvd(DenseMatrix matrix)
+ {
+ if (matrix == null)
+ {
+ throw new ArgumentNullException("matrix");
+ }
+
+ if (matrix.RowCount != matrix.ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSquare);
+ }
+
+ var order = matrix.RowCount;
+
+ // Initialize matricies for eigenvalues and eigenvectors
+ MatrixEv = DenseMatrix.Identity(order);
+ MatrixD = matrix.CreateMatrix(order, order);
+ VectorEv = new DenseVector(order);
+
+ IsSymmetric = true;
+
+ for (var i = 0; i < order & IsSymmetric; i++)
+ {
+ for (var j = 0; j < order & IsSymmetric; j++)
+ {
+ IsSymmetric &= matrix[i, j] == matrix[j, i].Conjugate();
+ }
+ }
+
+ if (IsSymmetric)
+ {
+ var matrixCopy = matrix.ToArray();
+ var tau = new Complex[order];
+ var d = new double[order];
+ var e = new double[order];
+
+ SymmetricTridiagonalize(matrixCopy, d, e, tau, order);
+ SymmetricDiagonalize(((DenseMatrix)MatrixEv).Data, d, e, order);
+ SymmetricUntridiagonalize(((DenseMatrix)MatrixEv).Data, matrixCopy, tau, order);
+
+ for (var i = 0; i < order; i++)
+ {
+ VectorEv[i] = new Complex(d[i], e[i]);
+ }
+ }
+ else
+ {
+ var matrixH = matrix.ToArray();
+ NonsymmetricReduceToHessenberg(((DenseMatrix)MatrixEv).Data, matrixH, order);
+ NonsymmetricReduceHessenberToRealSchur(((DenseVector)VectorEv).Data, ((DenseMatrix)MatrixEv).Data, matrixH, order);
+ }
+
+ MatrixD.SetDiagonal(VectorEv);
+ }
+
+ ///
+ /// Reduces a complex hermitian matrix to a real symmetric tridiagonal matrix using unitary similarity transformations.
+ ///
+ /// Source matrix to reduce
+ /// Output: Arrays for internal storage of real parts of eigenvalues
+ /// Output: Arrays for internal storage of imaginary parts of eigenvalues
+ /// Output: Arrays that contains further information about the transformations.
+ /// Order of initial matrix
+ /// This is derived from the Algol procedures HTRIDI by
+ /// Smith, Boyle, Dongarra, Garbow, Ikebe, Klema, Moler, and Wilkinson, Handbook for
+ /// Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
+ /// Fortran subroutine in EISPACK.
+ private static void SymmetricTridiagonalize(Complex[,] matrixA, double[] d, double[] e, Complex[] tau, int order)
+ {
+ double hh;
+ tau[order - 1] = Complex.One;
+
+ for (var i = 0; i < order; i++)
+ {
+ d[i] = matrixA[i, i].Real;
+ }
+
+ // Householder reduction to tridiagonal form.
+ for (var i = order - 1; i > 0; i--)
+ {
+ // Scale to avoid under/overflow.
+ var scale = 0.0;
+ var h = 0.0;
+
+ for (var k = 0; k < i; k++)
+ {
+ scale = scale + Math.Abs(matrixA[i, k].Real) + Math.Abs(matrixA[i, k].Imaginary);
+ }
+
+ if (scale == 0.0)
+ {
+ tau[i - 1] = Complex.One;
+ e[i] = 0.0;
+ }
+ else
+ {
+ for (var k = 0; k < i; k++)
+ {
+ matrixA[i, k] /= scale;
+ h += matrixA[i, k].MagnitudeSquared();
+ }
+
+ Complex g = Math.Sqrt(h);
+ e[i] = scale * g.Real;
+
+ Complex temp;
+ var f = matrixA[i, i - 1];
+ if (f.Magnitude != 0)
+ {
+ temp = -(matrixA[i, i - 1].Conjugate() * tau[i].Conjugate()) / f.Magnitude;
+ h += f.Magnitude * g.Real;
+ g = 1.0 + (g / f.Magnitude);
+ matrixA[i, i - 1] *= g;
+ }
+ else
+ {
+ temp = -tau[i].Conjugate();
+ matrixA[i, i - 1] = g;
+ }
+
+ if ((f.Magnitude == 0) || (i != 1))
+ {
+ f = Complex.Zero;
+ for (var j = 0; j < i; j++)
+ {
+ var tmp = Complex.Zero;
+
+ // Form element of A*U.
+ for (var k = 0; k <= j; k++)
+ {
+ tmp += matrixA[j, k] * matrixA[i, k].Conjugate();
+ }
+
+ for (var k = j + 1; k <= i - 1; k++)
+ {
+ tmp += matrixA[k, j].Conjugate() * matrixA[i, k].Conjugate();
+ }
+
+ // Form element of P
+ tau[j] = tmp / h;
+ f += (tmp / h) * matrixA[i, j];
+ }
+
+ hh = f.Real / (h + h);
+
+ // Form the reduced A.
+ for (var j = 0; j < i; j++)
+ {
+ f = matrixA[i, j].Conjugate();
+ g = tau[j] - (hh * f);
+ tau[j] = g.Conjugate();
+
+ for (var k = 0; k <= j; k++)
+ {
+ matrixA[j, k] -= (f * tau[k]) + (g * matrixA[i, k]);
+ }
+ }
+ }
+
+ for (var k = 0; k < i; k++)
+ {
+ matrixA[i, k] *= scale;
+ }
+
+ tau[i - 1] = temp.Conjugate();
+ }
+
+ hh = d[i];
+ d[i] = matrixA[i, i].Real;
+ matrixA[i, i] = new Complex(hh, scale * Math.Sqrt(h));
+ }
+
+ hh = d[0];
+ d[0] = matrixA[0, 0].Real;
+ matrixA[0, 0] = hh;
+ e[0] = 0.0;
+ }
+
+ ///
+ /// Symmetric tridiagonal QL algorithm.
+ ///
+ /// Data array of matrix V (eigenvectors)
+ /// Arrays for internal storage of real parts of eigenvalues
+ /// Arrays for internal storage of imaginary parts of eigenvalues
+ /// Order of initial matrix
+ /// This is derived from the Algol procedures tql2, by
+ /// Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
+ /// Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
+ /// Fortran subroutine in EISPACK.
+ private static void SymmetricDiagonalize(Complex[] dataEv, double[] d, double[] e, int order)
+ {
+ const int Maxiter = 1000;
+
+ for (var i = 1; i < order; i++)
+ {
+ e[i - 1] = e[i];
+ }
+
+ e[order - 1] = 0.0;
+
+ var f = 0.0;
+ var tst1 = 0.0;
+ var eps = Precision.DoubleMachinePrecision;
+ for (var l = 0; l < order; l++)
+ {
+ // Find small subdiagonal element
+ tst1 = Math.Max(tst1, Math.Abs(d[l]) + Math.Abs(e[l]));
+ var m = l;
+ while (m < order)
+ {
+ if (Math.Abs(e[m]) <= eps * tst1)
+ {
+ break;
+ }
+
+ m++;
+ }
+
+ // If m == l, d[l] is an eigenvalue,
+ // otherwise, iterate.
+ if (m > l)
+ {
+ var iter = 0;
+ do
+ {
+ iter = iter + 1; // (Could check iteration count here.)
+
+ // Compute implicit shift
+ var g = d[l];
+ var p = (d[l + 1] - g) / (2.0 * e[l]);
+ var r = SpecialFunctions.Hypotenuse(p, 1.0);
+ if (p < 0)
+ {
+ r = -r;
+ }
+
+ d[l] = e[l] / (p + r);
+ d[l + 1] = e[l] * (p + r);
+
+ var dl1 = d[l + 1];
+ var h = g - d[l];
+ for (var i = l + 2; i < order; i++)
+ {
+ d[i] -= h;
+ }
+
+ f = f + h;
+
+ // Implicit QL transformation.
+ p = d[m];
+ var c = 1.0;
+ var c2 = c;
+ var c3 = c;
+ var el1 = e[l + 1];
+ var s = 0.0;
+ var s2 = 0.0;
+ for (var i = m - 1; i >= l; i--)
+ {
+ c3 = c2;
+ c2 = c;
+ s2 = s;
+ g = c * e[i];
+ h = c * p;
+ r = SpecialFunctions.Hypotenuse(p, e[i]);
+ e[i + 1] = s * r;
+ s = e[i] / r;
+ c = p / r;
+ p = (c * d[i]) - (s * g);
+ d[i + 1] = h + (s * ((c * g) + (s * d[i])));
+
+ // Accumulate transformation.
+ for (var k = 0; k < order; k++)
+ {
+ h = dataEv[((i + 1) * order) + k].Real;
+ dataEv[((i + 1) * order) + k] = (s * dataEv[(i * order) + k].Real) + (c * h);
+ dataEv[(i * order) + k] = (c * dataEv[(i * order) + k].Real) - (s * h);
+ }
+ }
+
+ p = (-s) * s2 * c3 * el1 * e[l] / dl1;
+ e[l] = s * p;
+ d[l] = c * p;
+
+ // Check for convergence. If too many iterations have been performed,
+ // throw exception that Convergence Failed
+ if (iter >= Maxiter)
+ {
+ throw new ArgumentException(Resources.ConvergenceFailed);
+ }
+ }
+ while (Math.Abs(e[l]) > eps * tst1);
+ }
+
+ d[l] = d[l] + f;
+ e[l] = 0.0;
+ }
+
+ // Sort eigenvalues and corresponding vectors.
+ for (var i = 0; i < order - 1; i++)
+ {
+ var k = i;
+ var p = d[i];
+ for (var j = i + 1; j < order; j++)
+ {
+ if (d[j] < p)
+ {
+ k = j;
+ p = d[j];
+ }
+ }
+
+ if (k != i)
+ {
+ d[k] = d[i];
+ d[i] = p;
+ for (var j = 0; j < order; j++)
+ {
+ p = dataEv[(i * order) + j].Real;
+ dataEv[(i * order) + j] = dataEv[(k * order) + j];
+ dataEv[(k * order) + j] = p;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Determines eigenvectors by undoing the symmetric tridiagonalize transformation
+ ///
+ /// Data array of matrix V (eigenvectors)
+ /// Previously tridiagonalized matrix by .
+ /// Contains further information about the transformations
+ /// Input matrix order
+ /// This is derived from the Algol procedures HTRIBK, by
+ /// by Smith, Boyle, Dongarra, Garbow, Ikebe, Klema, Moler, and Wilkinson, Handbook for
+ /// Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
+ /// Fortran subroutine in EISPACK.
+ private static void SymmetricUntridiagonalize(Complex[] dataEv, Complex[,] matrixA, Complex[] tau, int order)
+ {
+ for (var i = 0; i < order; i++)
+ {
+ for (var j = 0; j < order; j++)
+ {
+ dataEv[(j * order) + i] = dataEv[(j * order) + i].Real * tau[i].Conjugate();
+ }
+ }
+
+ // Recover and apply the Householder matrices.
+ for (var i = 1; i < order; i++)
+ {
+ var h = matrixA[i, i].Imaginary;
+ if (h != 0)
+ {
+ for (var j = 0; j < order; j++)
+ {
+ var s = Complex.Zero;
+ for (var k = 0; k < i; k++)
+ {
+ s += dataEv[(j * order) + k] * matrixA[i, k];
+ }
+
+ s = (s / h) / h;
+
+ for (var k = 0; k < i; k++)
+ {
+ dataEv[(j * order) + k] -= s * matrixA[i, k].Conjugate();
+ }
+ }
+ }
+ }
+ }
+
+ ///
+ /// Nonsymmetric reduction to Hessenberg form.
+ ///
+ /// Data array of matrix V (eigenvectors)
+ /// Array for internal storage of nonsymmetric Hessenberg form.
+ /// Order of initial matrix
+ /// This is derived from the Algol procedures orthes and ortran,
+ /// by Martin and Wilkinson, Handbook for Auto. Comp.,
+ /// Vol.ii-Linear Algebra, and the corresponding
+ /// Fortran subroutines in EISPACK.
+ private static void NonsymmetricReduceToHessenberg(Complex[] dataEv, Complex[,] matrixH, int order)
+ {
+ var ort = new Complex[order];
+
+ for (var m = 1; m < order - 1; m++)
+ {
+ // Scale column.
+ var scale = 0.0;
+ for (var i = m; i < order; i++)
+ {
+ scale += Math.Abs(matrixH[i, m - 1].Real) + Math.Abs(matrixH[i, m - 1].Imaginary);
+ }
+
+ if (scale != 0.0)
+ {
+ // Compute Householder transformation.
+ var h = 0.0;
+ for (var i = order - 1; i >= m; i--)
+ {
+ ort[i] = matrixH[i, m - 1] / scale;
+ h += ort[i].MagnitudeSquared();
+ }
+
+ var g = Math.Sqrt(h);
+ if (ort[m].Magnitude != 0)
+ {
+ h = h + (ort[m].Magnitude * g);
+ g /= ort[m].Magnitude;
+ ort[m] = (1.0 + g) * ort[m];
+ }
+ else
+ {
+ ort[m] = g;
+ matrixH[m, m - 1] = scale;
+ }
+
+ // Apply Householder similarity transformation
+ // H = (I-u*u'/h)*H*(I-u*u')/h)
+ for (var j = m; j < order; j++)
+ {
+ var f = Complex.Zero;
+ for (var i = order - 1; i >= m; i--)
+ {
+ f += ort[i].Conjugate() * matrixH[i, j];
+ }
+
+ f = f / h;
+ for (var i = m; i < order; i++)
+ {
+ matrixH[i, j] -= f * ort[i];
+ }
+ }
+
+ for (var i = 0; i < order; i++)
+ {
+ var f = Complex.Zero;
+ for (var j = order - 1; j >= m; j--)
+ {
+ f += ort[j] * matrixH[i, j];
+ }
+
+ f = f / h;
+ for (var j = m; j < order; j++)
+ {
+ matrixH[i, j] -= f * ort[j].Conjugate();
+ }
+ }
+
+ ort[m] = scale * ort[m];
+ matrixH[m, m - 1] *= -g;
+ }
+ }
+
+ // Accumulate transformations (Algol's ortran).
+ for (var i = 0; i < order; i++)
+ {
+ for (var j = 0; j < order; j++)
+ {
+ dataEv[(j * order) + i] = i == j ? Complex.One : Complex.Zero;
+ }
+ }
+
+ for (var m = order - 2; m >= 1; m--)
+ {
+ if (matrixH[m, m - 1] != Complex.Zero && ort[m] != Complex.Zero)
+ {
+ var norm = (matrixH[m, m - 1].Real * ort[m].Real) + (matrixH[m, m - 1].Imaginary * ort[m].Imaginary);
+
+ for (var i = m + 1; i < order; i++)
+ {
+ ort[i] = matrixH[i, m - 1];
+ }
+
+ for (var j = m; j < order; j++)
+ {
+ var g = Complex.Zero;
+ for (var i = m; i < order; i++)
+ {
+ g += ort[i].Conjugate() * dataEv[(j * order) + i];
+ }
+
+ // Double division avoids possible underflow
+ g /= norm;
+ for (var i = m; i < order; i++)
+ {
+ dataEv[(j * order) + i] += g * ort[i];
+ }
+ }
+ }
+ }
+
+ // Create real subdiagonal elements.
+ for (var i = 1; i < order; i++)
+ {
+ if (matrixH[i, i - 1].Imaginary != 0.0)
+ {
+ var y = matrixH[i, i - 1] / matrixH[i, i - 1].Magnitude;
+ matrixH[i, i - 1] = matrixH[i, i - 1].Magnitude;
+ for (var j = i; j < order; j++)
+ {
+ matrixH[i, j] *= y.Conjugate();
+ }
+
+ for (var j = 0; j <= Math.Min(i + 1, order - 1); j++)
+ {
+ matrixH[j, i] *= y;
+ }
+
+ for (var j = 0; j < order; j++)
+ {
+ dataEv[(i * order) + j] *= y;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Nonsymmetric reduction from Hessenberg to real Schur form.
+ ///
+ /// Data array of the eigenvectors
+ /// Data array of matrix V (eigenvectors)
+ /// Array for internal storage of nonsymmetric Hessenberg form.
+ /// Order of initial matrix
+ /// This is derived from the Algol procedure hqr2,
+ /// by Martin and Wilkinson, Handbook for Auto. Comp.,
+ /// Vol.ii-Linear Algebra, and the corresponding
+ /// Fortran subroutine in EISPACK.
+ private static void NonsymmetricReduceHessenberToRealSchur(Complex[] vectorV, Complex[] dataEv, Complex[,] matrixH, int order)
+ {
+ // Initialize
+ var n = order - 1;
+ var eps = Precision.DoubleMachinePrecision;
+
+ double norm;
+ Complex s, x, y, z, exshift = Complex.Zero;
+
+ // Outer loop over eigenvalue index
+ var iter = 0;
+ while (n >= 0)
+ {
+ // Look for single small sub-diagonal element
+ var l = n;
+ while (l > 0)
+ {
+ var tst1 = Math.Abs(matrixH[l - 1, l - 1].Real) + Math.Abs(matrixH[l - 1, l - 1].Imaginary) + Math.Abs(matrixH[l, l].Real) + Math.Abs(matrixH[l, l].Imaginary);
+ if (Math.Abs(matrixH[l, l - 1].Real) < eps * tst1)
+ {
+ break;
+ }
+
+ l--;
+ }
+
+ // Check for convergence
+ // One root found
+ if (l == n)
+ {
+ matrixH[n, n] += exshift;
+ vectorV[n] = matrixH[n, n];
+ n--;
+ iter = 0;
+ }
+ else
+ {
+ // Form shift
+ if (iter != 10 && iter != 20)
+ {
+ s = matrixH[n, n];
+ x = matrixH[n - 1, n] * matrixH[n, n - 1].Real;
+
+ if (x.Real != 0.0 || x.Imaginary != 0.0)
+ {
+ y = (matrixH[n - 1, n - 1] - s) / 2.0;
+ z = ((y * y) + x).SquareRoot();
+ if ((y.Real * z.Real) + (y.Imaginary * z.Imaginary) < 0.0)
+ {
+ z *= -1.0;
+ }
+
+ x /= y + z;
+ s = s - x;
+ }
+ }
+ else
+ {
+ // Form exceptional shift
+ s = Math.Abs(matrixH[n, n - 1].Real) + Math.Abs(matrixH[n - 1, n - 2].Real);
+ }
+
+ for (var i = 0; i <= n; i++)
+ {
+ matrixH[i, i] -= s;
+ }
+
+ exshift += s;
+ iter++;
+
+ // Reduce to triangle (rows)
+ for (var i = l + 1; i <= n; i++)
+ {
+ s = matrixH[i, i - 1].Real;
+ norm = SpecialFunctions.Hypotenuse(matrixH[i - 1, i - 1].Magnitude, s.Real);
+ x = matrixH[i - 1, i - 1] / norm;
+ vectorV[i - 1] = x;
+ matrixH[i - 1, i - 1] = norm;
+ matrixH[i, i - 1] = new Complex(0.0, s.Real / norm);
+
+ for (var j = i; j < order; j++)
+ {
+ y = matrixH[i - 1, j];
+ z = matrixH[i, j];
+ matrixH[i - 1, j] = (x.Conjugate() * y) + (matrixH[i, i - 1].Imaginary * z);
+ matrixH[i, j] = (x * z) - (matrixH[i, i - 1].Imaginary * y);
+ }
+ }
+
+ s = matrixH[n, n];
+ if (s.Imaginary != 0.0)
+ {
+ s /= matrixH[n, n].Magnitude;
+ matrixH[n, n] = matrixH[n, n].Magnitude;
+
+ for (var j = n + 1; j < order; j++)
+ {
+ matrixH[n, j] *= s.Conjugate();
+ }
+ }
+
+ // Inverse operation (columns).
+ for (var j = l + 1; j <= n; j++)
+ {
+ x = vectorV[j - 1];
+ for (var i = 0; i <= j; i++)
+ {
+ z = matrixH[i, j];
+ if (i != j)
+ {
+ y = matrixH[i, j - 1];
+ matrixH[i, j - 1] = (x * y) + (matrixH[j, j - 1].Imaginary * z);
+ }
+ else
+ {
+ y = matrixH[i, j - 1].Real;
+ matrixH[i, j - 1] = new Complex((x.Real * y.Real) - (x.Imaginary * y.Imaginary) + (matrixH[j, j - 1].Imaginary * z.Real), matrixH[i, j - 1].Imaginary);
+ }
+
+ matrixH[i, j] = (x.Conjugate() * z) - (matrixH[j, j - 1].Imaginary * y);
+ }
+
+ for (var i = 0; i < order; i++)
+ {
+ y = dataEv[((j - 1) * order) + i];
+ z = dataEv[(j * order) + i];
+ dataEv[((j - 1) * order) + i] = (x * y) + (matrixH[j, j - 1].Imaginary * z);
+ dataEv[(j * order) + i] = (x.Conjugate() * z) - (matrixH[j, j - 1].Imaginary * y);
+ }
+ }
+
+ if (s.Imaginary != 0.0)
+ {
+ for (var i = 0; i <= n; i++)
+ {
+ matrixH[i, n] *= s;
+ }
+
+ for (var i = 0; i < order; i++)
+ {
+ dataEv[(n * order) + i] *= s;
+ }
+ }
+ }
+ }
+
+ // All roots found.
+ // Backsubstitute to find vectors of upper triangular form
+ norm = 0.0;
+ for (var i = 0; i < order; i++)
+ {
+ for (var j = i; j < order; j++)
+ {
+ norm = Math.Max(norm, Math.Abs(matrixH[i, j].Real) + Math.Abs(matrixH[i, j].Imaginary));
+ }
+ }
+
+ if (order == 1)
+ {
+ return;
+ }
+
+ if (norm == 0.0)
+ {
+ return;
+ }
+
+ for (n = order - 1; n > 0; n--)
+ {
+ x = vectorV[n];
+ matrixH[n, n] = 1.0;
+
+ for (var i = n - 1; i >= 0; i--)
+ {
+ z = 0.0;
+ for (var j = i + 1; j <= n; j++)
+ {
+ z += matrixH[i, j] * matrixH[j, n];
+ }
+
+ y = x - vectorV[i];
+ if (y.Real == 0.0 && y.Imaginary == 0.0)
+ {
+ y = eps * norm;
+ }
+
+ matrixH[i, n] = z / y;
+
+ // Overflow control
+ var tr = Math.Abs(matrixH[i, n].Real) + Math.Abs(matrixH[i, n].Imaginary);
+ if ((eps * tr) * tr > 1)
+ {
+ for (var j = i; j <= n; j++)
+ {
+ matrixH[j, n] = matrixH[j, n] / tr;
+ }
+ }
+ }
+ }
+
+ // Back transformation to get eigenvectors of original matrix
+ for (var j = order - 1; j > 0; j--)
+ {
+ for (var i = 0; i < order; i++)
+ {
+ z = Complex.Zero;
+ for (var k = 0; k <= j; k++)
+ {
+ z += dataEv[(k * order) + i] * matrixH[k, j];
+ }
+
+ dataEv[(j * order) + i] = z;
+ }
+ }
+ }
+
+ ///
+ /// Solves a system of linear equations, AX = B, with A SVD factorized.
+ ///
+ /// The right hand side , B.
+ /// The left hand side , X.
+ public override void Solve(Matrix input, Matrix result)
+ {
+ // Check for proper arguments.
+ if (input == null)
+ {
+ throw new ArgumentNullException("input");
+ }
+
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ // The solution X should have the same number of columns as B
+ if (input.ColumnCount != result.ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension);
+ }
+
+ // The dimension compatibility conditions for X = A\B require the two matrices A and B to have the same number of rows
+ if (VectorEv.Count != input.RowCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension);
+ }
+
+ // The solution X row dimension is equal to the column dimension of A
+ if (VectorEv.Count != result.RowCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension);
+ }
+
+ if (IsSymmetric)
+ {
+ var order = VectorEv.Count;
+ var tmp = new Complex[order];
+
+ for (var k = 0; k < order; k++)
+ {
+ for (var j = 0; j < order; j++)
+ {
+ Complex value = 0.0;
+ if (j < order)
+ {
+ for (var i = 0; i < order; i++)
+ {
+ value += ((DenseMatrix)MatrixEv).Data[(j * order) + i].Conjugate() * input.At(i, k);
+ }
+
+ value /= VectorEv[j].Real;
+ }
+
+ tmp[j] = value;
+ }
+
+ for (var j = 0; j < order; j++)
+ {
+ Complex value = 0.0;
+ for (var i = 0; i < order; i++)
+ {
+ value += ((DenseMatrix)MatrixEv).Data[(i * order) + j] * tmp[i];
+ }
+
+ result[j, k] = value;
+ }
+ }
+ }
+ else
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSymmetric);
+ }
+ }
+
+ ///
+ /// Solves a system of linear equations, Ax = b, with A EVD factorized.
+ ///
+ /// The right hand side vector, b.
+ /// The left hand side , x.
+ public override void Solve(Vector input, Vector result)
+ {
+ if (input == null)
+ {
+ throw new ArgumentNullException("input");
+ }
+
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ // Ax=b where A is an m x m matrix
+ // Check that b is a column vector with m entries
+ if (VectorEv.Count != input.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength);
+ }
+
+ // Check that x is a column vector with n entries
+ if (VectorEv.Count != result.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions);
+ }
+
+ if (IsSymmetric)
+ {
+ // Symmetric case -> x = V * inv(λ) * VH * b;
+ var order = VectorEv.Count;
+ var tmp = new Complex[order];
+ Complex value;
+
+ for (var j = 0; j < order; j++)
+ {
+ value = 0;
+ if (j < order)
+ {
+ for (var i = 0; i < order; i++)
+ {
+ value += ((DenseMatrix)MatrixEv).Data[(j * order) + i].Conjugate() * input[i];
+ }
+
+ value /= VectorEv[j].Real;
+ }
+
+ tmp[j] = value;
+ }
+
+ for (var j = 0; j < order; j++)
+ {
+ value = 0;
+ for (int i = 0; i < order; i++)
+ {
+ value += ((DenseMatrix)MatrixEv).Data[(i * order) + j] * tmp[i];
+ }
+
+ result[j] = value;
+ }
+ }
+ else
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSymmetric);
+ }
+ }
+
+ ///
+ /// Multiply two values T*T
+ ///
+ /// Left operand value
+ /// Right operand value
+ /// Result of multiplication
+ protected sealed override Complex MultiplyT(Complex val1, Complex val2)
+ {
+ return val1 * val2;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Numerics/LinearAlgebra/Complex/Factorization/DenseGramSchmidt.cs b/src/Numerics/LinearAlgebra/Complex/Factorization/DenseGramSchmidt.cs
new file mode 100644
index 00000000..048b84bc
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Complex/Factorization/DenseGramSchmidt.cs
@@ -0,0 +1,239 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+// http://mathnetnumerics.codeplex.com
+//
+// Copyright (c) 2009-2010 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization
+{
+ using System;
+ using System.Numerics;
+ using Generic;
+ using Generic.Factorization;
+ using Properties;
+ using Threading;
+
+ ///
+ /// A class which encapsulates the functionality of the QR decomposition Modified Gram-Schmidt Orthogonalization.
+ /// Any complex square matrix A may be decomposed as A = QR where Q is an unitary mxn matrix and R is an nxn upper triangular matrix.
+ ///
+ ///
+ /// The computation of the QR decomposition is done at construction time by modified Gram-Schmidt Orthogonalization.
+ ///
+ public class DenseGramSchmidt : GramSchmidt
+ {
+ ///
+ /// Initializes a new instance of the class. This object creates an unitary matrix
+ /// using the modified Gram-Schmidt method.
+ ///
+ /// The matrix to factor.
+ /// If is null.
+ /// If row count is less then column count
+ /// If is rank deficient
+ public DenseGramSchmidt(DenseMatrix matrix)
+ {
+ if (matrix == null)
+ {
+ throw new ArgumentNullException("matrix");
+ }
+
+ if (matrix.RowCount < matrix.ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions);
+ }
+
+ MatrixQ = matrix.Clone();
+ MatrixR = matrix.CreateMatrix(matrix.ColumnCount, matrix.ColumnCount);
+ Factorize(((DenseMatrix)MatrixQ).Data, MatrixQ.RowCount, MatrixQ.ColumnCount, ((DenseMatrix)MatrixR).Data);
+ }
+
+ ///
+ /// Factorize matrix using the modified Gram-Schmidt method.
+ ///
+ /// Initial matrix. On exit is replaced by Q.
+ /// Number of rows in Q.
+ /// Number of columns in Q.
+ /// On exit is filled by R.
+ private static void Factorize(Complex[] q, int rowsQ, int columnsQ, Complex[] r)
+ {
+ for (var k = 0; k < columnsQ; k++)
+ {
+ var norm = 0.0;
+ for (var i = 0; i < rowsQ; i++)
+ {
+ norm += q[(k * rowsQ) + i].Magnitude * q[(k * rowsQ) + i].Magnitude;
+ }
+
+ norm = Math.Sqrt(norm);
+ if (norm == 0.0)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixNotRankDeficient);
+ }
+
+ r[(k * columnsQ) + k] = norm;
+ for (var i = 0; i < rowsQ; i++)
+ {
+ q[(k * rowsQ) + i] /= norm;
+ }
+
+ for (var j = k + 1; j < columnsQ; j++)
+ {
+ int k1 = k;
+ int j1 = j;
+ var dot = CommonParallel.Aggregate(0, rowsQ, index => q[(k1 * rowsQ) + index].Conjugate() * q[(j1 * rowsQ) + index]);
+ r[(j * columnsQ) + k] = dot;
+ for (var i = 0; i < rowsQ; i++)
+ {
+ var value = q[(j * rowsQ) + i] - (q[(k * rowsQ) + i] * dot);
+ q[(j * rowsQ) + i] = value;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Solves a system of linear equations, AX = B, with A QR factorized.
+ ///
+ /// The right hand side , B.
+ /// The left hand side , X.
+ public override void Solve(Matrix input, Matrix result)
+ {
+ // Check for proper arguments.
+ if (input == null)
+ {
+ throw new ArgumentNullException("input");
+ }
+
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ // The solution X should have the same number of columns as B
+ if (input.ColumnCount != result.ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension);
+ }
+
+ // The dimension compatibility conditions for X = A\B require the two matrices A and B to have the same number of rows
+ if (MatrixQ.RowCount != input.RowCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension);
+ }
+
+ // The solution X row dimension is equal to the column dimension of A
+ if (MatrixQ.ColumnCount != result.RowCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension);
+ }
+
+ var dinput = input as DenseMatrix;
+ if (dinput == null)
+ {
+ throw new NotImplementedException("Can only do GramSchmidt factorization for dense matrices at the moment.");
+ }
+
+ var dresult = result as DenseMatrix;
+ if (dresult == null)
+ {
+ throw new NotImplementedException("Can only do GramSchmidt factorization for dense matrices at the moment.");
+ }
+
+ Control.LinearAlgebraProvider.QRSolveFactored(((DenseMatrix)MatrixQ).Data, ((DenseMatrix)MatrixR).Data, MatrixQ.RowCount, MatrixQ.ColumnCount, dinput.Data, input.ColumnCount, dresult.Data);
+ }
+
+ ///
+ /// Solves a system of linear equations, Ax = b, with A QR factorized.
+ ///
+ /// The right hand side vector, b.
+ /// The left hand side , x.
+ public override void Solve(Vector input, Vector result)
+ {
+ if (input == null)
+ {
+ throw new ArgumentNullException("input");
+ }
+
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ // Ax=b where A is an m x n matrix
+ // Check that b is a column vector with m entries
+ if (MatrixQ.RowCount != input.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength);
+ }
+
+ // Check that x is a column vector with n entries
+ if (MatrixQ.ColumnCount != result.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions);
+ }
+
+ var dinput = input as DenseVector;
+ if (dinput == null)
+ {
+ throw new NotImplementedException("Can only do GramSchmidt factorization for dense vectors at the moment.");
+ }
+
+ var dresult = result as DenseVector;
+ if (dresult == null)
+ {
+ throw new NotImplementedException("Can only do GramSchmidt factorization for dense vectors at the moment.");
+ }
+
+ Control.LinearAlgebraProvider.QRSolveFactored(((DenseMatrix)MatrixQ).Data, ((DenseMatrix)MatrixR).Data, MatrixQ.RowCount, MatrixQ.ColumnCount, dinput.Data, 1, dresult.Data);
+ }
+
+ #region Simple arithmetic of type T
+
+ ///
+ /// Multiply two values T*T
+ ///
+ /// Left operand value
+ /// Right operand value
+ /// Result of multiplication
+ protected sealed override Complex MultiplyT(Complex val1, Complex val2)
+ {
+ return val1 * val2;
+ }
+
+ ///
+ /// Returns the absolute value of a specified number.
+ ///
+ /// A number whose absolute is to be found
+ /// Absolute value
+ protected sealed override double AbsoluteT(Complex val1)
+ {
+ return val1.Magnitude;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Complex/Factorization/UserEvd.cs b/src/Numerics/LinearAlgebra/Complex/Factorization/UserEvd.cs
index aa06b67e..df1302ba 100644
--- a/src/Numerics/LinearAlgebra/Complex/Factorization/UserEvd.cs
+++ b/src/Numerics/LinearAlgebra/Complex/Factorization/UserEvd.cs
@@ -90,7 +90,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization
if (IsSymmetric)
{
- var matrixCopy = matrix.Clone();
+ var matrixCopy = matrix.ToArray();
var tau = new Complex[order];
var d = new double[order];
var e = new double[order];
@@ -126,14 +126,14 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization
/// Smith, Boyle, Dongarra, Garbow, Ikebe, Klema, Moler, and Wilkinson, Handbook for
/// Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
/// Fortran subroutine in EISPACK.
- private static void SymmetricTridiagonalize(Matrix matrixA, double[] d, double[] e, Complex[] tau, int order)
+ private static void SymmetricTridiagonalize(Complex[,] matrixA, double[] d, double[] e, Complex[] tau, int order)
{
double hh;
tau[order - 1] = Complex.One;
- for (var i = 0; i < matrixA.Diagonal().Count; i++)
+ for (var i = 0; i < order; i++)
{
- d[i] = matrixA.Diagonal()[i].Real;
+ d[i] = matrixA[i, i].Real;
}
// Householder reduction to tridiagonal form.
@@ -331,9 +331,9 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization
// Accumulate transformation.
for (var k = 0; k < order; k++)
{
- h = MatrixEv[k, i + 1].Real;
- MatrixEv[k, i + 1] = (s * MatrixEv[k, i].Real) + (c * h);
- MatrixEv[k, i] = (c * MatrixEv[k, i].Real) - (s * h);
+ h = MatrixEv.At(k, i + 1).Real;
+ MatrixEv.At(k, i + 1, (s * MatrixEv.At(k, i).Real) + (c * h));
+ MatrixEv.At(k, i, (c * MatrixEv.At(k, i).Real) - (s * h));
}
}
@@ -375,9 +375,9 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization
d[i] = p;
for (var j = 0; j < order; j++)
{
- p = MatrixEv[j, i].Real;
- MatrixEv[j, i] = MatrixEv[j, k];
- MatrixEv[j, k] = p;
+ p = MatrixEv.At(j, i).Real;
+ MatrixEv.At(j, i, MatrixEv.At(j, k));
+ MatrixEv.At(j, k, p);
}
}
}
@@ -393,13 +393,13 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization
/// by Smith, Boyle, Dongarra, Garbow, Ikebe, Klema, Moler, and Wilkinson, Handbook for
/// Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
/// Fortran subroutine in EISPACK.
- private void SymmetricUntridiagonalize(Matrix matrixA, Complex[] tau, int order)
+ private void SymmetricUntridiagonalize(Complex[,] matrixA, Complex[] tau, int order)
{
for (var i = 0; i < order; i++)
{
for (var j = 0; j < order; j++)
{
- MatrixEv[i, j] = MatrixEv[i, j].Real * tau[i].Conjugate();
+ MatrixEv.At(i, j, MatrixEv.At(i, j).Real * tau[i].Conjugate());
}
}
@@ -414,14 +414,14 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization
var s = Complex.Zero;
for (var k = 0; k < i; k++)
{
- s += MatrixEv[k, j] * matrixA[i, k];
+ s += MatrixEv.At(k, j) * matrixA[i, k];
}
s = (s / h) / h;
for (var k = 0; k < i; k++)
{
- MatrixEv[k, j] -= s * matrixA[i, k].Conjugate();
+ MatrixEv.At(k, j, MatrixEv.At(k, j) - s * matrixA[i, k].Conjugate());
}
}
}
@@ -515,7 +515,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization
{
for (var j = 0; j < order; j++)
{
- MatrixEv[i, j] = i == j ? Complex.One : Complex.Zero;
+ MatrixEv.At(i, j, i == j ? Complex.One : Complex.Zero);
}
}
@@ -535,14 +535,14 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization
var g = Complex.Zero;
for (var i = m; i < order; i++)
{
- g += ort[i].Conjugate() * MatrixEv[i, j];
+ g += ort[i].Conjugate() * MatrixEv.At(i, j);
}
// Double division avoids possible underflow
g /= norm;
for (var i = m; i < order; i++)
{
- MatrixEv[i, j] += g * ort[i];
+ MatrixEv.At(i, j, MatrixEv.At(i, j) + g * ort[i]);
}
}
}
@@ -567,7 +567,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization
for (var j = 0; j < order; j++)
{
- MatrixEv[j, i] *= y;
+ MatrixEv.At(j, i, MatrixEv.At(j, i) * y);
}
}
}
@@ -706,10 +706,10 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization
for (var i = 0; i < order; i++)
{
- y = MatrixEv[i, j - 1];
- z = MatrixEv[i, j];
- MatrixEv[i, j - 1] = (x * y) + (matrixH[j, j - 1].Imaginary * z);
- MatrixEv[i, j] = (x.Conjugate() * z) - (matrixH[j, j - 1].Imaginary * y);
+ y = MatrixEv.At(i, j - 1);
+ z = MatrixEv.At(i, j);
+ MatrixEv.At(i, j - 1, (x * y) + (matrixH[j, j - 1].Imaginary * z));
+ MatrixEv.At(i, j, (x.Conjugate() * z) - (matrixH[j, j - 1].Imaginary * y));
}
}
@@ -722,7 +722,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization
for (var i = 0; i < order; i++)
{
- MatrixEv[i, n] *= s;
+ MatrixEv.At(i, n, MatrixEv.At(i, n) * s);
}
}
}
@@ -790,10 +790,10 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization
z = Complex.Zero;
for (var k = 0; k <= j; k++)
{
- z += MatrixEv[i, k] * matrixH[k, j];
+ z += MatrixEv.At(i, k) * matrixH[k, j];
}
- MatrixEv[i, j] = z;
+ MatrixEv.At(i, j, z);
}
}
}
@@ -848,7 +848,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization
{
for (var i = 0; i < order; i++)
{
- value += MatrixEv.At(i, j) * input.At(i, k);
+ value += MatrixEv.At(i, j).Conjugate() * input.At(i, k);
}
value /= VectorEv[j].Real;
@@ -862,7 +862,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization
Complex value = 0.0;
for (var i = 0; i < order; i++)
{
- value += MatrixEv.At(j, i).Conjugate() * tmp[i];
+ value += MatrixEv.At(j, i) * tmp[i];
}
result[j, k] = value;
@@ -919,7 +919,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization
{
for (var i = 0; i < order; i++)
{
- value += MatrixEv.At(i, j) * input[i];
+ value += MatrixEv.At(i, j).Conjugate() * input[i];
}
value /= VectorEv[j].Real;
@@ -933,7 +933,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization
value = 0;
for (int i = 0; i < order; i++)
{
- value += MatrixEv.At(j, i).Conjugate() * tmp[i];
+ value += MatrixEv.At(j, i) * tmp[i];
}
result[j] = value;
diff --git a/src/Numerics/LinearAlgebra/Complex/Factorization/GramSchmidt.cs b/src/Numerics/LinearAlgebra/Complex/Factorization/UserGramSchmidt.cs
similarity index 87%
rename from src/Numerics/LinearAlgebra/Complex/Factorization/GramSchmidt.cs
rename to src/Numerics/LinearAlgebra/Complex/Factorization/UserGramSchmidt.cs
index 58ded73f..2ce704b4 100644
--- a/src/Numerics/LinearAlgebra/Complex/Factorization/GramSchmidt.cs
+++ b/src/Numerics/LinearAlgebra/Complex/Factorization/UserGramSchmidt.cs
@@ -1,4 +1,4 @@
-//
+//
// Math.NET Numerics, part of the Math.NET Project
// http://numerics.mathdotnet.com
// http://github.com/mathnet/mathnet-numerics
@@ -43,17 +43,17 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization
///
/// The computation of the QR decomposition is done at construction time by modified Gram-Schmidt Orthogonalization.
///
- public class GramSchmidt : QR
+ public class UserGramSchmidt : GramSchmidt
{
///
- /// Initializes a new instance of the class. This object creates an unitary matrix
+ /// Initializes a new instance of the class. This object creates an unitary matrix
/// using the modified Gram-Schmidt method.
///
/// The matrix to factor.
/// If is null.
/// If row count is less then column count
/// If is rank deficient
- public GramSchmidt(Matrix matrix)
+ public UserGramSchmidt(Matrix matrix)
{
if (matrix == null)
{
@@ -100,44 +100,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization
}
}
- ///
- /// Gets a value indicating whether the matrix is full rank or not.
- ///
- /// true if the matrix is full rank; otherwise false.
- public override bool IsFullRank
- {
- get
- {
- return true;
- }
- }
-
- ///
- /// Gets the determinant of the matrix for which the QR matrix was computed.
- ///
- public override double Determinant
- {
- get
- {
- if (MatrixQ.RowCount != MatrixQ.ColumnCount)
- {
- throw new ArgumentException(Resources.ArgumentMatrixSquare);
- }
-
- var det = Complex.One;
- for (var i = 0; i < MatrixR.ColumnCount; i++)
- {
- det *= MatrixR.At(i, i);
- if (MatrixR.At(i, i).Magnitude.AlmostEqualInDecimalPlaces(0.0, 15))
- {
- return 0;
- }
- }
-
- return det.Magnitude;
- }
- }
-
///
/// Solves a system of linear equations, AX = B, with A QR factorized.
///
diff --git a/src/Numerics/LinearAlgebra/Complex/Solvers/Iterative/MlkBiCgStab.cs b/src/Numerics/LinearAlgebra/Complex/Solvers/Iterative/MlkBiCgStab.cs
index 41791696..e3621bb7 100644
--- a/src/Numerics/LinearAlgebra/Complex/Solvers/Iterative/MlkBiCgStab.cs
+++ b/src/Numerics/LinearAlgebra/Complex/Solvers/Iterative/MlkBiCgStab.cs
@@ -36,8 +36,8 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Solvers.Iterative
using System.Linq;
using System.Numerics;
using Distributions;
- using Factorization;
using Generic;
+ using Generic.Factorization;
using Generic.Solvers;
using Generic.Solvers.Preconditioners;
using Generic.Solvers.Status;
@@ -642,7 +642,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Solvers.Iterative
}
// Compute the orthogonalization.
- var gs = new GramSchmidt(matrix);
+ var gs = matrix.GramSchmidt();
var orthogonalMatrix = gs.Q;
// Now transfer this to vectors
diff --git a/src/Numerics/LinearAlgebra/Complex/SparseVector.cs b/src/Numerics/LinearAlgebra/Complex/SparseVector.cs
index c0573acc..5d2673a0 100644
--- a/src/Numerics/LinearAlgebra/Complex/SparseVector.cs
+++ b/src/Numerics/LinearAlgebra/Complex/SparseVector.cs
@@ -28,6 +28,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex
{
using System;
using System.Collections.Generic;
+ using System.Linq;
using System.Numerics;
using Distributions;
using Generic;
@@ -1238,6 +1239,11 @@ namespace MathNet.Numerics.LinearAlgebra.Complex
return 0.0;
}
+ if (2.0 == p)
+ {
+ return _nonZeroValues.Aggregate(Complex.Zero, SpecialFunctions.Hypotenuse).Magnitude;
+ }
+
if (Double.IsPositiveInfinity(p))
{
return CommonParallel.Select(0, NonZerosCount, (index, localData) => Math.Max(localData, _nonZeroValues[index].Magnitude), Math.Max);
diff --git a/src/Numerics/LinearAlgebra/Complex32/DenseVector.cs b/src/Numerics/LinearAlgebra/Complex32/DenseVector.cs
index 21266793..f6ffb256 100644
--- a/src/Numerics/LinearAlgebra/Complex32/DenseVector.cs
+++ b/src/Numerics/LinearAlgebra/Complex32/DenseVector.cs
@@ -28,6 +28,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32
{
using System;
using System.Collections.Generic;
+ using System.Linq;
using Distributions;
using Generic;
using NumberTheory;
@@ -1185,6 +1186,11 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32
index => Data[index].Magnitude);
}
+ if (2.0 == p)
+ {
+ return Data.Aggregate(Complex32.Zero, SpecialFunctions.Hypotenuse).Magnitude;
+ }
+
if (Double.IsPositiveInfinity(p))
{
return CommonParallel.Select(
diff --git a/src/Numerics/LinearAlgebra/Complex32/Factorization/DenseEvd.cs b/src/Numerics/LinearAlgebra/Complex32/Factorization/DenseEvd.cs
new file mode 100644
index 00000000..dc40cac2
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Complex32/Factorization/DenseEvd.cs
@@ -0,0 +1,968 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+// http://mathnetnumerics.codeplex.com
+//
+// Copyright (c) 2009-2010 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization
+{
+ using System;
+ using System.Numerics;
+ using Generic;
+ using Generic.Factorization;
+ using Numerics;
+ using Properties;
+
+ ///
+ /// Eigenvalues and eigenvectors of a complex matrix.
+ ///
+ ///
+ /// If A is hermitan, then A = V*D*V' where the eigenvalue matrix D is
+ /// diagonal and the eigenvector matrix V is hermitan.
+ /// I.e. A = V*D*V' and V*VH=I.
+ /// If A is not symmetric, then the eigenvalue matrix D is block diagonal
+ /// with the real eigenvalues in 1-by-1 blocks and any complex eigenvalues,
+ /// lambda + i*mu, in 2-by-2 blocks, [lambda, mu; -mu, lambda]. The
+ /// columns of V represent the eigenvectors in the sense that A*V = V*D,
+ /// i.e. A.Multiply(V) equals V.Multiply(D). The matrix V may be badly
+ /// conditioned, or even singular, so the validity of the equation
+ /// A = V*D*Inverse(V) depends upon V.cond().
+ ///
+ public class DenseEvd : Evd
+ {
+ ///
+ /// Initializes a new instance of the class. This object will compute the
+ /// the eigenvalue decomposition when the constructor is called and cache it's decomposition.
+ ///
+ /// The matrix to factor.
+ /// If is null.
+ /// If EVD algorithm failed to converge with matrix .
+ public DenseEvd(DenseMatrix matrix)
+ {
+ if (matrix == null)
+ {
+ throw new ArgumentNullException("matrix");
+ }
+
+ if (matrix.RowCount != matrix.ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSquare);
+ }
+
+ var order = matrix.RowCount;
+
+ // Initialize matricies for eigenvalues and eigenvectors
+ MatrixEv = DenseMatrix.Identity(order);
+ MatrixD = matrix.CreateMatrix(order, order);
+ VectorEv = new LinearAlgebra.Complex.DenseVector(order);
+
+ IsSymmetric = true;
+
+ for (var i = 0; i < order & IsSymmetric; i++)
+ {
+ for (var j = 0; j < order & IsSymmetric; j++)
+ {
+ IsSymmetric &= matrix[i, j] == matrix[j, i].Conjugate();
+ }
+ }
+
+ if (IsSymmetric)
+ {
+ var matrixCopy = matrix.ToArray();
+ var tau = new Complex32[order];
+ var d = new float[order];
+ var e = new float[order];
+
+ SymmetricTridiagonalize(matrixCopy, d, e, tau, order);
+ SymmetricDiagonalize(((DenseMatrix)MatrixEv).Data, d, e, order);
+ SymmetricUntridiagonalize(((DenseMatrix)MatrixEv).Data, matrixCopy, tau, order);
+
+ for (var i = 0; i < order; i++)
+ {
+ VectorEv[i] = new Complex(d[i], e[i]);
+ }
+ }
+ else
+ {
+ var matrixH = matrix.ToArray();
+ NonsymmetricReduceToHessenberg(((DenseMatrix)MatrixEv).Data, matrixH, order);
+ NonsymmetricReduceHessenberToRealSchur(((LinearAlgebra.Complex.DenseVector)VectorEv).Data, ((DenseMatrix)MatrixEv).Data, matrixH, order);
+ }
+
+ for (var i = 0; i < VectorEv.Count; i++)
+ {
+ MatrixD.At(i, i, (Complex32)VectorEv[i]);
+ }
+ }
+
+ ///
+ /// Reduces a complex hermitian matrix to a real symmetric tridiagonal matrix using unitary similarity transformations.
+ ///
+ /// Source matrix to reduce
+ /// Output: Arrays for internal storage of real parts of eigenvalues
+ /// Output: Arrays for internal storage of imaginary parts of eigenvalues
+ /// Output: Arrays that contains further information about the transformations.
+ /// Order of initial matrix
+ /// This is derived from the Algol procedures HTRIDI by
+ /// Smith, Boyle, Dongarra, Garbow, Ikebe, Klema, Moler, and Wilkinson, Handbook for
+ /// Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
+ /// Fortran subroutine in EISPACK.
+ private static void SymmetricTridiagonalize(Complex32[,] matrixA, float[] d, float[] e, Complex32[] tau, int order)
+ {
+ float hh;
+ tau[order - 1] = Complex32.One;
+
+ for (var i = 0; i < order; i++)
+ {
+ d[i] = matrixA[i, i].Real;
+ }
+
+ // Householder reduction to tridiagonal form.
+ for (var i = order - 1; i > 0; i--)
+ {
+ // Scale to avoid under/overflow.
+ var scale = 0.0f;
+ var h = 0.0f;
+
+ for (var k = 0; k < i; k++)
+ {
+ scale = scale + Math.Abs(matrixA[i, k].Real) + Math.Abs(matrixA[i, k].Imaginary);
+ }
+
+ if (scale == 0.0f)
+ {
+ tau[i - 1] = Complex32.One;
+ e[i] = 0.0f;
+ }
+ else
+ {
+ for (var k = 0; k < i; k++)
+ {
+ matrixA[i, k] /= scale;
+ h += matrixA[i, k].MagnitudeSquared;
+ }
+
+ Complex32 g = (float)Math.Sqrt(h);
+ e[i] = scale * g.Real;
+
+ Complex32 temp;
+ var f = matrixA[i, i - 1];
+ if (f.Magnitude != 0)
+ {
+ temp = -(matrixA[i, i - 1].Conjugate() * tau[i].Conjugate()) / f.Magnitude;
+ h += f.Magnitude * g.Real;
+ g = 1.0f + (g / f.Magnitude);
+ matrixA[i, i - 1] *= g;
+ }
+ else
+ {
+ temp = -tau[i].Conjugate();
+ matrixA[i, i - 1] = g;
+ }
+
+ if ((f.Magnitude == 0) || (i != 1))
+ {
+ f = Complex32.Zero;
+ for (var j = 0; j < i; j++)
+ {
+ var tmp = Complex32.Zero;
+
+ // Form element of A*U.
+ for (var k = 0; k <= j; k++)
+ {
+ tmp += matrixA[j, k] * matrixA[i, k].Conjugate();
+ }
+
+ for (var k = j + 1; k <= i - 1; k++)
+ {
+ tmp += matrixA[k, j].Conjugate() * matrixA[i, k].Conjugate();
+ }
+
+ // Form element of P
+ tau[j] = tmp / h;
+ f += (tmp / h) * matrixA[i, j];
+ }
+
+ hh = f.Real / (h + h);
+
+ // Form the reduced A.
+ for (var j = 0; j < i; j++)
+ {
+ f = matrixA[i, j].Conjugate();
+ g = tau[j] - (hh * f);
+ tau[j] = g.Conjugate();
+
+ for (var k = 0; k <= j; k++)
+ {
+ matrixA[j, k] -= (f * tau[k]) + (g * matrixA[i, k]);
+ }
+ }
+ }
+
+ for (var k = 0; k < i; k++)
+ {
+ matrixA[i, k] *= scale;
+ }
+
+ tau[i - 1] = temp.Conjugate();
+ }
+
+ hh = d[i];
+ d[i] = matrixA[i, i].Real;
+ matrixA[i, i] = new Complex32(hh, scale * (float)Math.Sqrt(h));
+ }
+
+ hh = d[0];
+ d[0] = matrixA[0, 0].Real;
+ matrixA[0, 0] = hh;
+ e[0] = 0.0f;
+ }
+
+ ///
+ /// Symmetric tridiagonal QL algorithm.
+ ///
+ /// Data array of matrix V (eigenvectors)
+ /// Arrays for internal storage of real parts of eigenvalues
+ /// Arrays for internal storage of imaginary parts of eigenvalues
+ /// Order of initial matrix
+ /// This is derived from the Algol procedures tql2, by
+ /// Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
+ /// Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
+ /// Fortran subroutine in EISPACK.
+ private static void SymmetricDiagonalize(Complex32[] dataEv, float[] d, float[] e, int order)
+ {
+ const int Maxiter = 1000;
+
+ for (var i = 1; i < order; i++)
+ {
+ e[i - 1] = e[i];
+ }
+
+ e[order - 1] = 0.0f;
+
+ var f = 0.0f;
+ var tst1 = 0.0f;
+ var eps = Precision.DoubleMachinePrecision;
+ for (var l = 0; l < order; l++)
+ {
+ // Find small subdiagonal element
+ tst1 = Math.Max(tst1, Math.Abs(d[l]) + Math.Abs(e[l]));
+ var m = l;
+ while (m < order)
+ {
+ if (Math.Abs(e[m]) <= eps * tst1)
+ {
+ break;
+ }
+
+ m++;
+ }
+
+ // If m == l, d[l] is an eigenvalue,
+ // otherwise, iterate.
+ if (m > l)
+ {
+ var iter = 0;
+ do
+ {
+ iter = iter + 1; // (Could check iteration count here.)
+
+ // Compute implicit shift
+ var g = d[l];
+ var p = (d[l + 1] - g) / (2.0f * e[l]);
+ var r = SpecialFunctions.Hypotenuse(p, 1.0f);
+ if (p < 0)
+ {
+ r = -r;
+ }
+
+ d[l] = e[l] / (p + r);
+ d[l + 1] = e[l] * (p + r);
+
+ var dl1 = d[l + 1];
+ var h = g - d[l];
+ for (var i = l + 2; i < order; i++)
+ {
+ d[i] -= h;
+ }
+
+ f = f + h;
+
+ // Implicit QL transformation.
+ p = d[m];
+ var c = 1.0f;
+ var c2 = c;
+ var c3 = c;
+ var el1 = e[l + 1];
+ var s = 0.0f;
+ var s2 = 0.0f;
+ for (var i = m - 1; i >= l; i--)
+ {
+ c3 = c2;
+ c2 = c;
+ s2 = s;
+ g = c * e[i];
+ h = c * p;
+ r = SpecialFunctions.Hypotenuse(p, e[i]);
+ e[i + 1] = s * r;
+ s = e[i] / r;
+ c = p / r;
+ p = (c * d[i]) - (s * g);
+ d[i + 1] = h + (s * ((c * g) + (s * d[i])));
+
+ // Accumulate transformation.
+ for (var k = 0; k < order; k++)
+ {
+ h = dataEv[((i + 1) * order) + k].Real;
+ dataEv[((i + 1) * order) + k] = (s * dataEv[(i * order) + k].Real) + (c * h);
+ dataEv[(i * order) + k] = (c * dataEv[(i * order) + k].Real) - (s * h);
+ }
+ }
+
+ p = (-s) * s2 * c3 * el1 * e[l] / dl1;
+ e[l] = s * p;
+ d[l] = c * p;
+
+ // Check for convergence. If too many iterations have been performed,
+ // throw exception that Convergence Failed
+ if (iter >= Maxiter)
+ {
+ throw new ArgumentException(Resources.ConvergenceFailed);
+ }
+ }
+ while (Math.Abs(e[l]) > eps * tst1);
+ }
+
+ d[l] = d[l] + f;
+ e[l] = 0.0f;
+ }
+
+ // Sort eigenvalues and corresponding vectors.
+ for (var i = 0; i < order - 1; i++)
+ {
+ var k = i;
+ var p = d[i];
+ for (var j = i + 1; j < order; j++)
+ {
+ if (d[j] < p)
+ {
+ k = j;
+ p = d[j];
+ }
+ }
+
+ if (k != i)
+ {
+ d[k] = d[i];
+ d[i] = p;
+ for (var j = 0; j < order; j++)
+ {
+ p = dataEv[(i * order) + j].Real;
+ dataEv[(i * order) + j] = dataEv[(k * order) + j];
+ dataEv[(k * order) + j] = p;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Determines eigenvectors by undoing the symmetric tridiagonalize transformation
+ ///
+ /// Data array of matrix V (eigenvectors)
+ /// Previously tridiagonalized matrix by .
+ /// Contains further information about the transformations
+ /// Input matrix order
+ /// This is derived from the Algol procedures HTRIBK, by
+ /// by Smith, Boyle, Dongarra, Garbow, Ikebe, Klema, Moler, and Wilkinson, Handbook for
+ /// Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
+ /// Fortran subroutine in EISPACK.
+ private static void SymmetricUntridiagonalize(Complex32[] dataEv, Complex32[,] matrixA, Complex32[] tau, int order)
+ {
+ for (var i = 0; i < order; i++)
+ {
+ for (var j = 0; j < order; j++)
+ {
+ dataEv[(j * order) + i] = dataEv[(j * order) + i].Real * tau[i].Conjugate();
+ }
+ }
+
+ // Recover and apply the Householder matrices.
+ for (var i = 1; i < order; i++)
+ {
+ var h = matrixA[i, i].Imaginary;
+ if (h != 0)
+ {
+ for (var j = 0; j < order; j++)
+ {
+ var s = Complex32.Zero;
+ for (var k = 0; k < i; k++)
+ {
+ s += dataEv[(j * order) + k] * matrixA[i, k];
+ }
+
+ s = (s / h) / h;
+
+ for (var k = 0; k < i; k++)
+ {
+ dataEv[(j * order) + k] -= s * matrixA[i, k].Conjugate();
+ }
+ }
+ }
+ }
+ }
+
+ ///
+ /// Nonsymmetric reduction to Hessenberg form.
+ ///
+ /// Data array of matrix V (eigenvectors)
+ /// Array for internal storage of nonsymmetric Hessenberg form.
+ /// Order of initial matrix
+ /// This is derived from the Algol procedures orthes and ortran,
+ /// by Martin and Wilkinson, Handbook for Auto. Comp.,
+ /// Vol.ii-Linear Algebra, and the corresponding
+ /// Fortran subroutines in EISPACK.
+ private static void NonsymmetricReduceToHessenberg(Complex32[] dataEv, Complex32[,] matrixH, int order)
+ {
+ var ort = new Complex32[order];
+
+ for (var m = 1; m < order - 1; m++)
+ {
+ // Scale column.
+ var scale = 0.0f;
+ for (var i = m; i < order; i++)
+ {
+ scale += Math.Abs(matrixH[i, m - 1].Real) + Math.Abs(matrixH[i, m - 1].Imaginary);
+ }
+
+ if (scale != 0.0f)
+ {
+ // Compute Householder transformation.
+ var h = 0.0f;
+ for (var i = order - 1; i >= m; i--)
+ {
+ ort[i] = matrixH[i, m - 1] / scale;
+ h += ort[i].MagnitudeSquared;
+ }
+
+ var g = (float)Math.Sqrt(h);
+ if (ort[m].Magnitude != 0)
+ {
+ h = h + (ort[m].Magnitude * g);
+ g /= ort[m].Magnitude;
+ ort[m] = (1.0f + g) * ort[m];
+ }
+ else
+ {
+ ort[m] = g;
+ matrixH[m, m - 1] = scale;
+ }
+
+ // Apply Householder similarity transformation
+ // H = (I-u*u'/h)*H*(I-u*u')/h)
+ for (var j = m; j < order; j++)
+ {
+ var f = Complex32.Zero;
+ for (var i = order - 1; i >= m; i--)
+ {
+ f += ort[i].Conjugate() * matrixH[i, j];
+ }
+
+ f = f / h;
+ for (var i = m; i < order; i++)
+ {
+ matrixH[i, j] -= f * ort[i];
+ }
+ }
+
+ for (var i = 0; i < order; i++)
+ {
+ var f = Complex32.Zero;
+ for (var j = order - 1; j >= m; j--)
+ {
+ f += ort[j] * matrixH[i, j];
+ }
+
+ f = f / h;
+ for (var j = m; j < order; j++)
+ {
+ matrixH[i, j] -= f * ort[j].Conjugate();
+ }
+ }
+
+ ort[m] = scale * ort[m];
+ matrixH[m, m - 1] *= -g;
+ }
+ }
+
+ // Accumulate transformations (Algol's ortran).
+ for (var i = 0; i < order; i++)
+ {
+ for (var j = 0; j < order; j++)
+ {
+ dataEv[(j * order) + i] = i == j ? Complex32.One : Complex32.Zero;
+ }
+ }
+
+ for (var m = order - 2; m >= 1; m--)
+ {
+ if (matrixH[m, m - 1] != Complex32.Zero && ort[m] != Complex32.Zero)
+ {
+ var norm = (matrixH[m, m - 1].Real * ort[m].Real) + (matrixH[m, m - 1].Imaginary * ort[m].Imaginary);
+
+ for (var i = m + 1; i < order; i++)
+ {
+ ort[i] = matrixH[i, m - 1];
+ }
+
+ for (var j = m; j < order; j++)
+ {
+ var g = Complex32.Zero;
+ for (var i = m; i < order; i++)
+ {
+ g += ort[i].Conjugate() * dataEv[(j * order) + i];
+ }
+
+ // Double division avoids possible underflow
+ g /= norm;
+ for (var i = m; i < order; i++)
+ {
+ dataEv[(j * order) + i] += g * ort[i];
+ }
+ }
+ }
+ }
+
+ // Create real subdiagonal elements.
+ for (var i = 1; i < order; i++)
+ {
+ if (matrixH[i, i - 1].Imaginary != 0.0f)
+ {
+ var y = matrixH[i, i - 1] / matrixH[i, i - 1].Magnitude;
+ matrixH[i, i - 1] = matrixH[i, i - 1].Magnitude;
+ for (var j = i; j < order; j++)
+ {
+ matrixH[i, j] *= y.Conjugate();
+ }
+
+ for (var j = 0; j <= Math.Min(i + 1, order - 1); j++)
+ {
+ matrixH[j, i] *= y;
+ }
+
+ for (var j = 0; j < order; j++)
+ {
+ dataEv[(i * order) + j] *= y;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Nonsymmetric reduction from Hessenberg to real Schur form.
+ ///
+ /// Data array of the eigenvectors
+ /// Data array of matrix V (eigenvectors)
+ /// Array for internal storage of nonsymmetric Hessenberg form.
+ /// Order of initial matrix
+ /// This is derived from the Algol procedure hqr2,
+ /// by Martin and Wilkinson, Handbook for Auto. Comp.,
+ /// Vol.ii-Linear Algebra, and the corresponding
+ /// Fortran subroutine in EISPACK.
+ private static void NonsymmetricReduceHessenberToRealSchur(Complex[] vectorV, Complex32[] dataEv, Complex32[,] matrixH, int order)
+ {
+ // Initialize
+ var n = order - 1;
+ var eps = (float)Precision.SingleMachinePrecision;
+
+ float norm;
+ Complex32 s, x, y, z, exshift = Complex32.Zero;
+
+ // Outer loop over eigenvalue index
+ var iter = 0;
+ while (n >= 0)
+ {
+ // Look for single small sub-diagonal element
+ var l = n;
+ while (l > 0)
+ {
+ var tst1 = Math.Abs(matrixH[l - 1, l - 1].Real) + Math.Abs(matrixH[l - 1, l - 1].Imaginary) + Math.Abs(matrixH[l, l].Real) + Math.Abs(matrixH[l, l].Imaginary);
+ if (Math.Abs(matrixH[l, l - 1].Real) < eps * tst1)
+ {
+ break;
+ }
+
+ l--;
+ }
+
+ // Check for convergence
+ // One root found
+ if (l == n)
+ {
+ matrixH[n, n] += exshift;
+ vectorV[n] = matrixH[n, n].ToComplex();
+ n--;
+ iter = 0;
+ }
+ else
+ {
+ // Form shift
+ if (iter != 10 && iter != 20)
+ {
+ s = matrixH[n, n];
+ x = matrixH[n - 1, n] * matrixH[n, n - 1].Real;
+
+ if (x.Real != 0.0f || x.Imaginary != 0.0f)
+ {
+ y = (matrixH[n - 1, n - 1] - s) / 2.0f;
+ z = ((y * y) + x).SquareRoot();
+ if ((y.Real * z.Real) + (y.Imaginary * z.Imaginary) < 0.0f)
+ {
+ z *= -1.0f;
+ }
+
+ x /= y + z;
+ s = s - x;
+ }
+ }
+ else
+ {
+ // Form exceptional shift
+ s = Math.Abs(matrixH[n, n - 1].Real) + Math.Abs(matrixH[n - 1, n - 2].Real);
+ }
+
+ for (var i = 0; i <= n; i++)
+ {
+ matrixH[i, i] -= s;
+ }
+
+ exshift += s;
+ iter++;
+
+ // Reduce to triangle (rows)
+ for (var i = l + 1; i <= n; i++)
+ {
+ s = matrixH[i, i - 1].Real;
+ norm = SpecialFunctions.Hypotenuse(matrixH[i - 1, i - 1].Magnitude, s.Real);
+ x = matrixH[i - 1, i - 1] / norm;
+ vectorV[i - 1] = x.ToComplex();
+ matrixH[i - 1, i - 1] = norm;
+ matrixH[i, i - 1] = new Complex32(0.0f, s.Real / norm);
+
+ for (var j = i; j < order; j++)
+ {
+ y = matrixH[i - 1, j];
+ z = matrixH[i, j];
+ matrixH[i - 1, j] = (x.Conjugate() * y) + (matrixH[i, i - 1].Imaginary * z);
+ matrixH[i, j] = (x * z) - (matrixH[i, i - 1].Imaginary * y);
+ }
+ }
+
+ s = matrixH[n, n];
+ if (s.Imaginary != 0.0f)
+ {
+ s /= matrixH[n, n].Magnitude;
+ matrixH[n, n] = matrixH[n, n].Magnitude;
+
+ for (var j = n + 1; j < order; j++)
+ {
+ matrixH[n, j] *= s.Conjugate();
+ }
+ }
+
+ // Inverse operation (columns).
+ for (var j = l + 1; j <= n; j++)
+ {
+ x = (Complex32)vectorV[j - 1];
+ for (var i = 0; i <= j; i++)
+ {
+ z = matrixH[i, j];
+ if (i != j)
+ {
+ y = matrixH[i, j - 1];
+ matrixH[i, j - 1] = (x * y) + (matrixH[j, j - 1].Imaginary * z);
+ }
+ else
+ {
+ y = matrixH[i, j - 1].Real;
+ matrixH[i, j - 1] = new Complex32((x.Real * y.Real) - (x.Imaginary * y.Imaginary) + (matrixH[j, j - 1].Imaginary * z.Real), matrixH[i, j - 1].Imaginary);
+ }
+
+ matrixH[i, j] = (x.Conjugate() * z) - (matrixH[j, j - 1].Imaginary * y);
+ }
+
+ for (var i = 0; i < order; i++)
+ {
+ y = dataEv[((j - 1) * order) + i];
+ z = dataEv[(j * order) + i];
+ dataEv[((j - 1) * order) + i] = (x * y) + (matrixH[j, j - 1].Imaginary * z);
+ dataEv[(j * order) + i] = (x.Conjugate() * z) - (matrixH[j, j - 1].Imaginary * y);
+ }
+ }
+
+ if (s.Imaginary != 0.0f)
+ {
+ for (var i = 0; i <= n; i++)
+ {
+ matrixH[i, n] *= s;
+ }
+
+ for (var i = 0; i < order; i++)
+ {
+ dataEv[(n * order) + i] *= s;
+ }
+ }
+ }
+ }
+
+ // All roots found.
+ // Backsubstitute to find vectors of upper triangular form
+ norm = 0.0f;
+ for (var i = 0; i < order; i++)
+ {
+ for (var j = i; j < order; j++)
+ {
+ norm = Math.Max(norm, Math.Abs(matrixH[i, j].Real) + Math.Abs(matrixH[i, j].Imaginary));
+ }
+ }
+
+ if (order == 1)
+ {
+ return;
+ }
+
+ if (norm == 0.0f)
+ {
+ return;
+ }
+
+ for (n = order - 1; n > 0; n--)
+ {
+ x = (Complex32)vectorV[n];
+ matrixH[n, n] = 1.0f;
+
+ for (var i = n - 1; i >= 0; i--)
+ {
+ z = 0.0f;
+ for (var j = i + 1; j <= n; j++)
+ {
+ z += matrixH[i, j] * matrixH[j, n];
+ }
+
+ y = x - (Complex32)vectorV[i];
+ if (y.Real == 0.0f && y.Imaginary == 0.0f)
+ {
+ y = eps * norm;
+ }
+
+ matrixH[i, n] = z / y;
+
+ // Overflow control
+ var tr = Math.Abs(matrixH[i, n].Real) + Math.Abs(matrixH[i, n].Imaginary);
+ if ((eps * tr) * tr > 1)
+ {
+ for (var j = i; j <= n; j++)
+ {
+ matrixH[j, n] = matrixH[j, n] / tr;
+ }
+ }
+ }
+ }
+
+ // Back transformation to get eigenvectors of original matrix
+ for (var j = order - 1; j > 0; j--)
+ {
+ for (var i = 0; i < order; i++)
+ {
+ z = Complex32.Zero;
+ for (var k = 0; k <= j; k++)
+ {
+ z += dataEv[(k * order) + i] * matrixH[k, j];
+ }
+
+ dataEv[(j * order) + i] = z;
+ }
+ }
+ }
+
+ ///
+ /// Solves a system of linear equations, AX = B, with A SVD factorized.
+ ///
+ /// The right hand side , B.
+ /// The left hand side , X.
+ public override void Solve(Matrix input, Matrix result)
+ {
+ // Check for proper arguments.
+ if (input == null)
+ {
+ throw new ArgumentNullException("input");
+ }
+
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ // The solution X should have the same number of columns as B
+ if (input.ColumnCount != result.ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension);
+ }
+
+ // The dimension compatibility conditions for X = A\B require the two matrices A and B to have the same number of rows
+ if (VectorEv.Count != input.RowCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension);
+ }
+
+ // The solution X row dimension is equal to the column dimension of A
+ if (VectorEv.Count != result.RowCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension);
+ }
+
+ if (IsSymmetric)
+ {
+ var order = VectorEv.Count;
+ var tmp = new Complex32[order];
+
+ for (var k = 0; k < order; k++)
+ {
+ for (var j = 0; j < order; j++)
+ {
+ Complex32 value = 0.0f;
+ if (j < order)
+ {
+ for (var i = 0; i < order; i++)
+ {
+ value += ((DenseMatrix)MatrixEv).Data[(j * order) + i].Conjugate() * input.At(i, k);
+ }
+
+ value /= (float)VectorEv[j].Real;
+ }
+
+ tmp[j] = value;
+ }
+
+ for (var j = 0; j < order; j++)
+ {
+ Complex32 value = 0.0f;
+ for (var i = 0; i < order; i++)
+ {
+ value += ((DenseMatrix)MatrixEv).Data[(i * order) + j] * tmp[i];
+ }
+
+ result[j, k] = value;
+ }
+ }
+ }
+ else
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSymmetric);
+ }
+ }
+
+ ///
+ /// Solves a system of linear equations, Ax = b, with A EVD factorized.
+ ///
+ /// The right hand side vector, b.
+ /// The left hand side , x.
+ public override void Solve(Vector input, Vector result)
+ {
+ if (input == null)
+ {
+ throw new ArgumentNullException("input");
+ }
+
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ // Ax=b where A is an m x m matrix
+ // Check that b is a column vector with m entries
+ if (VectorEv.Count != input.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength);
+ }
+
+ // Check that x is a column vector with n entries
+ if (VectorEv.Count != result.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions);
+ }
+
+ if (IsSymmetric)
+ {
+ // Symmetric case -> x = V * inv(λ) * VH * b;
+ var order = VectorEv.Count;
+ var tmp = new Complex32[order];
+ Complex32 value;
+
+ for (var j = 0; j < order; j++)
+ {
+ value = 0;
+ if (j < order)
+ {
+ for (var i = 0; i < order; i++)
+ {
+ value += ((DenseMatrix)MatrixEv).Data[(j * order) + i].Conjugate() * input[i];
+ }
+
+ value /= (float)VectorEv[j].Real;
+ }
+
+ tmp[j] = value;
+ }
+
+ for (var j = 0; j < order; j++)
+ {
+ value = 0;
+ for (int i = 0; i < order; i++)
+ {
+ value += ((DenseMatrix)MatrixEv).Data[(i * order) + j] * tmp[i];
+ }
+
+ result[j] = value;
+ }
+ }
+ else
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSymmetric);
+ }
+ }
+
+ ///
+ /// Multiply two values T*T
+ ///
+ /// Left operand value
+ /// Right operand value
+ /// Result of multiplication
+ protected sealed override Complex32 MultiplyT(Complex32 val1, Complex32 val2)
+ {
+ return val1 * val2;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Numerics/LinearAlgebra/Complex32/Factorization/DenseGramSchmidt.cs b/src/Numerics/LinearAlgebra/Complex32/Factorization/DenseGramSchmidt.cs
new file mode 100644
index 00000000..42d21502
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Complex32/Factorization/DenseGramSchmidt.cs
@@ -0,0 +1,239 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+// http://mathnetnumerics.codeplex.com
+//
+// Copyright (c) 2009-2010 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization
+{
+ using System;
+ using Generic;
+ using Generic.Factorization;
+ using Numerics;
+ using Properties;
+ using Threading;
+
+ ///
+ /// A class which encapsulates the functionality of the QR decomposition Modified Gram-Schmidt Orthogonalization.
+ /// Any complex square matrix A may be decomposed as A = QR where Q is an unitary mxn matrix and R is an nxn upper triangular matrix.
+ ///
+ ///
+ /// The computation of the QR decomposition is done at construction time by modified Gram-Schmidt Orthogonalization.
+ ///
+ public class DenseGramSchmidt : GramSchmidt
+ {
+ ///
+ /// Initializes a new instance of the class. This object creates an unitary matrix
+ /// using the modified Gram-Schmidt method.
+ ///
+ /// The matrix to factor.
+ /// If is null.
+ /// If row count is less then column count
+ /// If is rank deficient
+ public DenseGramSchmidt(DenseMatrix matrix)
+ {
+ if (matrix == null)
+ {
+ throw new ArgumentNullException("matrix");
+ }
+
+ if (matrix.RowCount < matrix.ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions);
+ }
+
+ MatrixQ = matrix.Clone();
+ MatrixR = matrix.CreateMatrix(matrix.ColumnCount, matrix.ColumnCount);
+ Factorize(((DenseMatrix)MatrixQ).Data, MatrixQ.RowCount, MatrixQ.ColumnCount, ((DenseMatrix)MatrixR).Data);
+ }
+
+ ///
+ /// Factorize matrix using the modified Gram-Schmidt method.
+ ///
+ /// Initial matrix. On exit is replaced by Q.
+ /// Number of rows in Q.
+ /// Number of columns in Q.
+ /// On exit is filled by R.
+ private static void Factorize(Complex32[] q, int rowsQ, int columnsQ, Complex32[] r)
+ {
+ for (var k = 0; k < columnsQ; k++)
+ {
+ var norm = 0.0f;
+ for (var i = 0; i < rowsQ; i++)
+ {
+ norm += q[(k * rowsQ) + i].Magnitude * q[(k * rowsQ) + i].Magnitude;
+ }
+
+ norm = (float)Math.Sqrt(norm);
+ if (norm == 0.0f)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixNotRankDeficient);
+ }
+
+ r[(k * columnsQ) + k] = norm;
+ for (var i = 0; i < rowsQ; i++)
+ {
+ q[(k * rowsQ) + i] /= norm;
+ }
+
+ for (var j = k + 1; j < columnsQ; j++)
+ {
+ int k1 = k;
+ int j1 = j;
+ var dot = CommonParallel.Aggregate(0, rowsQ, index => q[(k1 * rowsQ) + index].Conjugate() * q[(j1 * rowsQ) + index]);
+ r[(j * columnsQ) + k] = dot;
+ for (var i = 0; i < rowsQ; i++)
+ {
+ var value = q[(j * rowsQ) + i] - (q[(k * rowsQ) + i] * dot);
+ q[(j * rowsQ) + i] = value;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Solves a system of linear equations, AX = B, with A QR factorized.
+ ///
+ /// The right hand side , B.
+ /// The left hand side , X.
+ public override void Solve(Matrix input, Matrix result)
+ {
+ // Check for proper arguments.
+ if (input == null)
+ {
+ throw new ArgumentNullException("input");
+ }
+
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ // The solution X should have the same number of columns as B
+ if (input.ColumnCount != result.ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension);
+ }
+
+ // The dimension compatibility conditions for X = A\B require the two matrices A and B to have the same number of rows
+ if (MatrixQ.RowCount != input.RowCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension);
+ }
+
+ // The solution X row dimension is equal to the column dimension of A
+ if (MatrixQ.ColumnCount != result.RowCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension);
+ }
+
+ var dinput = input as DenseMatrix;
+ if (dinput == null)
+ {
+ throw new NotImplementedException("Can only do GramSchmidt factorization for dense matrices at the moment.");
+ }
+
+ var dresult = result as DenseMatrix;
+ if (dresult == null)
+ {
+ throw new NotImplementedException("Can only do GramSchmidt factorization for dense matrices at the moment.");
+ }
+
+ Control.LinearAlgebraProvider.QRSolveFactored(((DenseMatrix)MatrixQ).Data, ((DenseMatrix)MatrixR).Data, MatrixQ.RowCount, MatrixQ.ColumnCount, dinput.Data, input.ColumnCount, dresult.Data);
+ }
+
+ ///
+ /// Solves a system of linear equations, Ax = b, with A QR factorized.
+ ///
+ /// The right hand side vector, b.
+ /// The left hand side , x.
+ public override void Solve(Vector input, Vector result)
+ {
+ if (input == null)
+ {
+ throw new ArgumentNullException("input");
+ }
+
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ // Ax=b where A is an m x n matrix
+ // Check that b is a column vector with m entries
+ if (MatrixQ.RowCount != input.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength);
+ }
+
+ // Check that x is a column vector with n entries
+ if (MatrixQ.ColumnCount != result.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions);
+ }
+
+ var dinput = input as DenseVector;
+ if (dinput == null)
+ {
+ throw new NotImplementedException("Can only do GramSchmidt factorization for dense vectors at the moment.");
+ }
+
+ var dresult = result as DenseVector;
+ if (dresult == null)
+ {
+ throw new NotImplementedException("Can only do GramSchmidt factorization for dense vectors at the moment.");
+ }
+
+ Control.LinearAlgebraProvider.QRSolveFactored(((DenseMatrix)MatrixQ).Data, ((DenseMatrix)MatrixR).Data, MatrixQ.RowCount, MatrixQ.ColumnCount, dinput.Data, 1, dresult.Data);
+ }
+
+ #region Simple arithmetic of type T
+
+ ///
+ /// Multiply two values T*T
+ ///
+ /// Left operand value
+ /// Right operand value
+ /// Result of multiplication
+ protected sealed override Complex32 MultiplyT(Complex32 val1, Complex32 val2)
+ {
+ return val1 * val2;
+ }
+
+ ///
+ /// Returns the absolute value of a specified number.
+ ///
+ /// A number whose absolute is to be found
+ /// Absolute value
+ protected sealed override double AbsoluteT(Complex32 val1)
+ {
+ return val1.Magnitude;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Complex32/Factorization/UserEvd.cs b/src/Numerics/LinearAlgebra/Complex32/Factorization/UserEvd.cs
index 57d56040..a86371b9 100644
--- a/src/Numerics/LinearAlgebra/Complex32/Factorization/UserEvd.cs
+++ b/src/Numerics/LinearAlgebra/Complex32/Factorization/UserEvd.cs
@@ -91,7 +91,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization
if (IsSymmetric)
{
- var matrixCopy = matrix.Clone();
+ var matrixCopy = matrix.ToArray();
var tau = new Complex32[order];
var d = new float[order];
var e = new float[order];
@@ -114,7 +114,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization
for (var i = 0; i < VectorEv.Count; i++)
{
- MatrixD[i, i] = (Complex32)VectorEv[i];
+ MatrixD.At(i, i, (Complex32)VectorEv[i]);
}
}
@@ -130,14 +130,14 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization
/// Smith, Boyle, Dongarra, Garbow, Ikebe, Klema, Moler, and Wilkinson, Handbook for
/// Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
/// Fortran subroutine in EISPACK.
- private static void SymmetricTridiagonalize(Matrix matrixA, float[] d, float[] e, Complex32[] tau, int order)
+ private static void SymmetricTridiagonalize(Complex32[,] matrixA, float[] d, float[] e, Complex32[] tau, int order)
{
float hh;
tau[order - 1] = Complex32.One;
- for (var i = 0; i < matrixA.Diagonal().Count; i++)
+ for (var i = 0; i < order; i++)
{
- d[i] = matrixA.Diagonal()[i].Real;
+ d[i] = matrixA[i, i].Real;
}
// Householder reduction to tridiagonal form.
@@ -335,9 +335,9 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization
// Accumulate transformation.
for (var k = 0; k < order; k++)
{
- h = MatrixEv[k, i + 1].Real;
- MatrixEv[k, i + 1] = (s * MatrixEv[k, i].Real) + (c * h);
- MatrixEv[k, i] = (c * MatrixEv[k, i].Real) - (s * h);
+ h = MatrixEv.At(k, i + 1).Real;
+ MatrixEv.At(k, i + 1, (s * MatrixEv.At(k, i).Real) + (c * h));
+ MatrixEv.At(k, i, (c * MatrixEv.At(k, i).Real) - (s * h));
}
}
@@ -379,9 +379,9 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization
d[i] = p;
for (var j = 0; j < order; j++)
{
- p = MatrixEv[j, i].Real;
- MatrixEv[j, i] = MatrixEv[j, k];
- MatrixEv[j, k] = p;
+ p = MatrixEv.At(j, i).Real;
+ MatrixEv.At(j, i, MatrixEv.At(j, k));
+ MatrixEv.At(j, k, p);
}
}
}
@@ -397,13 +397,13 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization
/// by Smith, Boyle, Dongarra, Garbow, Ikebe, Klema, Moler, and Wilkinson, Handbook for
/// Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
/// Fortran subroutine in EISPACK.
- private void SymmetricUntridiagonalize(Matrix matrixA, Complex32[] tau, int order)
+ private void SymmetricUntridiagonalize(Complex32[,] matrixA, Complex32[] tau, int order)
{
for (var i = 0; i < order; i++)
{
for (var j = 0; j < order; j++)
{
- MatrixEv[i, j] = MatrixEv[i, j].Real * tau[i].Conjugate();
+ MatrixEv.At(i, j, MatrixEv.At(i, j).Real * tau[i].Conjugate());
}
}
@@ -418,14 +418,14 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization
var s = Complex32.Zero;
for (var k = 0; k < i; k++)
{
- s += MatrixEv[k, j] * matrixA[i, k];
+ s += MatrixEv.At(k, j) * matrixA[i, k];
}
s = (s / h) / h;
for (var k = 0; k < i; k++)
{
- MatrixEv[k, j] -= s * matrixA[i, k].Conjugate();
+ MatrixEv.At(k, j, MatrixEv.At(k, j) - s * matrixA[i, k].Conjugate());
}
}
}
@@ -519,7 +519,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization
{
for (var j = 0; j < order; j++)
{
- MatrixEv[i, j] = i == j ? Complex32.One : Complex32.Zero;
+ MatrixEv.At(i, j, i == j ? Complex32.One : Complex32.Zero);
}
}
@@ -539,14 +539,14 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization
var g = Complex32.Zero;
for (var i = m; i < order; i++)
{
- g += ort[i].Conjugate() * MatrixEv[i, j];
+ g += ort[i].Conjugate() * MatrixEv.At(i, j);
}
// Double division avoids possible underflow
g /= norm;
for (var i = m; i < order; i++)
{
- MatrixEv[i, j] += g * ort[i];
+ MatrixEv.At(i, j, MatrixEv.At(i, j) + g * ort[i]);
}
}
}
@@ -571,7 +571,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization
for (var j = 0; j < order; j++)
{
- MatrixEv[j, i] *= y;
+ MatrixEv.At(j, i, MatrixEv.At(j, i) * y);
}
}
}
@@ -710,10 +710,10 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization
for (var i = 0; i < order; i++)
{
- y = MatrixEv[i, j - 1];
- z = MatrixEv[i, j];
- MatrixEv[i, j - 1] = (x * y) + (matrixH[j, j - 1].Imaginary * z);
- MatrixEv[i, j] = (x.Conjugate() * z) - (matrixH[j, j - 1].Imaginary * y);
+ y = MatrixEv.At(i, j - 1);
+ z = MatrixEv.At(i, j);
+ MatrixEv.At(i, j - 1, (x * y) + (matrixH[j, j - 1].Imaginary * z));
+ MatrixEv.At(i, j, (x.Conjugate() * z) - (matrixH[j, j - 1].Imaginary * y));
}
}
@@ -726,7 +726,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization
for (var i = 0; i < order; i++)
{
- MatrixEv[i, n] *= s;
+ MatrixEv.At(i, n, MatrixEv.At(i, n) * s);
}
}
}
@@ -794,10 +794,10 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization
z = Complex32.Zero;
for (var k = 0; k <= j; k++)
{
- z += MatrixEv[i, k] * matrixH[k, j];
+ z += MatrixEv.At(i, k) * matrixH[k, j];
}
- MatrixEv[i, j] = z;
+ MatrixEv.At(i, j, z);
}
}
}
@@ -852,7 +852,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization
{
for (var i = 0; i < order; i++)
{
- value += MatrixEv.At(i, j) * input.At(i, k);
+ value += MatrixEv.At(i, j).Conjugate() * input.At(i, k);
}
value /= (float)VectorEv[j].Real;
@@ -866,7 +866,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization
Complex32 value = 0.0f;
for (var i = 0; i < order; i++)
{
- value += MatrixEv.At(j, i).Conjugate() * tmp[i];
+ value += MatrixEv.At(j, i) * tmp[i];
}
result[j, k] = value;
@@ -923,7 +923,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization
{
for (var i = 0; i < order; i++)
{
- value += MatrixEv.At(i, j) * input[i];
+ value += MatrixEv.At(i, j).Conjugate() * input[i];
}
value /= (float)VectorEv[j].Real;
@@ -937,7 +937,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization
value = 0;
for (int i = 0; i < order; i++)
{
- value += MatrixEv.At(j, i).Conjugate() * tmp[i];
+ value += MatrixEv.At(j, i) * tmp[i];
}
result[j] = value;
diff --git a/src/Numerics/LinearAlgebra/Complex32/Factorization/GramSchmidt.cs b/src/Numerics/LinearAlgebra/Complex32/Factorization/UserGramSchmidt.cs
similarity index 87%
rename from src/Numerics/LinearAlgebra/Complex32/Factorization/GramSchmidt.cs
rename to src/Numerics/LinearAlgebra/Complex32/Factorization/UserGramSchmidt.cs
index 3ae4ae9a..385e35e9 100644
--- a/src/Numerics/LinearAlgebra/Complex32/Factorization/GramSchmidt.cs
+++ b/src/Numerics/LinearAlgebra/Complex32/Factorization/UserGramSchmidt.cs
@@ -1,4 +1,4 @@
-//
+//
// Math.NET Numerics, part of the Math.NET Project
// http://numerics.mathdotnet.com
// http://github.com/mathnet/mathnet-numerics
@@ -43,17 +43,17 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization
///
/// The computation of the QR decomposition is done at construction time by modified Gram-Schmidt Orthogonalization.
///
- public class GramSchmidt : QR
+ public class UserGramSchmidt : GramSchmidt
{
///
- /// Initializes a new instance of the class. This object creates an unitary matrix
+ /// Initializes a new instance of the class. This object creates an unitary matrix
/// using the modified Gram-Schmidt method.
///
/// The matrix to factor.
/// If is null.
/// If row count is less then column count
/// If is rank deficient
- public GramSchmidt(Matrix matrix)
+ public UserGramSchmidt(Matrix matrix)
{
if (matrix == null)
{
@@ -99,45 +99,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization
}
}
}
-
- ///
- /// Gets a value indicating whether the matrix is full rank or not.
- ///
- /// true if the matrix is full rank; otherwise false.
- public override bool IsFullRank
- {
- get
- {
- return true;
- }
- }
-
- ///
- /// Gets the determinant of the matrix for which the QR matrix was computed.
- ///
- public override double Determinant
- {
- get
- {
- if (MatrixQ.RowCount != MatrixQ.ColumnCount)
- {
- throw new ArgumentException(Resources.ArgumentMatrixSquare);
- }
-
- var det = Complex32.One;
- for (var i = 0; i < MatrixR.ColumnCount; i++)
- {
- det *= MatrixR.At(i, i);
- if (MatrixR.At(i, i).Magnitude.AlmostEqualInDecimalPlaces(0.0f, 7))
- {
- return 0;
- }
- }
-
- return det.Magnitude;
- }
- }
-
+
///
/// Solves a system of linear equations, AX = B, with A QR factorized.
///
diff --git a/src/Numerics/LinearAlgebra/Complex32/Solvers/Iterative/MlkBiCgStab.cs b/src/Numerics/LinearAlgebra/Complex32/Solvers/Iterative/MlkBiCgStab.cs
index c95633b7..f6e671a6 100644
--- a/src/Numerics/LinearAlgebra/Complex32/Solvers/Iterative/MlkBiCgStab.cs
+++ b/src/Numerics/LinearAlgebra/Complex32/Solvers/Iterative/MlkBiCgStab.cs
@@ -35,8 +35,8 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Solvers.Iterative
using System.Diagnostics;
using System.Linq;
using Distributions;
- using Factorization;
using Generic;
+ using Generic.Factorization;
using Generic.Solvers;
using Generic.Solvers.Preconditioners;
using Generic.Solvers.Status;
@@ -642,7 +642,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Solvers.Iterative
}
// Compute the orthogonalization.
- var gs = new GramSchmidt(matrix);
+ var gs = matrix.GramSchmidt();
var orthogonalMatrix = gs.Q;
// Now transfer this to vectors
diff --git a/src/Numerics/LinearAlgebra/Complex32/SparseVector.cs b/src/Numerics/LinearAlgebra/Complex32/SparseVector.cs
index af76de33..ead46d83 100644
--- a/src/Numerics/LinearAlgebra/Complex32/SparseVector.cs
+++ b/src/Numerics/LinearAlgebra/Complex32/SparseVector.cs
@@ -28,6 +28,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32
{
using System;
using System.Collections.Generic;
+ using System.Linq;
using Distributions;
using Generic;
using NumberTheory;
@@ -1238,6 +1239,11 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32
return 0.0;
}
+ if (2.0 == p)
+ {
+ return _nonZeroValues.Aggregate(Complex32.Zero, SpecialFunctions.Hypotenuse).Magnitude;
+ }
+
if (Double.IsPositiveInfinity(p))
{
return CommonParallel.Select(0, NonZerosCount, (index, localData) => Math.Max(localData, _nonZeroValues[index].Magnitude), Math.Max);
diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/DenseEvd.cs b/src/Numerics/LinearAlgebra/Double/Factorization/DenseEvd.cs
new file mode 100644
index 00000000..5ec5ea22
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Factorization/DenseEvd.cs
@@ -0,0 +1,1237 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+// http://mathnetnumerics.codeplex.com
+//
+// Copyright (c) 2009-2010 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
+{
+ using System;
+ using System.Numerics;
+ using Generic;
+ using Generic.Factorization;
+ using Properties;
+
+ ///
+ /// Eigenvalues and eigenvectors of a real matrix.
+ ///
+ ///
+ /// If A is symmetric, then A = V*D*V' where the eigenvalue matrix D is
+ /// diagonal and the eigenvector matrix V is orthogonal.
+ /// I.e. A = V*D*V' and V*VT=I.
+ /// If A is not symmetric, then the eigenvalue matrix D is block diagonal
+ /// with the real eigenvalues in 1-by-1 blocks and any complex eigenvalues,
+ /// lambda + i*mu, in 2-by-2 blocks, [lambda, mu; -mu, lambda]. The
+ /// columns of V represent the eigenvectors in the sense that A*V = V*D,
+ /// i.e. A.Multiply(V) equals V.Multiply(D). The matrix V may be badly
+ /// conditioned, or even singular, so the validity of the equation
+ /// A = V*D*Inverse(V) depends upon V.cond().
+ ///
+ public class DenseEvd : Evd
+ {
+ ///
+ /// Initializes a new instance of the class. This object will compute the
+ /// the eigenvalue decomposition when the constructor is called and cache it's decomposition.
+ ///
+ /// The matrix to factor.
+ /// If is null.
+ /// If EVD algorithm failed to converge with matrix .
+ public DenseEvd(DenseMatrix matrix)
+ {
+ if (matrix == null)
+ {
+ throw new ArgumentNullException("matrix");
+ }
+
+ if (matrix.RowCount != matrix.ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSquare);
+ }
+
+ var order = matrix.RowCount;
+
+ // Initialize matricies for eigenvalues and eigenvectors
+ MatrixEv = matrix.CreateMatrix(order, order);
+ MatrixD = matrix.CreateMatrix(order, order);
+ VectorEv = new LinearAlgebra.Complex.DenseVector(order);
+
+ IsSymmetric = true;
+
+ for (var i = 0; i < order & IsSymmetric; i++)
+ {
+ for (var j = 0; j < order & IsSymmetric; j++)
+ {
+ IsSymmetric &= matrix[i, j] == matrix[j, i];
+ }
+ }
+
+ var d = new double[order];
+ var e = new double[order];
+
+ if (IsSymmetric)
+ {
+ matrix.CopyTo(MatrixEv);
+ d = MatrixEv.Row(order - 1).ToArray();
+
+ SymmetricTridiagonalize(((DenseMatrix)MatrixEv).Data, d, e, order);
+ SymmetricDiagonalize(((DenseMatrix)MatrixEv).Data, d, e, order);
+ }
+ else
+ {
+ var matrixH = matrix.ToArray();
+
+ NonsymmetricReduceToHessenberg(((DenseMatrix)MatrixEv).Data, matrixH, order);
+ NonsymmetricReduceHessenberToRealSchur(((DenseMatrix)MatrixEv).Data, matrixH, d, e, order);
+ }
+
+ for (var i = 0; i < order; i++)
+ {
+ MatrixD[i, i] = d[i];
+
+ if (e[i] > 0)
+ {
+ MatrixD.At(i, i + 1, e[i]);
+ }
+ else if (e[i] < 0)
+ {
+ MatrixD.At(i, i - 1, e[i]);
+ }
+ }
+
+ for (var i = 0; i < order; i++)
+ {
+ VectorEv[i] = new Complex(d[i], e[i]);
+ }
+ }
+
+ ///
+ /// Symmetric Householder reduction to tridiagonal form.
+ ///
+ /// Data array of matrix V (eigenvectors)
+ /// Arrays for internal storage of real parts of eigenvalues
+ /// Arrays for internal storage of imaginary parts of eigenvalues
+ /// Order of initial matrix
+ /// This is derived from the Algol procedures tred2 by
+ /// Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
+ /// Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
+ /// Fortran subroutine in EISPACK.
+ private static void SymmetricTridiagonalize(double[] a, double[] d, double[] e, int order)
+ {
+ // Householder reduction to tridiagonal form.
+ for (var i = order - 1; i > 0; i--)
+ {
+ // Scale to avoid under/overflow.
+ var scale = 0.0;
+ var h = 0.0;
+
+ for (var k = 0; k < i; k++)
+ {
+ scale = scale + Math.Abs(d[k]);
+ }
+
+ if (scale == 0.0)
+ {
+ e[i] = d[i - 1];
+ for (var j = 0; j < i; j++)
+ {
+ d[j] = a[(j * order) + i - 1];
+ a[(j * order) + i] = 0.0;
+ a[(i * order) + j] = 0.0;
+ }
+ }
+ else
+ {
+ // Generate Householder vector.
+ for (var k = 0; k < i; k++)
+ {
+ d[k] /= scale;
+ h += d[k] * d[k];
+ }
+
+ var f = d[i - 1];
+ var g = Math.Sqrt(h);
+ if (f > 0)
+ {
+ g = -g;
+ }
+
+ e[i] = scale * g;
+ h = h - (f * g);
+ d[i - 1] = f - g;
+
+ for (var j = 0; j < i; j++)
+ {
+ e[j] = 0.0;
+ }
+
+ // Apply similarity transformation to remaining columns.
+ for (var j = 0; j < i; j++)
+ {
+ f = d[j];
+ a[(i * order) + j] = f;
+ g = e[j] + (a[(j * order) + j] * f);
+
+ for (var k = j + 1; k <= i - 1; k++)
+ {
+ g += a[(j * order) + k] * d[k];
+ e[k] += a[(j * order) + k] * f;
+ }
+
+ e[j] = g;
+ }
+
+ f = 0.0;
+
+ for (var j = 0; j < i; j++)
+ {
+ e[j] /= h;
+ f += e[j] * d[j];
+ }
+
+ var hh = f / (h + h);
+
+ for (var j = 0; j < i; j++)
+ {
+ e[j] -= hh * d[j];
+ }
+
+ for (var j = 0; j < i; j++)
+ {
+ f = d[j];
+ g = e[j];
+
+ for (var k = j; k <= i - 1; k++)
+ {
+ a[(j * order) + k] -= (f * e[k]) + (g * d[k]);
+ }
+
+ d[j] = a[(j * order) + i - 1];
+ a[(j * order) + i] = 0.0;
+ }
+ }
+
+ d[i] = h;
+ }
+
+ // Accumulate transformations.
+ for (var i = 0; i < order - 1; i++)
+ {
+ a[(i * order) + order - 1] = a[(i * order) + i];
+ a[(i * order) + i] = 1.0;
+ var h = d[i + 1];
+ if (h != 0.0)
+ {
+ for (var k = 0; k <= i; k++)
+ {
+ d[k] = a[((i + 1) * order) + k] / h;
+ }
+
+ for (var j = 0; j <= i; j++)
+ {
+ var g = 0.0;
+ for (var k = 0; k <= i; k++)
+ {
+ g += a[((i + 1) * order) + k] * a[(j * order) + k];
+ }
+
+ for (var k = 0; k <= i; k++)
+ {
+ a[(j * order) + k] -= g * d[k];
+ }
+ }
+ }
+
+ for (var k = 0; k <= i; k++)
+ {
+ a[((i + 1) * order) + k] = 0.0;
+ }
+ }
+
+ for (var j = 0; j < order; j++)
+ {
+ d[j] = a[(j * order) + order - 1];
+ a[(j * order) + order - 1] = 0.0;
+ }
+
+ a[(order * order) - 1] = 1.0;
+ e[0] = 0.0;
+ }
+
+ ///
+ /// Symmetric tridiagonal QL algorithm.
+ ///
+ /// Data array of matrix V (eigenvectors)
+ /// Arrays for internal storage of real parts of eigenvalues
+ /// Arrays for internal storage of imaginary parts of eigenvalues
+ /// Order of initial matrix
+ /// This is derived from the Algol procedures tql2, by
+ /// Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
+ /// Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
+ /// Fortran subroutine in EISPACK.
+ private static void SymmetricDiagonalize(double[] a, double[] d, double[] e, int order)
+ {
+ const int Maxiter = 1000;
+
+ for (var i = 1; i < order; i++)
+ {
+ e[i - 1] = e[i];
+ }
+
+ e[order - 1] = 0.0;
+
+ var f = 0.0;
+ var tst1 = 0.0;
+ var eps = Precision.DoubleMachinePrecision;
+ for (var l = 0; l < order; l++)
+ {
+ // Find small subdiagonal element
+ tst1 = Math.Max(tst1, Math.Abs(d[l]) + Math.Abs(e[l]));
+ var m = l;
+ while (m < order)
+ {
+ if (Math.Abs(e[m]) <= eps * tst1)
+ {
+ break;
+ }
+
+ m++;
+ }
+
+ // If m == l, d[l] is an eigenvalue,
+ // otherwise, iterate.
+ if (m > l)
+ {
+ var iter = 0;
+ do
+ {
+ iter = iter + 1; // (Could check iteration count here.)
+
+ // Compute implicit shift
+ var g = d[l];
+ var p = (d[l + 1] - g) / (2.0 * e[l]);
+ var r = SpecialFunctions.Hypotenuse(p, 1.0);
+ if (p < 0)
+ {
+ r = -r;
+ }
+
+ d[l] = e[l] / (p + r);
+ d[l + 1] = e[l] * (p + r);
+
+ var dl1 = d[l + 1];
+ var h = g - d[l];
+ for (var i = l + 2; i < order; i++)
+ {
+ d[i] -= h;
+ }
+
+ f = f + h;
+
+ // Implicit QL transformation.
+ p = d[m];
+ var c = 1.0;
+ var c2 = c;
+ var c3 = c;
+ var el1 = e[l + 1];
+ var s = 0.0;
+ var s2 = 0.0;
+ for (var i = m - 1; i >= l; i--)
+ {
+ c3 = c2;
+ c2 = c;
+ s2 = s;
+ g = c * e[i];
+ h = c * p;
+ r = SpecialFunctions.Hypotenuse(p, e[i]);
+ e[i + 1] = s * r;
+ s = e[i] / r;
+ c = p / r;
+ p = (c * d[i]) - (s * g);
+ d[i + 1] = h + (s * ((c * g) + (s * d[i])));
+
+ // Accumulate transformation.
+ for (var k = 0; k < order; k++)
+ {
+ h = a[((i + 1) * order) + k];
+ a[((i + 1) * order) + k] = (s * a[(i * order) + k]) + (c * h);
+ a[(i * order) + k] = (c * a[(i * order) + k]) - (s * h);
+ }
+ }
+
+ p = (-s) * s2 * c3 * el1 * e[l] / dl1;
+ e[l] = s * p;
+ d[l] = c * p;
+
+ // Check for convergence. If too many iterations have been performed,
+ // throw exception that Convergence Failed
+ if (iter >= Maxiter)
+ {
+ throw new ArgumentException(Resources.ConvergenceFailed);
+ }
+ }
+ while (Math.Abs(e[l]) > eps * tst1);
+ }
+
+ d[l] = d[l] + f;
+ e[l] = 0.0;
+ }
+
+ // Sort eigenvalues and corresponding vectors.
+ for (var i = 0; i < order - 1; i++)
+ {
+ var k = i;
+ var p = d[i];
+ for (var j = i + 1; j < order; j++)
+ {
+ if (d[j] < p)
+ {
+ k = j;
+ p = d[j];
+ }
+ }
+
+ if (k != i)
+ {
+ d[k] = d[i];
+ d[i] = p;
+ for (var j = 0; j < order; j++)
+ {
+ p = a[(i * order) + j];
+ a[(i * order) + j] = a[(k * order) + j];
+ a[(k * order) + j] = p;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Nonsymmetric reduction to Hessenberg form.
+ ///
+ /// Data array of matrix V (eigenvectors)
+ /// Array for internal storage of nonsymmetric Hessenberg form.
+ /// Order of initial matrix
+ /// This is derived from the Algol procedures orthes and ortran,
+ /// by Martin and Wilkinson, Handbook for Auto. Comp.,
+ /// Vol.ii-Linear Algebra, and the corresponding
+ /// Fortran subroutines in EISPACK.
+ private static void NonsymmetricReduceToHessenberg(double[] a, double[,] matrixH, int order)
+ {
+ var ort = new double[order];
+
+ for (var m = 1; m < order - 1; m++)
+ {
+ // Scale column.
+ var scale = 0.0;
+ for (var i = m; i < order; i++)
+ {
+ scale = scale + Math.Abs(matrixH[i, m - 1]);
+ }
+
+ if (scale != 0.0)
+ {
+ // Compute Householder transformation.
+ var h = 0.0;
+ for (var i = order - 1; i >= m; i--)
+ {
+ ort[i] = matrixH[i, m - 1] / scale;
+ h += ort[i] * ort[i];
+ }
+
+ var g = Math.Sqrt(h);
+ if (ort[m] > 0)
+ {
+ g = -g;
+ }
+
+ h = h - (ort[m] * g);
+ ort[m] = ort[m] - g;
+
+ // Apply Householder similarity transformation
+ // H = (I-u*u'/h)*H*(I-u*u')/h)
+ for (var j = m; j < order; j++)
+ {
+ var f = 0.0;
+ for (var i = order - 1; i >= m; i--)
+ {
+ f += ort[i] * matrixH[i, j];
+ }
+
+ f = f / h;
+ for (var i = m; i < order; i++)
+ {
+ matrixH[i, j] -= f * ort[i];
+ }
+ }
+
+ for (var i = 0; i < order; i++)
+ {
+ var f = 0.0;
+ for (var j = order - 1; j >= m; j--)
+ {
+ f += ort[j] * matrixH[i, j];
+ }
+
+ f = f / h;
+ for (var j = m; j < order; j++)
+ {
+ matrixH[i, j] -= f * ort[j];
+ }
+ }
+
+ ort[m] = scale * ort[m];
+ matrixH[m, m - 1] = scale * g;
+ }
+ }
+
+ // Accumulate transformations (Algol's ortran).
+ for (var i = 0; i < order; i++)
+ {
+ for (var j = 0; j < order; j++)
+ {
+ a[(j * order) + i] = i == j ? 1.0 : 0.0;
+ }
+ }
+
+ for (var m = order - 2; m >= 1; m--)
+ {
+ if (matrixH[m, m - 1] != 0.0)
+ {
+ for (var i = m + 1; i < order; i++)
+ {
+ ort[i] = matrixH[i, m - 1];
+ }
+
+ for (var j = m; j < order; j++)
+ {
+ var g = 0.0;
+ for (var i = m; i < order; i++)
+ {
+ g += ort[i] * a[(j * order) + i];
+ }
+
+ // Double division avoids possible underflow
+ g = (g / ort[m]) / matrixH[m, m - 1];
+ for (var i = m; i < order; i++)
+ {
+ a[(j * order) + i] += g * ort[i];
+ }
+ }
+ }
+ }
+ }
+
+ ///
+ /// Nonsymmetric reduction from Hessenberg to real Schur form.
+ ///
+ /// Data array of matrix V (eigenvectors)
+ /// Array for internal storage of nonsymmetric Hessenberg form.
+ /// Arrays for internal storage of real parts of eigenvalues
+ /// Arrays for internal storage of imaginary parts of eigenvalues
+ /// Order of initial matrix
+ /// This is derived from the Algol procedure hqr2,
+ /// by Martin and Wilkinson, Handbook for Auto. Comp.,
+ /// Vol.ii-Linear Algebra, and the corresponding
+ /// Fortran subroutine in EISPACK.
+ private static void NonsymmetricReduceHessenberToRealSchur(double[] a, double[,] matrixH, double[] d, double[] e, int order)
+ {
+ // Initialize
+ var n = order - 1;
+ var eps = Precision.DoubleMachinePrecision;
+ var exshift = 0.0;
+ double p = 0, q = 0, r = 0, s = 0, z = 0, w, x, y;
+
+ // Store roots isolated by balanc and compute matrix norm
+ var norm = 0.0;
+ for (var i = 0; i < order; i++)
+ {
+ for (var j = Math.Max(i - 1, 0); j < order; j++)
+ {
+ norm = norm + Math.Abs(matrixH[i, j]);
+ }
+ }
+
+ // Outer loop over eigenvalue index
+ var iter = 0;
+ while (n >= 0)
+ {
+ // Look for single small sub-diagonal element
+ var l = n;
+ while (l > 0)
+ {
+ s = Math.Abs(matrixH[l - 1, l - 1]) + Math.Abs(matrixH[l, l]);
+
+ if (s == 0.0)
+ {
+ s = norm;
+ }
+
+ if (Math.Abs(matrixH[l, l - 1]) < eps * s)
+ {
+ break;
+ }
+
+ l--;
+ }
+
+ // Check for convergence
+ // One root found
+ if (l == n)
+ {
+ matrixH[n, n] = matrixH[n, n] + exshift;
+ d[n] = matrixH[n, n];
+ e[n] = 0.0;
+ n--;
+ iter = 0;
+
+ // Two roots found
+ }
+ else if (l == n - 1)
+ {
+ w = matrixH[n, n - 1] * matrixH[n - 1, n];
+ p = (matrixH[n - 1, n - 1] - matrixH[n, n]) / 2.0;
+ q = (p * p) + w;
+ z = Math.Sqrt(Math.Abs(q));
+ matrixH[n, n] = matrixH[n, n] + exshift;
+ matrixH[n - 1, n - 1] = matrixH[n - 1, n - 1] + exshift;
+ x = matrixH[n, n];
+
+ // Real pair
+ if (q >= 0)
+ {
+ if (p >= 0)
+ {
+ z = p + z;
+ }
+ else
+ {
+ z = p - z;
+ }
+
+ d[n - 1] = x + z;
+
+ d[n] = d[n - 1];
+ if (z != 0.0)
+ {
+ d[n] = x - (w / z);
+ }
+
+ e[n - 1] = 0.0;
+ e[n] = 0.0;
+ x = matrixH[n, n - 1];
+ s = Math.Abs(x) + Math.Abs(z);
+ p = x / s;
+ q = z / s;
+ r = Math.Sqrt((p * p) + (q * q));
+ p = p / r;
+ q = q / r;
+
+ // Row modification
+ for (var j = n - 1; j < order; j++)
+ {
+ z = matrixH[n - 1, j];
+ matrixH[n - 1, j] = (q * z) + (p * matrixH[n, j]);
+ matrixH[n, j] = (q * matrixH[n, j]) - (p * z);
+ }
+
+ // Column modification
+ for (var i = 0; i <= n; i++)
+ {
+ z = matrixH[i, n - 1];
+ matrixH[i, n - 1] = (q * z) + (p * matrixH[i, n]);
+ matrixH[i, n] = (q * matrixH[i, n]) - (p * z);
+ }
+
+ // Accumulate transformations
+ for (var i = 0; i < order; i++)
+ {
+ z = a[((n - 1) * order) + i];
+ a[((n - 1) * order) + i] = (q * z) + (p * a[(n * order) + i]);
+ a[(n * order) + i] = (q * a[(n * order) + i]) - (p * z);
+ }
+
+ // Complex pair
+ }
+ else
+ {
+ d[n - 1] = x + p;
+ d[n] = x + p;
+ e[n - 1] = z;
+ e[n] = -z;
+ }
+
+ n = n - 2;
+ iter = 0;
+
+ // No convergence yet
+ }
+ else
+ {
+ // Form shift
+ x = matrixH[n, n];
+ y = 0.0;
+ w = 0.0;
+ if (l < n)
+ {
+ y = matrixH[n - 1, n - 1];
+ w = matrixH[n, n - 1] * matrixH[n - 1, n];
+ }
+
+ // Wilkinson's original ad hoc shift
+ if (iter == 10)
+ {
+ exshift += x;
+ for (var i = 0; i <= n; i++)
+ {
+ matrixH[i, i] -= x;
+ }
+
+ s = Math.Abs(matrixH[n, n - 1]) + Math.Abs(matrixH[n - 1, n - 2]);
+ x = y = 0.75 * s;
+ w = (-0.4375) * s * s;
+ }
+
+ // MATLAB's new ad hoc shift
+ if (iter == 30)
+ {
+ s = (y - x) / 2.0;
+ s = (s * s) + w;
+ if (s > 0)
+ {
+ s = Math.Sqrt(s);
+ if (y < x)
+ {
+ s = -s;
+ }
+
+ s = x - (w / (((y - x) / 2.0) + s));
+ for (var i = 0; i <= n; i++)
+ {
+ matrixH[i, i] -= s;
+ }
+
+ exshift += s;
+ x = y = w = 0.964;
+ }
+ }
+
+ iter = iter + 1; // (Could check iteration count here.)
+
+ // Look for two consecutive small sub-diagonal elements
+ var m = n - 2;
+ while (m >= l)
+ {
+ z = matrixH[m, m];
+ r = x - z;
+ s = y - z;
+ p = (((r * s) - w) / matrixH[m + 1, m]) + matrixH[m, m + 1];
+ q = matrixH[m + 1, m + 1] - z - r - s;
+ r = matrixH[m + 2, m + 1];
+ s = Math.Abs(p) + Math.Abs(q) + Math.Abs(r);
+ p = p / s;
+ q = q / s;
+ r = r / s;
+
+ if (m == l)
+ {
+ break;
+ }
+
+ if (Math.Abs(matrixH[m, m - 1]) * (Math.Abs(q) + Math.Abs(r)) < eps * (Math.Abs(p) * (Math.Abs(matrixH[m - 1, m - 1]) + Math.Abs(z) + Math.Abs(matrixH[m + 1, m + 1]))))
+ {
+ break;
+ }
+
+ m--;
+ }
+
+ for (var i = m + 2; i <= n; i++)
+ {
+ matrixH[i, i - 2] = 0.0;
+ if (i > m + 2)
+ {
+ matrixH[i, i - 3] = 0.0;
+ }
+ }
+
+ // Double QR step involving rows l:n and columns m:n
+ for (var k = m; k <= n - 1; k++)
+ {
+ bool notlast = k != n - 1;
+
+ if (k != m)
+ {
+ p = matrixH[k, k - 1];
+ q = matrixH[k + 1, k - 1];
+ r = notlast ? matrixH[k + 2, k - 1] : 0.0;
+ x = Math.Abs(p) + Math.Abs(q) + Math.Abs(r);
+ if (x != 0.0)
+ {
+ p = p / x;
+ q = q / x;
+ r = r / x;
+ }
+ }
+
+ if (x == 0.0)
+ {
+ break;
+ }
+
+ s = Math.Sqrt((p * p) + (q * q) + (r * r));
+ if (p < 0)
+ {
+ s = -s;
+ }
+
+ if (s != 0.0)
+ {
+ if (k != m)
+ {
+ matrixH[k, k - 1] = (-s) * x;
+ }
+ else if (l != m)
+ {
+ matrixH[k, k - 1] = -matrixH[k, k - 1];
+ }
+
+ p = p + s;
+ x = p / s;
+ y = q / s;
+ z = r / s;
+ q = q / p;
+ r = r / p;
+
+ // Row modification
+ for (var j = k; j < order; j++)
+ {
+ p = matrixH[k, j] + (q * matrixH[k + 1, j]);
+
+ if (notlast)
+ {
+ p = p + (r * matrixH[k + 2, j]);
+ matrixH[k + 2, j] = matrixH[k + 2, j] - (p * z);
+ }
+
+ matrixH[k, j] = matrixH[k, j] - (p * x);
+ matrixH[k + 1, j] = matrixH[k + 1, j] - (p * y);
+ }
+
+ // Column modification
+ for (var i = 0; i <= Math.Min(n, k + 3); i++)
+ {
+ p = (x * matrixH[i, k]) + (y * matrixH[i, k + 1]);
+
+ if (notlast)
+ {
+ p = p + (z * matrixH[i, k + 2]);
+ matrixH[i, k + 2] = matrixH[i, k + 2] - (p * r);
+ }
+
+ matrixH[i, k] = matrixH[i, k] - p;
+ matrixH[i, k + 1] = matrixH[i, k + 1] - (p * q);
+ }
+
+ // Accumulate transformations
+ for (var i = 0; i < order; i++)
+ {
+ p = (x * a[(k * order) + i]) + (y * a[((k + 1) * order) + i]);
+
+ if (notlast)
+ {
+ p = p + (z * a[((k + 2) * order) + i]);
+ a[((k + 2) * order) + i] -= p * r;
+ }
+
+ a[(k * order) + i] -= p;
+ a[((k + 1) * order) + i] -= p * q;
+ }
+ } // (s != 0)
+ } // k loop
+ } // check convergence
+ } // while (n >= low)
+
+ // Backsubstitute to find vectors of upper triangular form
+ if (norm == 0.0)
+ {
+ return;
+ }
+
+ for (n = order - 1; n >= 0; n--)
+ {
+ double t;
+
+ p = d[n];
+ q = e[n];
+
+ // Real vector
+ if (q == 0.0)
+ {
+ var l = n;
+ matrixH[n, n] = 1.0;
+ for (var i = n - 1; i >= 0; i--)
+ {
+ w = matrixH[i, i] - p;
+ r = 0.0;
+ for (var j = l; j <= n; j++)
+ {
+ r = r + (matrixH[i, j] * matrixH[j, n]);
+ }
+
+ if (e[i] < 0.0)
+ {
+ z = w;
+ s = r;
+ }
+ else
+ {
+ l = i;
+ if (e[i] == 0.0)
+ {
+ if (w != 0.0)
+ {
+ matrixH[i, n] = (-r) / w;
+ }
+ else
+ {
+ matrixH[i, n] = (-r) / (eps * norm);
+ }
+
+ // Solve real equations
+ }
+ else
+ {
+ x = matrixH[i, i + 1];
+ y = matrixH[i + 1, i];
+ q = ((d[i] - p) * (d[i] - p)) + (e[i] * e[i]);
+ t = ((x * s) - (z * r)) / q;
+ matrixH[i, n] = t;
+ if (Math.Abs(x) > Math.Abs(z))
+ {
+ matrixH[i + 1, n] = (-r - (w * t)) / x;
+ }
+ else
+ {
+ matrixH[i + 1, n] = (-s - (y * t)) / z;
+ }
+ }
+
+ // Overflow control
+ t = Math.Abs(matrixH[i, n]);
+ if ((eps * t) * t > 1)
+ {
+ for (var j = i; j <= n; j++)
+ {
+ matrixH[j, n] = matrixH[j, n] / t;
+ }
+ }
+ }
+ }
+
+ // Complex vector
+ }
+ else if (q < 0)
+ {
+ var l = n - 1;
+
+ // Last vector component imaginary so matrix is triangular
+ if (Math.Abs(matrixH[n, n - 1]) > Math.Abs(matrixH[n - 1, n]))
+ {
+ matrixH[n - 1, n - 1] = q / matrixH[n, n - 1];
+ matrixH[n - 1, n] = (-(matrixH[n, n] - p)) / matrixH[n, n - 1];
+ }
+ else
+ {
+ var res = Cdiv(0.0, -matrixH[n - 1, n], matrixH[n - 1, n - 1] - p, q);
+ matrixH[n - 1, n - 1] = res.Real;
+ matrixH[n - 1, n] = res.Imaginary;
+ }
+
+ matrixH[n, n - 1] = 0.0;
+ matrixH[n, n] = 1.0;
+ for (var i = n - 2; i >= 0; i--)
+ {
+ double ra = 0.0;
+ double sa = 0.0;
+ for (var j = l; j <= n; j++)
+ {
+ ra = ra + (matrixH[i, j] * matrixH[j, n - 1]);
+ sa = sa + (matrixH[i, j] * matrixH[j, n]);
+ }
+
+ w = matrixH[i, i] - p;
+
+ if (e[i] < 0.0)
+ {
+ z = w;
+ r = ra;
+ s = sa;
+ }
+ else
+ {
+ l = i;
+ if (e[i] == 0.0)
+ {
+ var res = Cdiv(-ra, -sa, w, q);
+ matrixH[i, n - 1] = res.Real;
+ matrixH[i, n] = res.Imaginary;
+ }
+ else
+ {
+ // Solve complex equations
+ x = matrixH[i, i + 1];
+ y = matrixH[i + 1, i];
+
+ double vr = ((d[i] - p) * (d[i] - p)) + (e[i] * e[i]) - (q * q);
+ double vi = (d[i] - p) * 2.0 * q;
+ if ((vr == 0.0) && (vi == 0.0))
+ {
+ vr = eps * norm * (Math.Abs(w) + Math.Abs(q) + Math.Abs(x) + Math.Abs(y) + Math.Abs(z));
+ }
+
+ var res = Cdiv((x * r) - (z * ra) + (q * sa), (x * s) - (z * sa) - (q * ra), vr, vi);
+ matrixH[i, n - 1] = res.Real;
+ matrixH[i, n] = res.Imaginary;
+ if (Math.Abs(x) > (Math.Abs(z) + Math.Abs(q)))
+ {
+ matrixH[i + 1, n - 1] = (-ra - (w * matrixH[i, n - 1]) + (q * matrixH[i, n])) / x;
+ matrixH[i + 1, n] = (-sa - (w * matrixH[i, n]) - (q * matrixH[i, n - 1])) / x;
+ }
+ else
+ {
+ res = Cdiv(-r - (y * matrixH[i, n - 1]), -s - (y * matrixH[i, n]), z, q);
+ matrixH[i + 1, n - 1] = res.Real;
+ matrixH[i + 1, n] = res.Imaginary;
+ }
+ }
+
+ // Overflow control
+ t = Math.Max(Math.Abs(matrixH[i, n - 1]), Math.Abs(matrixH[i, n]));
+ if ((eps * t) * t > 1)
+ {
+ for (var j = i; j <= n; j++)
+ {
+ matrixH[j, n - 1] = matrixH[j, n - 1] / t;
+ matrixH[j, n] = matrixH[j, n] / t;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Back transformation to get eigenvectors of original matrix
+ for (var j = order - 1; j >= 0; j--)
+ {
+ for (var i = 0; i < order; i++)
+ {
+ z = 0.0;
+ for (var k = 0; k <= j; k++)
+ {
+ z = z + (a[(k * order) + i] * matrixH[k, j]);
+ }
+
+ a[(j * order) + i] = z;
+ }
+ }
+ }
+
+ ///
+ /// Complex scalar division X/Y.
+ ///
+ /// Real part of X
+ /// Imaginary part of X
+ /// Real part of Y
+ /// Imaginary part of Y
+ /// Division result as a number.
+ private static Complex Cdiv(double xreal, double ximag, double yreal, double yimag)
+ {
+ if (Math.Abs(yimag) < Math.Abs(yreal))
+ {
+ return new Complex((xreal + (ximag * (yimag / yreal))) / (yreal + (yimag * (yimag / yreal))), (ximag - (xreal * (yimag / yreal))) / (yreal + (yimag * (yimag / yreal))));
+ }
+
+ return new Complex((ximag + (xreal * (yreal / yimag))) / (yimag + (yreal * (yreal / yimag))), (-xreal + (ximag * (yreal / yimag))) / (yimag + (yreal * (yreal / yimag))));
+ }
+
+ ///
+ /// Solves a system of linear equations, AX = B, with A SVD factorized.
+ ///
+ /// The right hand side , B.
+ /// The left hand side , X.
+ public override void Solve(Matrix input, Matrix result)
+ {
+ // Check for proper arguments.
+ if (input == null)
+ {
+ throw new ArgumentNullException("input");
+ }
+
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ // The solution X should have the same number of columns as B
+ if (input.ColumnCount != result.ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension);
+ }
+
+ // The dimension compatibility conditions for X = A\B require the two matrices A and B to have the same number of rows
+ if (VectorEv.Count != input.RowCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension);
+ }
+
+ // The solution X row dimension is equal to the column dimension of A
+ if (VectorEv.Count != result.RowCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension);
+ }
+
+ if (IsSymmetric)
+ {
+ var order = VectorEv.Count;
+ var tmp = new double[order];
+
+ for (var k = 0; k < order; k++)
+ {
+ for (var j = 0; j < order; j++)
+ {
+ double value = 0;
+ if (j < order)
+ {
+ for (var i = 0; i < order; i++)
+ {
+ value += ((DenseMatrix)MatrixEv).Data[(j * order) + i] * input.At(i, k);
+ }
+
+ value /= VectorEv[j].Real;
+ }
+
+ tmp[j] = value;
+ }
+
+ for (var j = 0; j < order; j++)
+ {
+ double value = 0;
+ for (var i = 0; i < order; i++)
+ {
+ value += ((DenseMatrix)MatrixEv).Data[(i * order) + j] * tmp[i];
+ }
+
+ result[j, k] = value;
+ }
+ }
+ }
+ else
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSymmetric);
+ }
+ }
+
+ ///
+ /// Solves a system of linear equations, Ax = b, with A EVD factorized.
+ ///
+ /// The right hand side vector, b.
+ /// The left hand side , x.
+ public override void Solve(Vector input, Vector result)
+ {
+ if (input == null)
+ {
+ throw new ArgumentNullException("input");
+ }
+
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ // Ax=b where A is an m x m matrix
+ // Check that b is a column vector with m entries
+ if (VectorEv.Count != input.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength);
+ }
+
+ // Check that x is a column vector with n entries
+ if (VectorEv.Count != result.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions);
+ }
+
+ if (IsSymmetric)
+ {
+ // Symmetric case -> x = V * inv(λ) * VT * b;
+ var order = VectorEv.Count;
+ var tmp = new double[order];
+ double value;
+
+ for (var j = 0; j < order; j++)
+ {
+ value = 0;
+ if (j < order)
+ {
+ for (var i = 0; i < order; i++)
+ {
+ value += ((DenseMatrix)MatrixEv).Data[(j * order) + i] * input[i];
+ }
+
+ value /= VectorEv[j].Real;
+ }
+
+ tmp[j] = value;
+ }
+
+ for (var j = 0; j < order; j++)
+ {
+ value = 0;
+ for (int i = 0; i < order; i++)
+ {
+ value += ((DenseMatrix)MatrixEv).Data[(i * order) + j] * tmp[i];
+ }
+
+ result[j] = value;
+ }
+ }
+ else
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSymmetric);
+ }
+ }
+
+ ///
+ /// Multiply two values T*T
+ ///
+ /// Left operand value
+ /// Right operand value
+ /// Result of multiplication
+ protected sealed override double MultiplyT(double val1, double val2)
+ {
+ return val1 * val2;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/DenseGramSchmidt.cs b/src/Numerics/LinearAlgebra/Double/Factorization/DenseGramSchmidt.cs
new file mode 100644
index 00000000..3b190b9f
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/Factorization/DenseGramSchmidt.cs
@@ -0,0 +1,237 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+// http://mathnetnumerics.codeplex.com
+//
+// Copyright (c) 2009-2010 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
+{
+ using System;
+ using Generic;
+ using Generic.Factorization;
+ using Properties;
+ using Threading;
+
+ ///
+ /// A class which encapsulates the functionality of the QR decomposition Modified Gram-Schmidt Orthogonalization.
+ /// Any real square matrix A may be decomposed as A = QR where Q is an orthogonal mxn matrix and R is an nxn upper triangular matrix.
+ ///
+ ///
+ /// The computation of the QR decomposition is done at construction time by modified Gram-Schmidt Orthogonalization.
+ ///
+ public class DenseGramSchmidt : GramSchmidt
+ {
+ ///
+ /// Initializes a new instance of the class. This object creates an orthogonal matrix
+ /// using the modified Gram-Schmidt method.
+ ///
+ /// The matrix to factor.
+ /// If is null.
+ /// If row count is less then column count
+ /// If is rank deficient
+ public DenseGramSchmidt(DenseMatrix matrix)
+ {
+ if (matrix == null)
+ {
+ throw new ArgumentNullException("matrix");
+ }
+
+ if (matrix.RowCount < matrix.ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions);
+ }
+
+ MatrixQ = matrix.Clone();
+ MatrixR = matrix.CreateMatrix(matrix.ColumnCount, matrix.ColumnCount);
+ Factorize(((DenseMatrix)MatrixQ).Data, MatrixQ.RowCount, MatrixQ.ColumnCount, ((DenseMatrix)MatrixR).Data);
+ }
+
+ ///
+ /// Factorize matrix using the modified Gram-Schmidt method.
+ ///
+ /// Initial matrix. On exit is replaced by Q.
+ /// Number of rows in Q.
+ /// Number of columns in Q.
+ /// On exit is filled by R.
+ private static void Factorize(double[] q, int rowsQ, int columnsQ, double[] r)
+ {
+ for (var k = 0; k < columnsQ; k++)
+ {
+ var norm = 0.0;
+ for (var i = 0; i < rowsQ; i++)
+ {
+ norm += q[(k * rowsQ) + i] * q[(k * rowsQ) + i];
+ }
+
+ norm = Math.Sqrt(norm);
+ if (norm == 0.0)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixNotRankDeficient);
+ }
+
+ r[(k * columnsQ) + k] = norm;
+ for (var i = 0; i < rowsQ; i++)
+ {
+ q[(k * rowsQ) + i] /= norm;
+ }
+
+ for (var j = k + 1; j < columnsQ; j++)
+ {
+ int k1 = k;
+ int j1 = j;
+ var dot = CommonParallel.Aggregate(0, rowsQ, index => q[(k1 * rowsQ) + index] * q[(j1 * rowsQ) + index]);
+ r[(j * columnsQ) + k] = dot;
+ for (var i = 0; i < rowsQ; i++)
+ {
+ var value = q[(j * rowsQ) + i] - (q[(k * rowsQ) + i] * dot);
+ q[(j * rowsQ) + i] = value;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Solves a system of linear equations, AX = B, with A QR factorized.
+ ///
+ /// The right hand side , B.
+ /// The left hand side , X.
+ public override void Solve(Matrix input, Matrix result)
+ {
+ // Check for proper arguments.
+ if (input == null)
+ {
+ throw new ArgumentNullException("input");
+ }
+
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ // The solution X should have the same number of columns as B
+ if (input.ColumnCount != result.ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension);
+ }
+
+ // The dimension compatibility conditions for X = A\B require the two matrices A and B to have the same number of rows
+ if (MatrixQ.RowCount != input.RowCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension);
+ }
+
+ // The solution X row dimension is equal to the column dimension of A
+ if (MatrixQ.ColumnCount != result.RowCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension);
+ }
+
+ var dinput = input as DenseMatrix;
+ if (dinput == null)
+ {
+ throw new NotImplementedException("Can only do GramSchmidt factorization for dense matrices at the moment.");
+ }
+
+ var dresult = result as DenseMatrix;
+ if (dresult == null)
+ {
+ throw new NotImplementedException("Can only do GramSchmidt factorization for dense matrices at the moment.");
+ }
+
+ Control.LinearAlgebraProvider.QRSolveFactored(((DenseMatrix)MatrixQ).Data, ((DenseMatrix)MatrixR).Data, MatrixQ.RowCount, MatrixQ.ColumnCount, dinput.Data, input.ColumnCount, dresult.Data);
+ }
+
+ ///
+ /// Solves a system of linear equations, Ax = b, with A QR factorized.
+ ///
+ /// The right hand side vector, b.
+ /// The left hand side , x.
+ public override void Solve(Vector input, Vector result)
+ {
+ if (input == null)
+ {
+ throw new ArgumentNullException("input");
+ }
+
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ // Ax=b where A is an m x n matrix
+ // Check that b is a column vector with m entries
+ if (MatrixQ.RowCount != input.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength);
+ }
+
+ // Check that x is a column vector with n entries
+ if (MatrixQ.ColumnCount != result.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions);
+ }
+
+ var dinput = input as DenseVector;
+ if (dinput == null)
+ {
+ throw new NotImplementedException("Can only do GramSchmidt factorization for dense vectors at the moment.");
+ }
+
+ var dresult = result as DenseVector;
+ if (dresult == null)
+ {
+ throw new NotImplementedException("Can only do GramSchmidt factorization for dense vectors at the moment.");
+ }
+
+ Control.LinearAlgebraProvider.QRSolveFactored(((DenseMatrix)MatrixQ).Data, ((DenseMatrix)MatrixR).Data, MatrixQ.RowCount, MatrixQ.ColumnCount, dinput.Data, 1, dresult.Data);
+ }
+
+ #region Simple arithmetic of type T
+
+ ///
+ /// Multiply two values T*T
+ ///
+ /// Left operand value
+ /// Right operand value
+ /// Result of multiplication
+ protected sealed override double MultiplyT(double val1, double val2)
+ {
+ return val1 * val2;
+ }
+
+ ///
+ /// Returns the absolute value of a specified number.
+ ///
+ /// A number whose absolute is to be found
+ /// Absolute value
+ protected sealed override double AbsoluteT(double val1)
+ {
+ return Math.Abs(val1);
+ }
+ #endregion
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/UserEvd.cs b/src/Numerics/LinearAlgebra/Double/Factorization/UserEvd.cs
index c6acd360..3850d09f 100644
--- a/src/Numerics/LinearAlgebra/Double/Factorization/UserEvd.cs
+++ b/src/Numerics/LinearAlgebra/Double/Factorization/UserEvd.cs
@@ -113,11 +113,11 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
if (e[i] > 0)
{
- MatrixD[i, i + 1] = e[i];
+ MatrixD.At(i, i + 1, e[i]);
}
else if (e[i] < 0)
{
- MatrixD[i, i - 1] = e[i];
+ MatrixD.At(i, i - 1, e[i]);
}
}
@@ -156,9 +156,9 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
e[i] = d[i - 1];
for (var j = 0; j < i; j++)
{
- d[j] = MatrixEv[i - 1, j];
- MatrixEv[i, j] = 0.0;
- MatrixEv[j, i] = 0.0;
+ d[j] = MatrixEv.At(i - 1, j);
+ MatrixEv.At(i, j, 0.0);
+ MatrixEv.At(j, i, 0.0);
}
}
else
@@ -190,13 +190,13 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
for (var j = 0; j < i; j++)
{
f = d[j];
- MatrixEv[j, i] = f;
- g = e[j] + (MatrixEv[j, j] * f);
+ MatrixEv.At(j, i, f);
+ g = e[j] + (MatrixEv.At(j, j) * f);
for (var k = j + 1; k <= i - 1; k++)
{
- g += MatrixEv[k, j] * d[k];
- e[k] += MatrixEv[k, j] * f;
+ g += MatrixEv.At(k, j) * d[k];
+ e[k] += MatrixEv.At(k, j) * f;
}
e[j] = g;
@@ -224,11 +224,11 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
for (var k = j; k <= i - 1; k++)
{
- MatrixEv[k, j] -= (f * e[k]) + (g * d[k]);
+ MatrixEv.At(k, j, MatrixEv.At(k, j) - (f * e[k]) - (g * d[k]));
}
- d[j] = MatrixEv[i - 1, j];
- MatrixEv[i, j] = 0.0;
+ d[j] = MatrixEv.At(i - 1, j);
+ MatrixEv.At(i, j, 0.0);
}
}
@@ -238,14 +238,14 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
// Accumulate transformations.
for (var i = 0; i < order - 1; i++)
{
- MatrixEv[order - 1, i] = MatrixEv[i, i];
- MatrixEv[i, i] = 1.0;
+ MatrixEv.At(order - 1, i, MatrixEv.At(i, i));
+ MatrixEv.At(i, i, 1.0);
var h = d[i + 1];
if (h != 0.0)
{
for (var k = 0; k <= i; k++)
{
- d[k] = MatrixEv[k, i + 1] / h;
+ d[k] = MatrixEv.At(k, i + 1) / h;
}
for (var j = 0; j <= i; j++)
@@ -253,29 +253,29 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
var g = 0.0;
for (var k = 0; k <= i; k++)
{
- g += MatrixEv[k, i + 1] * MatrixEv[k, j];
+ g += MatrixEv.At(k, i + 1) * MatrixEv.At(k, j);
}
for (var k = 0; k <= i; k++)
{
- MatrixEv[k, j] -= g * d[k];
+ MatrixEv.At(k, j, MatrixEv.At(k, j) - g * d[k]);
}
}
}
for (var k = 0; k <= i; k++)
{
- MatrixEv[k, i + 1] = 0.0;
+ MatrixEv.At(k, i + 1, 0.0);
}
}
for (var j = 0; j < order; j++)
{
- d[j] = MatrixEv[order - 1, j];
- MatrixEv[order - 1, j] = 0.0;
+ d[j] = MatrixEv.At(order - 1, j);
+ MatrixEv.At(order - 1, j, 0.0);
}
- MatrixEv[order - 1, order - 1] = 1.0;
+ MatrixEv.At(order - 1, order - 1, 1.0);
e[0] = 0.0;
}
@@ -373,9 +373,9 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
// Accumulate transformation.
for (var k = 0; k < order; k++)
{
- h = MatrixEv[k, i + 1];
- MatrixEv[k, i + 1] = (s * MatrixEv[k, i]) + (c * h);
- MatrixEv[k, i] = (c * MatrixEv[k, i]) - (s * h);
+ h = MatrixEv.At(k, i + 1);
+ MatrixEv.At(k, i + 1, (s * MatrixEv.At(k, i)) + (c * h));
+ MatrixEv.At(k, i, (c * MatrixEv.At(k, i)) - (s * h));
}
}
@@ -417,9 +417,9 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
d[i] = p;
for (var j = 0; j < order; j++)
{
- p = MatrixEv[j, i];
- MatrixEv[j, i] = MatrixEv[j, k];
- MatrixEv[j, k] = p;
+ p = MatrixEv.At(j, i);
+ MatrixEv.At(j, i, MatrixEv.At(j, k));
+ MatrixEv.At(j, k, p);
}
}
}
@@ -508,7 +508,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
{
for (var j = 0; j < order; j++)
{
- MatrixEv[i, j] = i == j ? 1.0 : 0.0;
+ MatrixEv.At(i, j, i == j ? 1.0 : 0.0);
}
}
@@ -526,14 +526,14 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
var g = 0.0;
for (var i = m; i < order; i++)
{
- g += ort[i] * MatrixEv[i, j];
+ g += ort[i] * MatrixEv.At(i, j);
}
// Double division avoids possible underflow
g = (g / ort[m]) / matrixH[m, m - 1];
for (var i = m; i < order; i++)
{
- MatrixEv[i, j] += g * ort[i];
+ MatrixEv.At(i, j, MatrixEv.At(i, j) + g * ort[i]);
}
}
}
@@ -663,9 +663,9 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
// Accumulate transformations
for (var i = 0; i < order; i++)
{
- z = MatrixEv[i, n - 1];
- MatrixEv[i, n - 1] = (q * z) + (p * MatrixEv[i, n]);
- MatrixEv[i, n] = (q * MatrixEv[i, n]) - (p * z);
+ z = MatrixEv.At(i, n - 1);
+ MatrixEv.At(i, n - 1, (q * z) + (p * MatrixEv.At(i, n)));
+ MatrixEv.At(i, n, (q * MatrixEv.At(i, n)) - (p * z));
}
// Complex pair
@@ -853,16 +853,16 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
// Accumulate transformations
for (var i = 0; i < order; i++)
{
- p = (x * MatrixEv[i, k]) + (y * MatrixEv[i, k + 1]);
+ p = (x * MatrixEv.At(i, k)) + (y * MatrixEv.At(i, k + 1));
if (notlast)
{
- p = p + (z * MatrixEv[i, k + 2]);
- MatrixEv[i, k + 2] = MatrixEv[i, k + 2] - (p * r);
+ p = p + (z * MatrixEv.At(i, k + 2));
+ MatrixEv.At(i, k + 2, MatrixEv.At(i, k + 2) - (p * r));
}
- MatrixEv[i, k] = MatrixEv[i, k] - p;
- MatrixEv[i, k + 1] = MatrixEv[i, k + 1] - (p * q);
+ MatrixEv.At(i, k, MatrixEv.At(i, k) - p);
+ MatrixEv.At(i, k + 1, MatrixEv.At(i, k + 1) - (p * q));
}
} // (s != 0)
} // k loop
@@ -1046,10 +1046,10 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
z = 0.0;
for (var k = 0; k <= j; k++)
{
- z = z + (MatrixEv[i, k] * matrixH[k, j]);
+ z = z + (MatrixEv.At(i, k) * matrixH[k, j]);
}
- MatrixEv[i, j] = z;
+ MatrixEv.At(i, j, z);
}
}
}
diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/GramSchmidt.cs b/src/Numerics/LinearAlgebra/Double/Factorization/UserGramSchmidt.cs
similarity index 87%
rename from src/Numerics/LinearAlgebra/Double/Factorization/GramSchmidt.cs
rename to src/Numerics/LinearAlgebra/Double/Factorization/UserGramSchmidt.cs
index 7fdc0cd4..417a5922 100644
--- a/src/Numerics/LinearAlgebra/Double/Factorization/GramSchmidt.cs
+++ b/src/Numerics/LinearAlgebra/Double/Factorization/UserGramSchmidt.cs
@@ -1,4 +1,4 @@
-//
+//
// Math.NET Numerics, part of the Math.NET Project
// http://numerics.mathdotnet.com
// http://github.com/mathnet/mathnet-numerics
@@ -42,17 +42,17 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
///
/// The computation of the QR decomposition is done at construction time by modified Gram-Schmidt Orthogonalization.
///
- public class GramSchmidt : QR
+ public class UserGramSchmidt : GramSchmidt
{
///
- /// Initializes a new instance of the class. This object creates an orthogonal matrix
+ /// Initializes a new instance of the class. This object creates an orthogonal matrix
/// using the modified Gram-Schmidt method.
///
/// The matrix to factor.
/// If is null.
/// If row count is less then column count
/// If is rank deficient
- public GramSchmidt(Matrix matrix)
+ public UserGramSchmidt(Matrix matrix)
{
if (matrix == null)
{
@@ -94,44 +94,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization
}
}
- ///
- /// Gets a value indicating whether the matrix is full rank or not.
- ///
- /// true if the matrix is full rank; otherwise false.
- public override bool IsFullRank
- {
- get
- {
- return true;
- }
- }
-
- ///
- /// Gets the determinant of the matrix for which the QR matrix was computed.
- ///
- public override double Determinant
- {
- get
- {
- if (MatrixQ.RowCount != MatrixQ.ColumnCount)
- {
- throw new ArgumentException(Resources.ArgumentMatrixSquare);
- }
-
- var det = 1.0;
- for (var i = 0; i < MatrixR.ColumnCount; i++)
- {
- det *= MatrixR.At(i, i);
- if (Math.Abs(MatrixR.At(i, i)).AlmostEqualInDecimalPlaces(0.0, 15))
- {
- return 0;
- }
- }
-
- return Math.Abs(det);
- }
- }
-
///
/// Solves a system of linear equations, AX = B, with A QR factorized.
///
diff --git a/src/Numerics/LinearAlgebra/Double/Solvers/Iterative/MlkBiCgStab.cs b/src/Numerics/LinearAlgebra/Double/Solvers/Iterative/MlkBiCgStab.cs
index 4410e705..bf600e04 100644
--- a/src/Numerics/LinearAlgebra/Double/Solvers/Iterative/MlkBiCgStab.cs
+++ b/src/Numerics/LinearAlgebra/Double/Solvers/Iterative/MlkBiCgStab.cs
@@ -35,8 +35,8 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Solvers.Iterative
using System.Diagnostics;
using System.Linq;
using Distributions;
- using Factorization;
using Generic;
+ using Generic.Factorization;
using Generic.Solvers;
using Generic.Solvers.Preconditioners;
using Generic.Solvers.Status;
@@ -635,7 +635,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Solvers.Iterative
}
// Compute the orthogonalization.
- var gs = new GramSchmidt(matrix);
+ var gs = matrix.GramSchmidt();
var orthogonalMatrix = gs.Q;
// Now transfer this to vectors
diff --git a/src/Numerics/LinearAlgebra/Double/SparseVector.cs b/src/Numerics/LinearAlgebra/Double/SparseVector.cs
index da281ae0..6ef0ad24 100644
--- a/src/Numerics/LinearAlgebra/Double/SparseVector.cs
+++ b/src/Numerics/LinearAlgebra/Double/SparseVector.cs
@@ -29,6 +29,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double
using System;
using System.Collections.Generic;
using System.Globalization;
+ using System.Linq;
using Distributions;
using Generic;
using NumberTheory;
@@ -1237,6 +1238,11 @@ namespace MathNet.Numerics.LinearAlgebra.Double
return 0.0;
}
+ if (2.0 == p)
+ {
+ return _nonZeroValues.Aggregate(0.0, SpecialFunctions.Hypotenuse);
+ }
+
if (Double.IsPositiveInfinity(p))
{
return CommonParallel.Select(0, NonZerosCount, (index, localData) => Math.Max(localData, Math.Abs(_nonZeroValues[index])), Math.Max);
diff --git a/src/Numerics/LinearAlgebra/Generic/Factorization/Evd.cs b/src/Numerics/LinearAlgebra/Generic/Factorization/Evd.cs
index f2a26d7a..faff97fb 100644
--- a/src/Numerics/LinearAlgebra/Generic/Factorization/Evd.cs
+++ b/src/Numerics/LinearAlgebra/Generic/Factorization/Evd.cs
@@ -99,21 +99,45 @@ namespace MathNet.Numerics.LinearAlgebra.Generic.Factorization
{
if (typeof(T) == typeof(double))
{
+ var dense = matrix as LinearAlgebra.Double.DenseMatrix;
+ if (dense != null)
+ {
+ return new LinearAlgebra.Double.Factorization.DenseEvd(dense) as Evd;
+ }
+
return new LinearAlgebra.Double.Factorization.UserEvd(matrix as Matrix) as Evd;
}
if (typeof(T) == typeof(float))
{
+ var dense = matrix as LinearAlgebra.Single.DenseMatrix;
+ if (dense != null)
+ {
+ return new LinearAlgebra.Single.Factorization.DenseEvd(dense) as Evd;
+ }
+
return new LinearAlgebra.Single.Factorization.UserEvd(matrix as Matrix) as Evd;
}
if (typeof(T) == typeof(Complex))
{
+ var dense = matrix as LinearAlgebra.Complex.DenseMatrix;
+ if (dense != null)
+ {
+ return new LinearAlgebra.Complex.Factorization.DenseEvd(dense) as Evd;
+ }
+
return new LinearAlgebra.Complex.Factorization.UserEvd(matrix as Matrix) as Evd;
}
if (typeof(T) == typeof(Complex32))
{
+ var dense = matrix as LinearAlgebra.Complex32.DenseMatrix;
+ if (dense != null)
+ {
+ return new LinearAlgebra.Complex32.Factorization.DenseEvd(dense) as Evd;
+ }
+
return new LinearAlgebra.Complex32.Factorization.UserEvd(matrix as Matrix) as Evd;
}
diff --git a/src/Numerics/LinearAlgebra/Generic/Factorization/ExtensionMethods.cs b/src/Numerics/LinearAlgebra/Generic/Factorization/ExtensionMethods.cs
index 0f89e881..520cf252 100644
--- a/src/Numerics/LinearAlgebra/Generic/Factorization/ExtensionMethods.cs
+++ b/src/Numerics/LinearAlgebra/Generic/Factorization/ExtensionMethods.cs
@@ -79,29 +79,9 @@ namespace MathNet.Numerics.LinearAlgebra.Generic.Factorization
/// The matrix to factor.
/// The QR decomposition object.
/// Supported data types are double, single, , and .
- public static QR GramSchmidt(this Matrix matrix) where T : struct, IEquatable, IFormattable
+ public static GramSchmidt GramSchmidt(this Matrix matrix) where T : struct, IEquatable, IFormattable
{
- if (typeof(T) == typeof(double))
- {
- return new LinearAlgebra.Double.Factorization.GramSchmidt(matrix as Matrix) as QR;
- }
-
- if (typeof(T) == typeof(float))
- {
- return new LinearAlgebra.Single.Factorization.GramSchmidt(matrix as Matrix) as QR;
- }
-
- if (typeof(T) == typeof(Complex))
- {
- return new LinearAlgebra.Complex.Factorization.GramSchmidt(matrix as Matrix) as QR;
- }
-
- if (typeof(T) == typeof(Complex32))
- {
- return new LinearAlgebra.Complex32.Factorization.GramSchmidt(matrix as Matrix) as QR;
- }
-
- throw new NotImplementedException();
+ return Factorization.GramSchmidt.Create(matrix);
}
///
diff --git a/src/Numerics/LinearAlgebra/Generic/Factorization/GramSchmidt.cs b/src/Numerics/LinearAlgebra/Generic/Factorization/GramSchmidt.cs
new file mode 100644
index 00000000..94b43bd7
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Generic/Factorization/GramSchmidt.cs
@@ -0,0 +1,138 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+// http://mathnetnumerics.codeplex.com
+// Copyright (c) 2009-2010 Math.NET
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Generic.Factorization
+{
+ using System;
+ using System.Numerics;
+ using Generic;
+ using Numerics;
+ using Properties;
+
+ ///
+ /// A class which encapsulates the functionality of the QR decomposition Modified Gram-Schmidt Orthogonalization.
+ /// Any real square matrix A may be decomposed as A = QR where Q is an orthogonal mxn matrix and R is an nxn upper triangular matrix.
+ ///
+ ///
+ /// The computation of the QR decomposition is done at construction time by modified Gram-Schmidt Orthogonalization.
+ ///
+ /// Supported data types are double, single, , and .
+ public abstract class GramSchmidt : QR
+ where T : struct, IEquatable, IFormattable
+ {
+ ///
+ /// Internal method which routes the call to perform the QR factorization to the appropriate class.
+ ///
+ /// The matrix to factor.
+ /// A QR factorization object.
+ new internal static GramSchmidt Create(Matrix matrix)
+ {
+ if (typeof(T) == typeof(double))
+ {
+ var dense = matrix as LinearAlgebra.Double.DenseMatrix;
+ if (dense != null)
+ {
+ return new LinearAlgebra.Double.Factorization.DenseGramSchmidt(dense) as GramSchmidt;
+ }
+
+ return new LinearAlgebra.Double.Factorization.UserGramSchmidt(matrix as Matrix) as GramSchmidt;
+ }
+
+ if (typeof(T) == typeof(float))
+ {
+ var dense = matrix as LinearAlgebra.Single.DenseMatrix;
+ if (dense != null)
+ {
+ return new LinearAlgebra.Single.Factorization.DenseGramSchmidt(dense) as GramSchmidt;
+ }
+
+ return new LinearAlgebra.Single.Factorization.UserGramSchmidt(matrix as Matrix) as GramSchmidt;
+ }
+
+ if (typeof(T) == typeof(Complex))
+ {
+ var dense = matrix as LinearAlgebra.Complex.DenseMatrix;
+ if (dense != null)
+ {
+ return new LinearAlgebra.Complex.Factorization.DenseGramSchmidt(dense) as GramSchmidt;
+ }
+
+ return new LinearAlgebra.Complex.Factorization.UserGramSchmidt(matrix as Matrix) as GramSchmidt;
+ }
+
+ if (typeof(T) == typeof(Complex32))
+ {
+ var dense = matrix as LinearAlgebra.Complex32.DenseMatrix;
+ if (dense != null)
+ {
+ return new LinearAlgebra.Complex32.Factorization.DenseGramSchmidt(dense) as GramSchmidt;
+ }
+
+ return new LinearAlgebra.Complex32.Factorization.UserGramSchmidt(matrix as Matrix) as GramSchmidt;
+ }
+
+ throw new NotImplementedException();
+ }
+
+ ///
+ /// Gets a value indicating whether the matrix is full rank or not.
+ ///
+ /// true if the matrix is full rank; otherwise false.
+ public sealed override bool IsFullRank
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ ///
+ /// Gets the absolute determinant value of the matrix for which the QR matrix was computed.
+ ///
+ public override double Determinant
+ {
+ get
+ {
+ if (MatrixQ.RowCount != MatrixQ.ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSquare);
+ }
+
+ var det = OneValueT;
+ for (var i = 0; i < MatrixR.ColumnCount; i++)
+ {
+ det = MultiplyT(det, MatrixR.At(i, i));
+ if (AbsoluteT(MatrixR.At(i, i)).AlmostEqualInDecimalPlaces(0.0, (typeof(T) == typeof(float) || typeof(T) == typeof(Complex32)) ? 7 : 15))
+ {
+ return 0;
+ }
+ }
+
+ return AbsoluteT(det);
+ }
+ }
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Generic/Factorization/QR.cs b/src/Numerics/LinearAlgebra/Generic/Factorization/QR.cs
index cf45704e..cc36babe 100644
--- a/src/Numerics/LinearAlgebra/Generic/Factorization/QR.cs
+++ b/src/Numerics/LinearAlgebra/Generic/Factorization/QR.cs
@@ -255,7 +255,7 @@ namespace MathNet.Numerics.LinearAlgebra.Generic.Factorization
/// Gets value of type T equal to one
///
/// One value
- private static T OneValueT
+ protected static T OneValueT
{
get
{
diff --git a/src/Numerics/LinearAlgebra/Single/Factorization/DenseEvd.cs b/src/Numerics/LinearAlgebra/Single/Factorization/DenseEvd.cs
new file mode 100644
index 00000000..712c4ff2
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Single/Factorization/DenseEvd.cs
@@ -0,0 +1,1238 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+// http://mathnetnumerics.codeplex.com
+//
+// Copyright (c) 2009-2010 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+namespace MathNet.Numerics.LinearAlgebra.Single.Factorization
+{
+ using System;
+ using System.Numerics;
+ using Generic;
+ using Generic.Factorization;
+ using Numerics;
+ using Properties;
+
+ ///
+ /// Eigenvalues and eigenvectors of a real matrix.
+ ///
+ ///
+ /// If A is symmetric, then A = V*D*V' where the eigenvalue matrix D is
+ /// diagonal and the eigenvector matrix V is orthogonal.
+ /// I.e. A = V*D*V' and V*VT=I.
+ /// If A is not symmetric, then the eigenvalue matrix D is block diagonal
+ /// with the real eigenvalues in 1-by-1 blocks and any complex eigenvalues,
+ /// lambda + i*mu, in 2-by-2 blocks, [lambda, mu; -mu, lambda]. The
+ /// columns of V represent the eigenvectors in the sense that A*V = V*D,
+ /// i.e. A.Multiply(V) equals V.Multiply(D). The matrix V may be badly
+ /// conditioned, or even singular, so the validity of the equation
+ /// A = V*D*Inverse(V) depends upon V.cond().
+ ///
+ public class DenseEvd : Evd
+ {
+ ///
+ /// Initializes a new instance of the class. This object will compute the
+ /// the eigenvalue decomposition when the constructor is called and cache it's decomposition.
+ ///
+ /// The matrix to factor.
+ /// If is null.
+ /// If EVD algorithm failed to converge with matrix .
+ public DenseEvd(DenseMatrix matrix)
+ {
+ if (matrix == null)
+ {
+ throw new ArgumentNullException("matrix");
+ }
+
+ if (matrix.RowCount != matrix.ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSquare);
+ }
+
+ var order = matrix.RowCount;
+
+ // Initialize matricies for eigenvalues and eigenvectors
+ MatrixEv = matrix.CreateMatrix(order, order);
+ MatrixD = matrix.CreateMatrix(order, order);
+ VectorEv = new LinearAlgebra.Complex.DenseVector(order);
+
+ IsSymmetric = true;
+
+ for (var i = 0; i < order & IsSymmetric; i++)
+ {
+ for (var j = 0; j < order & IsSymmetric; j++)
+ {
+ IsSymmetric &= matrix[i, j] == matrix[j, i];
+ }
+ }
+
+ var d = new float[order];
+ var e = new float[order];
+
+ if (IsSymmetric)
+ {
+ matrix.CopyTo(MatrixEv);
+ d = MatrixEv.Row(order - 1).ToArray();
+
+ SymmetricTridiagonalize(((DenseMatrix)MatrixEv).Data, d, e, order);
+ SymmetricDiagonalize(((DenseMatrix)MatrixEv).Data, d, e, order);
+ }
+ else
+ {
+ var matrixH = matrix.ToArray();
+
+ NonsymmetricReduceToHessenberg(((DenseMatrix)MatrixEv).Data, matrixH, order);
+ NonsymmetricReduceHessenberToRealSchur(((DenseMatrix)MatrixEv).Data, matrixH, d, e, order);
+ }
+
+ for (var i = 0; i < order; i++)
+ {
+ MatrixD.At(i, i, d[i]);
+
+ if (e[i] > 0)
+ {
+ MatrixD.At(i, i + 1, e[i]);
+ }
+ else if (e[i] < 0)
+ {
+ MatrixD.At(i, i - 1, e[i]);
+ }
+ }
+
+ for (var i = 0; i < order; i++)
+ {
+ VectorEv[i] = new Complex(d[i], e[i]);
+ }
+ }
+
+ ///
+ /// Symmetric Householder reduction to tridiagonal form.
+ ///
+ /// Data array of matrix V (eigenvectors)
+ /// Arrays for internal storage of real parts of eigenvalues
+ /// Arrays for internal storage of imaginary parts of eigenvalues
+ /// Order of initial matrix
+ /// This is derived from the Algol procedures tred2 by
+ /// Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
+ /// Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
+ /// Fortran subroutine in EISPACK.
+ private static void SymmetricTridiagonalize(float[] a, float[] d, float[] e, int order)
+ {
+ // Householder reduction to tridiagonal form.
+ for (var i = order - 1; i > 0; i--)
+ {
+ // Scale to avoid under/overflow.
+ var scale = 0.0f;
+ var h = 0.0f;
+
+ for (var k = 0; k < i; k++)
+ {
+ scale = scale + Math.Abs(d[k]);
+ }
+
+ if (scale == 0.0f)
+ {
+ e[i] = d[i - 1];
+ for (var j = 0; j < i; j++)
+ {
+ d[j] = a[(j * order) + i - 1];
+ a[(j * order) + i] = 0.0f;
+ a[(i * order) + j] = 0.0f;
+ }
+ }
+ else
+ {
+ // Generate Householder vector.
+ for (var k = 0; k < i; k++)
+ {
+ d[k] /= scale;
+ h += d[k] * d[k];
+ }
+
+ var f = d[i - 1];
+ var g = (float)Math.Sqrt(h);
+ if (f > 0)
+ {
+ g = -g;
+ }
+
+ e[i] = scale * g;
+ h = h - (f * g);
+ d[i - 1] = f - g;
+
+ for (var j = 0; j < i; j++)
+ {
+ e[j] = 0.0f;
+ }
+
+ // Apply similarity transformation to remaining columns.
+ for (var j = 0; j < i; j++)
+ {
+ f = d[j];
+ a[(i * order) + j] = f;
+ g = e[j] + (a[(j * order) + j] * f);
+
+ for (var k = j + 1; k <= i - 1; k++)
+ {
+ g += a[(j * order) + k] * d[k];
+ e[k] += a[(j * order) + k] * f;
+ }
+
+ e[j] = g;
+ }
+
+ f = 0.0f;
+
+ for (var j = 0; j < i; j++)
+ {
+ e[j] /= h;
+ f += e[j] * d[j];
+ }
+
+ var hh = f / (h + h);
+
+ for (var j = 0; j < i; j++)
+ {
+ e[j] -= hh * d[j];
+ }
+
+ for (var j = 0; j < i; j++)
+ {
+ f = d[j];
+ g = e[j];
+
+ for (var k = j; k <= i - 1; k++)
+ {
+ a[(j * order) + k] -= (f * e[k]) + (g * d[k]);
+ }
+
+ d[j] = a[(j * order) + i - 1];
+ a[(j * order) + i] = 0.0f;
+ }
+ }
+
+ d[i] = h;
+ }
+
+ // Accumulate transformations.
+ for (var i = 0; i < order - 1; i++)
+ {
+ a[(i * order) + order - 1] = a[(i * order) + i];
+ a[(i * order) + i] = 1.0f;
+ var h = d[i + 1];
+ if (h != 0.0f)
+ {
+ for (var k = 0; k <= i; k++)
+ {
+ d[k] = a[((i + 1) * order) + k] / h;
+ }
+
+ for (var j = 0; j <= i; j++)
+ {
+ var g = 0.0f;
+ for (var k = 0; k <= i; k++)
+ {
+ g += a[((i + 1) * order) + k] * a[(j * order) + k];
+ }
+
+ for (var k = 0; k <= i; k++)
+ {
+ a[(j * order) + k] -= g * d[k];
+ }
+ }
+ }
+
+ for (var k = 0; k <= i; k++)
+ {
+ a[((i + 1) * order) + k] = 0.0f;
+ }
+ }
+
+ for (var j = 0; j < order; j++)
+ {
+ d[j] = a[(j * order) + order - 1];
+ a[(j * order) + order - 1] = 0.0f;
+ }
+
+ a[(order * order) - 1] = 1.0f;
+ e[0] = 0.0f;
+ }
+
+ ///
+ /// Symmetric tridiagonal QL algorithm.
+ ///
+ /// Data array of matrix V (eigenvectors)
+ /// Arrays for internal storage of real parts of eigenvalues
+ /// Arrays for internal storage of imaginary parts of eigenvalues
+ /// Order of initial matrix
+ /// This is derived from the Algol procedures tql2, by
+ /// Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
+ /// Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
+ /// Fortran subroutine in EISPACK.
+ private static void SymmetricDiagonalize(float[] a, float[] d, float[] e, int order)
+ {
+ const int Maxiter = 1000;
+
+ for (var i = 1; i < order; i++)
+ {
+ e[i - 1] = e[i];
+ }
+
+ e[order - 1] = 0.0f;
+
+ var f = 0.0f;
+ var tst1 = 0.0f;
+ var eps = Precision.DoubleMachinePrecision;
+ for (var l = 0; l < order; l++)
+ {
+ // Find small subdiagonal element
+ tst1 = Math.Max(tst1, Math.Abs(d[l]) + Math.Abs(e[l]));
+ var m = l;
+ while (m < order)
+ {
+ if (Math.Abs(e[m]) <= eps * tst1)
+ {
+ break;
+ }
+
+ m++;
+ }
+
+ // If m == l, d[l] is an eigenvalue,
+ // otherwise, iterate.
+ if (m > l)
+ {
+ var iter = 0;
+ do
+ {
+ iter = iter + 1; // (Could check iteration count here.)
+
+ // Compute implicit shift
+ var g = d[l];
+ var p = (d[l + 1] - g) / (2.0f * e[l]);
+ var r = SpecialFunctions.Hypotenuse(p, 1.0f);
+ if (p < 0)
+ {
+ r = -r;
+ }
+
+ d[l] = e[l] / (p + r);
+ d[l + 1] = e[l] * (p + r);
+
+ var dl1 = d[l + 1];
+ var h = g - d[l];
+ for (var i = l + 2; i < order; i++)
+ {
+ d[i] -= h;
+ }
+
+ f = f + h;
+
+ // Implicit QL transformation.
+ p = d[m];
+ var c = 1.0f;
+ var c2 = c;
+ var c3 = c;
+ var el1 = e[l + 1];
+ var s = 0.0f;
+ var s2 = 0.0f;
+ for (var i = m - 1; i >= l; i--)
+ {
+ c3 = c2;
+ c2 = c;
+ s2 = s;
+ g = c * e[i];
+ h = c * p;
+ r = SpecialFunctions.Hypotenuse(p, e[i]);
+ e[i + 1] = s * r;
+ s = e[i] / r;
+ c = p / r;
+ p = (c * d[i]) - (s * g);
+ d[i + 1] = h + (s * ((c * g) + (s * d[i])));
+
+ // Accumulate transformation.
+ for (var k = 0; k < order; k++)
+ {
+ h = a[((i + 1) * order) + k];
+ a[((i + 1) * order) + k] = (s * a[(i * order) + k]) + (c * h);
+ a[(i * order) + k] = (c * a[(i * order) + k]) - (s * h);
+ }
+ }
+
+ p = (-s) * s2 * c3 * el1 * e[l] / dl1;
+ e[l] = s * p;
+ d[l] = c * p;
+
+ // Check for convergence. If too many iterations have been performed,
+ // throw exception that Convergence Failed
+ if (iter >= Maxiter)
+ {
+ throw new ArgumentException(Resources.ConvergenceFailed);
+ }
+ }
+ while (Math.Abs(e[l]) > eps * tst1);
+ }
+
+ d[l] = d[l] + f;
+ e[l] = 0.0f;
+ }
+
+ // Sort eigenvalues and corresponding vectors.
+ for (var i = 0; i < order - 1; i++)
+ {
+ var k = i;
+ var p = d[i];
+ for (var j = i + 1; j < order; j++)
+ {
+ if (d[j] < p)
+ {
+ k = j;
+ p = d[j];
+ }
+ }
+
+ if (k != i)
+ {
+ d[k] = d[i];
+ d[i] = p;
+ for (var j = 0; j < order; j++)
+ {
+ p = a[(i * order) + j];
+ a[(i * order) + j] = a[(k * order) + j];
+ a[(k * order) + j] = p;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Nonsymmetric reduction to Hessenberg form.
+ ///
+ /// Data array of matrix V (eigenvectors)
+ /// Array for internal storage of nonsymmetric Hessenberg form.
+ /// Order of initial matrix
+ /// This is derived from the Algol procedures orthes and ortran,
+ /// by Martin and Wilkinson, Handbook for Auto. Comp.,
+ /// Vol.ii-Linear Algebra, and the corresponding
+ /// Fortran subroutines in EISPACK.
+ private static void NonsymmetricReduceToHessenberg(float[] a, float[,] matrixH, int order)
+ {
+ var ort = new float[order];
+
+ for (var m = 1; m < order - 1; m++)
+ {
+ // Scale column.
+ var scale = 0.0f;
+ for (var i = m; i < order; i++)
+ {
+ scale = scale + Math.Abs(matrixH[i, m - 1]);
+ }
+
+ if (scale != 0.0f)
+ {
+ // Compute Householder transformation.
+ var h = 0.0f;
+ for (var i = order - 1; i >= m; i--)
+ {
+ ort[i] = matrixH[i, m - 1] / scale;
+ h += ort[i] * ort[i];
+ }
+
+ var g = (float)Math.Sqrt(h);
+ if (ort[m] > 0)
+ {
+ g = -g;
+ }
+
+ h = h - (ort[m] * g);
+ ort[m] = ort[m] - g;
+
+ // Apply Householder similarity transformation
+ // H = (I-u*u'/h)*H*(I-u*u')/h)
+ for (var j = m; j < order; j++)
+ {
+ var f = 0.0f;
+ for (var i = order - 1; i >= m; i--)
+ {
+ f += ort[i] * matrixH[i, j];
+ }
+
+ f = f / h;
+ for (var i = m; i < order; i++)
+ {
+ matrixH[i, j] -= f * ort[i];
+ }
+ }
+
+ for (var i = 0; i < order; i++)
+ {
+ var f = 0.0f;
+ for (var j = order - 1; j >= m; j--)
+ {
+ f += ort[j] * matrixH[i, j];
+ }
+
+ f = f / h;
+ for (var j = m; j < order; j++)
+ {
+ matrixH[i, j] -= f * ort[j];
+ }
+ }
+
+ ort[m] = scale * ort[m];
+ matrixH[m, m - 1] = scale * g;
+ }
+ }
+
+ // Accumulate transformations (Algol's ortran).
+ for (var i = 0; i < order; i++)
+ {
+ for (var j = 0; j < order; j++)
+ {
+ a[(j * order) + i] = i == j ? 1.0f : 0.0f;
+ }
+ }
+
+ for (var m = order - 2; m >= 1; m--)
+ {
+ if (matrixH[m, m - 1] != 0.0f)
+ {
+ for (var i = m + 1; i < order; i++)
+ {
+ ort[i] = matrixH[i, m - 1];
+ }
+
+ for (var j = m; j < order; j++)
+ {
+ var g = 0.0f;
+ for (var i = m; i < order; i++)
+ {
+ g += ort[i] * a[(j * order) + i];
+ }
+
+ // Double division avoids possible underflow
+ g = (g / ort[m]) / matrixH[m, m - 1];
+ for (var i = m; i < order; i++)
+ {
+ a[(j * order) + i] += g * ort[i];
+ }
+ }
+ }
+ }
+ }
+
+ ///
+ /// Nonsymmetric reduction from Hessenberg to real Schur form.
+ ///
+ /// Data array of matrix V (eigenvectors)
+ /// Array for internal storage of nonsymmetric Hessenberg form.
+ /// Arrays for internal storage of real parts of eigenvalues
+ /// Arrays for internal storage of imaginary parts of eigenvalues
+ /// Order of initial matrix
+ /// This is derived from the Algol procedure hqr2,
+ /// by Martin and Wilkinson, Handbook for Auto. Comp.,
+ /// Vol.ii-Linear Algebra, and the corresponding
+ /// Fortran subroutine in EISPACK.
+ private static void NonsymmetricReduceHessenberToRealSchur(float[] a, float[,] matrixH, float[] d, float[] e, int order)
+ {
+ // Initialize
+ var n = order - 1;
+ var eps = (float)Precision.SingleMachinePrecision;
+ var exshift = 0.0f;
+ float p = 0, q = 0, r = 0, s = 0, z = 0, w, x, y;
+
+ // Store roots isolated by balanc and compute matrix norm
+ var norm = 0.0f;
+ for (var i = 0; i < order; i++)
+ {
+ for (var j = Math.Max(i - 1, 0); j < order; j++)
+ {
+ norm = norm + Math.Abs(matrixH[i, j]);
+ }
+ }
+
+ // Outer loop over eigenvalue index
+ var iter = 0;
+ while (n >= 0)
+ {
+ // Look for single small sub-diagonal element
+ var l = n;
+ while (l > 0)
+ {
+ s = Math.Abs(matrixH[l - 1, l - 1]) + Math.Abs(matrixH[l, l]);
+
+ if (s == 0.0f)
+ {
+ s = norm;
+ }
+
+ if (Math.Abs(matrixH[l, l - 1]) < eps * s)
+ {
+ break;
+ }
+
+ l--;
+ }
+
+ // Check for convergence
+ // One root found
+ if (l == n)
+ {
+ matrixH[n, n] = matrixH[n, n] + exshift;
+ d[n] = matrixH[n, n];
+ e[n] = 0.0f;
+ n--;
+ iter = 0;
+
+ // Two roots found
+ }
+ else if (l == n - 1)
+ {
+ w = matrixH[n, n - 1] * matrixH[n - 1, n];
+ p = (matrixH[n - 1, n - 1] - matrixH[n, n]) / 2.0f;
+ q = (p * p) + w;
+ z = (float)Math.Sqrt(Math.Abs(q));
+ matrixH[n, n] = matrixH[n, n] + exshift;
+ matrixH[n - 1, n - 1] = matrixH[n - 1, n - 1] + exshift;
+ x = matrixH[n, n];
+
+ // Real pair
+ if (q >= 0)
+ {
+ if (p >= 0)
+ {
+ z = p + z;
+ }
+ else
+ {
+ z = p - z;
+ }
+
+ d[n - 1] = x + z;
+
+ d[n] = d[n - 1];
+ if (z != 0.0f)
+ {
+ d[n] = x - (w / z);
+ }
+
+ e[n - 1] = 0.0f;
+ e[n] = 0.0f;
+ x = matrixH[n, n - 1];
+ s = Math.Abs(x) + Math.Abs(z);
+ p = x / s;
+ q = z / s;
+ r = (float)Math.Sqrt((p * p) + (q * q));
+ p = p / r;
+ q = q / r;
+
+ // Row modification
+ for (var j = n - 1; j < order; j++)
+ {
+ z = matrixH[n - 1, j];
+ matrixH[n - 1, j] = (q * z) + (p * matrixH[n, j]);
+ matrixH[n, j] = (q * matrixH[n, j]) - (p * z);
+ }
+
+ // Column modification
+ for (var i = 0; i <= n; i++)
+ {
+ z = matrixH[i, n - 1];
+ matrixH[i, n - 1] = (q * z) + (p * matrixH[i, n]);
+ matrixH[i, n] = (q * matrixH[i, n]) - (p * z);
+ }
+
+ // Accumulate transformations
+ for (var i = 0; i < order; i++)
+ {
+ z = a[((n - 1) * order) + i];
+ a[((n - 1) * order) + i] = (q * z) + (p * a[(n * order) + i]);
+ a[(n * order) + i] = (q * a[(n * order) + i]) - (p * z);
+ }
+
+ // Complex pair
+ }
+ else
+ {
+ d[n - 1] = x + p;
+ d[n] = x + p;
+ e[n - 1] = z;
+ e[n] = -z;
+ }
+
+ n = n - 2;
+ iter = 0;
+
+ // No convergence yet
+ }
+ else
+ {
+ // Form shift
+ x = matrixH[n, n];
+ y = 0.0f;
+ w = 0.0f;
+ if (l < n)
+ {
+ y = matrixH[n - 1, n - 1];
+ w = matrixH[n, n - 1] * matrixH[n - 1, n];
+ }
+
+ // Wilkinson's original ad hoc shift
+ if (iter == 10)
+ {
+ exshift += x;
+ for (var i = 0; i <= n; i++)
+ {
+ matrixH[i, i] -= x;
+ }
+
+ s = Math.Abs(matrixH[n, n - 1]) + Math.Abs(matrixH[n - 1, n - 2]);
+ x = y = 0.75f * s;
+ w = (-0.4375f) * s * s;
+ }
+
+ // MATLAB's new ad hoc shift
+ if (iter == 30)
+ {
+ s = (y - x) / 2.0f;
+ s = (s * s) + w;
+ if (s > 0)
+ {
+ s = (float)Math.Sqrt(s);
+ if (y < x)
+ {
+ s = -s;
+ }
+
+ s = x - (w / (((y - x) / 2.0f) + s));
+ for (var i = 0; i <= n; i++)
+ {
+ matrixH[i, i] -= s;
+ }
+
+ exshift += s;
+ x = y = w = 0.964f;
+ }
+ }
+
+ iter = iter + 1; // (Could check iteration count here.)
+
+ // Look for two consecutive small sub-diagonal elements
+ var m = n - 2;
+ while (m >= l)
+ {
+ z = matrixH[m, m];
+ r = x - z;
+ s = y - z;
+ p = (((r * s) - w) / matrixH[m + 1, m]) + matrixH[m, m + 1];
+ q = matrixH[m + 1, m + 1] - z - r - s;
+ r = matrixH[m + 2, m + 1];
+ s = Math.Abs(p) + Math.Abs(q) + Math.Abs(r);
+ p = p / s;
+ q = q / s;
+ r = r / s;
+
+ if (m == l)
+ {
+ break;
+ }
+
+ if (Math.Abs(matrixH[m, m - 1]) * (Math.Abs(q) + Math.Abs(r)) < eps * (Math.Abs(p) * (Math.Abs(matrixH[m - 1, m - 1]) + Math.Abs(z) + Math.Abs(matrixH[m + 1, m + 1]))))
+ {
+ break;
+ }
+
+ m--;
+ }
+
+ for (var i = m + 2; i <= n; i++)
+ {
+ matrixH[i, i - 2] = 0.0f;
+ if (i > m + 2)
+ {
+ matrixH[i, i - 3] = 0.0f;
+ }
+ }
+
+ // Double QR step involving rows l:n and columns m:n
+ for (var k = m; k <= n - 1; k++)
+ {
+ bool notlast = k != n - 1;
+
+ if (k != m)
+ {
+ p = matrixH[k, k - 1];
+ q = matrixH[k + 1, k - 1];
+ r = notlast ? matrixH[k + 2, k - 1] : 0.0f;
+ x = Math.Abs(p) + Math.Abs(q) + Math.Abs(r);
+ if (x != 0.0f)
+ {
+ p = p / x;
+ q = q / x;
+ r = r / x;
+ }
+ }
+
+ if (x == 0.0f)
+ {
+ break;
+ }
+
+ s = (float)Math.Sqrt((p * p) + (q * q) + (r * r));
+ if (p < 0)
+ {
+ s = -s;
+ }
+
+ if (s != 0.0f)
+ {
+ if (k != m)
+ {
+ matrixH[k, k - 1] = (-s) * x;
+ }
+ else if (l != m)
+ {
+ matrixH[k, k - 1] = -matrixH[k, k - 1];
+ }
+
+ p = p + s;
+ x = p / s;
+ y = q / s;
+ z = r / s;
+ q = q / p;
+ r = r / p;
+
+ // Row modification
+ for (var j = k; j < order; j++)
+ {
+ p = matrixH[k, j] + (q * matrixH[k + 1, j]);
+
+ if (notlast)
+ {
+ p = p + (r * matrixH[k + 2, j]);
+ matrixH[k + 2, j] = matrixH[k + 2, j] - (p * z);
+ }
+
+ matrixH[k, j] = matrixH[k, j] - (p * x);
+ matrixH[k + 1, j] = matrixH[k + 1, j] - (p * y);
+ }
+
+ // Column modification
+ for (var i = 0; i <= Math.Min(n, k + 3); i++)
+ {
+ p = (x * matrixH[i, k]) + (y * matrixH[i, k + 1]);
+
+ if (notlast)
+ {
+ p = p + (z * matrixH[i, k + 2]);
+ matrixH[i, k + 2] = matrixH[i, k + 2] - (p * r);
+ }
+
+ matrixH[i, k] = matrixH[i, k] - p;
+ matrixH[i, k + 1] = matrixH[i, k + 1] - (p * q);
+ }
+
+ // Accumulate transformations
+ for (var i = 0; i < order; i++)
+ {
+ p = (x * a[(k * order) + i]) + (y * a[((k + 1) * order) + i]);
+
+ if (notlast)
+ {
+ p = p + (z * a[((k + 2) * order) + i]);
+ a[((k + 2) * order) + i] -= p * r;
+ }
+
+ a[(k * order) + i] -= p;
+ a[((k + 1) * order) + i] -= p * q;
+ }
+ } // (s != 0)
+ } // k loop
+ } // check convergence
+ } // while (n >= low)
+
+ // Backsubstitute to find vectors of upper triangular form
+ if (norm == 0.0f)
+ {
+ return;
+ }
+
+ for (n = order - 1; n >= 0; n--)
+ {
+ float t;
+
+ p = d[n];
+ q = e[n];
+
+ // Real vector
+ if (q == 0.0f)
+ {
+ var l = n;
+ matrixH[n, n] = 1.0f;
+ for (var i = n - 1; i >= 0; i--)
+ {
+ w = matrixH[i, i] - p;
+ r = 0.0f;
+ for (var j = l; j <= n; j++)
+ {
+ r = r + (matrixH[i, j] * matrixH[j, n]);
+ }
+
+ if (e[i] < 0.0f)
+ {
+ z = w;
+ s = r;
+ }
+ else
+ {
+ l = i;
+ if (e[i] == 0.0f)
+ {
+ if (w != 0.0f)
+ {
+ matrixH[i, n] = (-r) / w;
+ }
+ else
+ {
+ matrixH[i, n] = (-r) / (eps * norm);
+ }
+
+ // Solve real equations
+ }
+ else
+ {
+ x = matrixH[i, i + 1];
+ y = matrixH[i + 1, i];
+ q = ((d[i] - p) * (d[i] - p)) + (e[i] * e[i]);
+ t = ((x * s) - (z * r)) / q;
+ matrixH[i, n] = t;
+ if (Math.Abs(x) > Math.Abs(z))
+ {
+ matrixH[i + 1, n] = (-r - (w * t)) / x;
+ }
+ else
+ {
+ matrixH[i + 1, n] = (-s - (y * t)) / z;
+ }
+ }
+
+ // Overflow control
+ t = Math.Abs(matrixH[i, n]);
+ if ((eps * t) * t > 1)
+ {
+ for (var j = i; j <= n; j++)
+ {
+ matrixH[j, n] = matrixH[j, n] / t;
+ }
+ }
+ }
+ }
+
+ // Complex vector
+ }
+ else if (q < 0)
+ {
+ var l = n - 1;
+
+ // Last vector component imaginary so matrix is triangular
+ if (Math.Abs(matrixH[n, n - 1]) > Math.Abs(matrixH[n - 1, n]))
+ {
+ matrixH[n - 1, n - 1] = q / matrixH[n, n - 1];
+ matrixH[n - 1, n] = (-(matrixH[n, n] - p)) / matrixH[n, n - 1];
+ }
+ else
+ {
+ var res = Cdiv(0.0f, -matrixH[n - 1, n], matrixH[n - 1, n - 1] - p, q);
+ matrixH[n - 1, n - 1] = res.Real;
+ matrixH[n - 1, n] = res.Imaginary;
+ }
+
+ matrixH[n, n - 1] = 0.0f;
+ matrixH[n, n] = 1.0f;
+ for (var i = n - 2; i >= 0; i--)
+ {
+ float ra = 0.0f;
+ float sa = 0.0f;
+ for (var j = l; j <= n; j++)
+ {
+ ra = ra + (matrixH[i, j] * matrixH[j, n - 1]);
+ sa = sa + (matrixH[i, j] * matrixH[j, n]);
+ }
+
+ w = matrixH[i, i] - p;
+
+ if (e[i] < 0.0f)
+ {
+ z = w;
+ r = ra;
+ s = sa;
+ }
+ else
+ {
+ l = i;
+ if (e[i] == 0.0f)
+ {
+ var res = Cdiv(-ra, -sa, w, q);
+ matrixH[i, n - 1] = res.Real;
+ matrixH[i, n] = res.Imaginary;
+ }
+ else
+ {
+ // Solve complex equations
+ x = matrixH[i, i + 1];
+ y = matrixH[i + 1, i];
+
+ float vr = ((d[i] - p) * (d[i] - p)) + (e[i] * e[i]) - (q * q);
+ float vi = (d[i] - p) * 2.0f * q;
+ if ((vr == 0.0f) && (vi == 0.0f))
+ {
+ vr = eps * norm * (Math.Abs(w) + Math.Abs(q) + Math.Abs(x) + Math.Abs(y) + Math.Abs(z));
+ }
+
+ var res = Cdiv((x * r) - (z * ra) + (q * sa), (x * s) - (z * sa) - (q * ra), vr, vi);
+ matrixH[i, n - 1] = res.Real;
+ matrixH[i, n] = res.Imaginary;
+ if (Math.Abs(x) > (Math.Abs(z) + Math.Abs(q)))
+ {
+ matrixH[i + 1, n - 1] = (-ra - (w * matrixH[i, n - 1]) + (q * matrixH[i, n])) / x;
+ matrixH[i + 1, n] = (-sa - (w * matrixH[i, n]) - (q * matrixH[i, n - 1])) / x;
+ }
+ else
+ {
+ res = Cdiv(-r - (y * matrixH[i, n - 1]), -s - (y * matrixH[i, n]), z, q);
+ matrixH[i + 1, n - 1] = res.Real;
+ matrixH[i + 1, n] = res.Imaginary;
+ }
+ }
+
+ // Overflow control
+ t = Math.Max(Math.Abs(matrixH[i, n - 1]), Math.Abs(matrixH[i, n]));
+ if ((eps * t) * t > 1)
+ {
+ for (var j = i; j <= n; j++)
+ {
+ matrixH[j, n - 1] = matrixH[j, n - 1] / t;
+ matrixH[j, n] = matrixH[j, n] / t;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Back transformation to get eigenvectors of original matrix
+ for (var j = order - 1; j >= 0; j--)
+ {
+ for (var i = 0; i < order; i++)
+ {
+ z = 0.0f;
+ for (var k = 0; k <= j; k++)
+ {
+ z = z + (a[(k * order) + i] * matrixH[k, j]);
+ }
+
+ a[(j * order) + i] = z;
+ }
+ }
+ }
+
+ ///
+ /// Complex scalar division X/Y.
+ ///
+ /// Real part of X
+ /// Imaginary part of X
+ /// Real part of Y
+ /// Imaginary part of Y
+ /// Division result as a number.
+ private static Complex32 Cdiv(float xreal, float ximag, float yreal, float yimag)
+ {
+ if (Math.Abs(yimag) < Math.Abs(yreal))
+ {
+ return new Complex32((xreal + (ximag * (yimag / yreal))) / (yreal + (yimag * (yimag / yreal))), (ximag - (xreal * (yimag / yreal))) / (yreal + (yimag * (yimag / yreal))));
+ }
+
+ return new Complex32((ximag + (xreal * (yreal / yimag))) / (yimag + (yreal * (yreal / yimag))), (-xreal + (ximag * (yreal / yimag))) / (yimag + (yreal * (yreal / yimag))));
+ }
+
+ ///
+ /// Solves a system of linear equations, AX = B, with A SVD factorized.
+ ///
+ /// The right hand side , B.
+ /// The left hand side , X.
+ public override void Solve(Matrix input, Matrix result)
+ {
+ // Check for proper arguments.
+ if (input == null)
+ {
+ throw new ArgumentNullException("input");
+ }
+
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ // The solution X should have the same number of columns as B
+ if (input.ColumnCount != result.ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension);
+ }
+
+ // The dimension compatibility conditions for X = A\B require the two matrices A and B to have the same number of rows
+ if (VectorEv.Count != input.RowCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension);
+ }
+
+ // The solution X row dimension is equal to the column dimension of A
+ if (VectorEv.Count != result.RowCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension);
+ }
+
+ if (IsSymmetric)
+ {
+ var order = VectorEv.Count;
+ var tmp = new float[order];
+
+ for (var k = 0; k < order; k++)
+ {
+ for (var j = 0; j < order; j++)
+ {
+ float value = 0;
+ if (j < order)
+ {
+ for (var i = 0; i < order; i++)
+ {
+ value += ((DenseMatrix)MatrixEv).Data[(j * order) + i] * input.At(i, k);
+ }
+
+ value /= (float)VectorEv[j].Real;
+ }
+
+ tmp[j] = value;
+ }
+
+ for (var j = 0; j < order; j++)
+ {
+ float value = 0;
+ for (var i = 0; i < order; i++)
+ {
+ value += ((DenseMatrix)MatrixEv).Data[(i * order) + j] * tmp[i];
+ }
+
+ result[j, k] = value;
+ }
+ }
+ }
+ else
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSymmetric);
+ }
+ }
+
+ ///
+ /// Solves a system of linear equations, Ax = b, with A EVD factorized.
+ ///
+ /// The right hand side vector, b.
+ /// The left hand side , x.
+ public override void Solve(Vector input, Vector result)
+ {
+ if (input == null)
+ {
+ throw new ArgumentNullException("input");
+ }
+
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ // Ax=b where A is an m x m matrix
+ // Check that b is a column vector with m entries
+ if (VectorEv.Count != input.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength);
+ }
+
+ // Check that x is a column vector with n entries
+ if (VectorEv.Count != result.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions);
+ }
+
+ if (IsSymmetric)
+ {
+ // Symmetric case -> x = V * inv(λ) * VT * b;
+ var order = VectorEv.Count;
+ var tmp = new float[order];
+ float value;
+
+ for (var j = 0; j < order; j++)
+ {
+ value = 0;
+ if (j < order)
+ {
+ for (var i = 0; i < order; i++)
+ {
+ value += ((DenseMatrix)MatrixEv).Data[(j * order) + i] * input[i];
+ }
+
+ value /= (float)VectorEv[j].Real;
+ }
+
+ tmp[j] = value;
+ }
+
+ for (var j = 0; j < order; j++)
+ {
+ value = 0;
+ for (int i = 0; i < order; i++)
+ {
+ value += ((DenseMatrix)MatrixEv).Data[(i * order) + j] * tmp[i];
+ }
+
+ result[j] = value;
+ }
+ }
+ else
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSymmetric);
+ }
+ }
+
+ ///
+ /// Multiply two values T*T
+ ///
+ /// Left operand value
+ /// Right operand value
+ /// Result of multiplication
+ protected sealed override float MultiplyT(float val1, float val2)
+ {
+ return val1 * val2;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Numerics/LinearAlgebra/Single/Factorization/DenseGramSchmidt.cs b/src/Numerics/LinearAlgebra/Single/Factorization/DenseGramSchmidt.cs
new file mode 100644
index 00000000..fbe127ac
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Single/Factorization/DenseGramSchmidt.cs
@@ -0,0 +1,238 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+// http://mathnetnumerics.codeplex.com
+//
+// Copyright (c) 2009-2010 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Single.Factorization
+{
+ using System;
+ using Generic;
+ using Generic.Factorization;
+ using Properties;
+ using Threading;
+
+ ///
+ /// A class which encapsulates the functionality of the QR decomposition Modified Gram-Schmidt Orthogonalization.
+ /// Any real square matrix A may be decomposed as A = QR where Q is an orthogonal mxn matrix and R is an nxn upper triangular matrix.
+ ///
+ ///
+ /// The computation of the QR decomposition is done at construction time by modified Gram-Schmidt Orthogonalization.
+ ///
+ public class DenseGramSchmidt : GramSchmidt
+ {
+ ///
+ /// Initializes a new instance of the class. This object creates an orthogonal matrix
+ /// using the modified Gram-Schmidt method.
+ ///
+ /// The matrix to factor.
+ /// If is null.
+ /// If row count is less then column count
+ /// If is rank deficient
+ public DenseGramSchmidt(DenseMatrix matrix)
+ {
+ if (matrix == null)
+ {
+ throw new ArgumentNullException("matrix");
+ }
+
+ if (matrix.RowCount < matrix.ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions);
+ }
+
+ MatrixQ = matrix.Clone();
+ MatrixR = matrix.CreateMatrix(matrix.ColumnCount, matrix.ColumnCount);
+ Factorize(((DenseMatrix)MatrixQ).Data, MatrixQ.RowCount, MatrixQ.ColumnCount, ((DenseMatrix)MatrixR).Data);
+ }
+
+ ///
+ /// Factorize matrix using the modified Gram-Schmidt method.
+ ///
+ /// Initial matrix. On exit is replaced by Q.
+ /// Number of rows in Q.
+ /// Number of columns in Q.
+ /// On exit is filled by R.
+ private static void Factorize(float[] q, int rowsQ, int columnsQ, float[] r)
+ {
+ for (var k = 0; k < columnsQ; k++)
+ {
+ var norm = 0.0f;
+ for (var i = 0; i < rowsQ; i++)
+ {
+ norm += q[(k * rowsQ) + i] * q[(k * rowsQ) + i];
+ }
+
+ norm = (float)Math.Sqrt(norm);
+ if (norm == 0.0)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixNotRankDeficient);
+ }
+
+ r[(k * columnsQ) + k] = norm;
+ for (var i = 0; i < rowsQ; i++)
+ {
+ q[(k * rowsQ) + i] /= norm;
+ }
+
+ for (var j = k + 1; j < columnsQ; j++)
+ {
+ int k1 = k;
+ int j1 = j;
+ var dot = CommonParallel.Aggregate(0, rowsQ, index => q[(k1 * rowsQ) + index] * q[(j1 * rowsQ) + index]);
+ r[(j * columnsQ) + k] = dot;
+ for (var i = 0; i < rowsQ; i++)
+ {
+ var value = q[(j * rowsQ) + i] - (q[(k * rowsQ) + i] * dot);
+ q[(j * rowsQ) + i] = value;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Solves a system of linear equations, AX = B, with A QR factorized.
+ ///
+ /// The right hand side , B.
+ /// The left hand side , X.
+ public override void Solve(Matrix input, Matrix result)
+ {
+ // Check for proper arguments.
+ if (input == null)
+ {
+ throw new ArgumentNullException("input");
+ }
+
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ // The solution X should have the same number of columns as B
+ if (input.ColumnCount != result.ColumnCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension);
+ }
+
+ // The dimension compatibility conditions for X = A\B require the two matrices A and B to have the same number of rows
+ if (MatrixQ.RowCount != input.RowCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension);
+ }
+
+ // The solution X row dimension is equal to the column dimension of A
+ if (MatrixQ.ColumnCount != result.RowCount)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension);
+ }
+
+ var dinput = input as DenseMatrix;
+ if (dinput == null)
+ {
+ throw new NotImplementedException("Can only do GramSchmidt factorization for dense matrices at the moment.");
+ }
+
+ var dresult = result as DenseMatrix;
+ if (dresult == null)
+ {
+ throw new NotImplementedException("Can only do GramSchmidt factorization for dense matrices at the moment.");
+ }
+
+ Control.LinearAlgebraProvider.QRSolveFactored(((DenseMatrix)MatrixQ).Data, ((DenseMatrix)MatrixR).Data, MatrixQ.RowCount, MatrixQ.ColumnCount, dinput.Data, input.ColumnCount, dresult.Data);
+ }
+
+ ///
+ /// Solves a system of linear equations, Ax = b, with A QR factorized.
+ ///
+ /// The right hand side vector, b.
+ /// The left hand side , x.
+ public override void Solve(Vector input, Vector result)
+ {
+ if (input == null)
+ {
+ throw new ArgumentNullException("input");
+ }
+
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ // Ax=b where A is an m x n matrix
+ // Check that b is a column vector with m entries
+ if (MatrixQ.RowCount != input.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength);
+ }
+
+ // Check that x is a column vector with n entries
+ if (MatrixQ.ColumnCount != result.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentMatrixDimensions);
+ }
+
+ var dinput = input as DenseVector;
+ if (dinput == null)
+ {
+ throw new NotImplementedException("Can only do GramSchmidt factorization for dense vectors at the moment.");
+ }
+
+ var dresult = result as DenseVector;
+ if (dresult == null)
+ {
+ throw new NotImplementedException("Can only do GramSchmidt factorization for dense vectors at the moment.");
+ }
+
+ Control.LinearAlgebraProvider.QRSolveFactored(((DenseMatrix)MatrixQ).Data, ((DenseMatrix)MatrixR).Data, MatrixQ.RowCount, MatrixQ.ColumnCount, dinput.Data, 1, dresult.Data);
+ }
+
+ #region Simple arithmetic of type T
+
+ ///
+ /// Multiply two values T*T
+ ///
+ /// Left operand value
+ /// Right operand value
+ /// Result of multiplication
+ protected sealed override float MultiplyT(float val1, float val2)
+ {
+ return val1 * val2;
+ }
+
+ ///
+ /// Returns the absolute value of a specified number.
+ ///
+ /// A number whose absolute is to be found
+ /// Absolute value
+ protected sealed override double AbsoluteT(float val1)
+ {
+ return Math.Abs(val1);
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Numerics/LinearAlgebra/Single/Factorization/UserEvd.cs b/src/Numerics/LinearAlgebra/Single/Factorization/UserEvd.cs
index 23407184..8f9aef8a 100644
--- a/src/Numerics/LinearAlgebra/Single/Factorization/UserEvd.cs
+++ b/src/Numerics/LinearAlgebra/Single/Factorization/UserEvd.cs
@@ -110,15 +110,15 @@ namespace MathNet.Numerics.LinearAlgebra.Single.Factorization
for (var i = 0; i < order; i++)
{
- MatrixD[i, i] = d[i];
+ MatrixD.At(i, i, d[i]);
if (e[i] > 0)
{
- MatrixD[i, i + 1] = e[i];
+ MatrixD.At(i, i + 1, e[i]);
}
else if (e[i] < 0)
{
- MatrixD[i, i - 1] = e[i];
+ MatrixD.At(i, i - 1, e[i]);
}
}
@@ -157,9 +157,9 @@ namespace MathNet.Numerics.LinearAlgebra.Single.Factorization
e[i] = d[i - 1];
for (var j = 0; j < i; j++)
{
- d[j] = MatrixEv[i - 1, j];
- MatrixEv[i, j] = 0.0f;
- MatrixEv[j, i] = 0.0f;
+ d[j] = MatrixEv.At(i - 1, j);
+ MatrixEv.At(i, j, 0.0f);
+ MatrixEv.At(j, i, 0.0f);
}
}
else
@@ -191,13 +191,13 @@ namespace MathNet.Numerics.LinearAlgebra.Single.Factorization
for (var j = 0; j < i; j++)
{
f = d[j];
- MatrixEv[j, i] = f;
- g = e[j] + (MatrixEv[j, j] * f);
+ MatrixEv.At(j, i, f);
+ g = e[j] + (MatrixEv.At(j, j) * f);
for (var k = j + 1; k <= i - 1; k++)
{
- g += MatrixEv[k, j] * d[k];
- e[k] += MatrixEv[k, j] * f;
+ g += MatrixEv.At(k, j) * d[k];
+ e[k] += MatrixEv.At(k, j) * f;
}
e[j] = g;
@@ -225,11 +225,11 @@ namespace MathNet.Numerics.LinearAlgebra.Single.Factorization
for (var k = j; k <= i - 1; k++)
{
- MatrixEv[k, j] -= (f * e[k]) + (g * d[k]);
+ MatrixEv.At(k, j, MatrixEv.At(k, j) - (f * e[k]) - (g * d[k]));
}
- d[j] = MatrixEv[i - 1, j];
- MatrixEv[i, j] = 0.0f;
+ d[j] = MatrixEv.At(i - 1, j);
+ MatrixEv.At(i, j, 0.0f);
}
}
@@ -239,14 +239,14 @@ namespace MathNet.Numerics.LinearAlgebra.Single.Factorization
// Accumulate transformations.
for (var i = 0; i < order - 1; i++)
{
- MatrixEv[order - 1, i] = MatrixEv[i, i];
- MatrixEv[i, i] = 1.0f;
+ MatrixEv.At(order - 1, i, MatrixEv.At(i, i));
+ MatrixEv.At(i, i, 1.0f);
var h = d[i + 1];
if (h != 0.0f)
{
for (var k = 0; k <= i; k++)
{
- d[k] = MatrixEv[k, i + 1] / h;
+ d[k] = MatrixEv.At(k, i + 1) / h;
}
for (var j = 0; j <= i; j++)
@@ -254,29 +254,29 @@ namespace MathNet.Numerics.LinearAlgebra.Single.Factorization
var g = 0.0f;
for (var k = 0; k <= i; k++)
{
- g += MatrixEv[k, i + 1] * MatrixEv[k, j];
+ g += MatrixEv.At(k, i + 1) * MatrixEv.At(k, j);
}
for (var k = 0; k <= i; k++)
{
- MatrixEv[k, j] -= g * d[k];
+ MatrixEv.At(k, j, MatrixEv.At(k, j) - g * d[k]);
}
}
}
for (var k = 0; k <= i; k++)
{
- MatrixEv[k, i + 1] = 0.0f;
+ MatrixEv.At(k, i + 1, 0.0f);
}
}
for (var j = 0; j < order; j++)
{
- d[j] = MatrixEv[order - 1, j];
- MatrixEv[order - 1, j] = 0.0f;
+ d[j] = MatrixEv.At(order - 1, j);
+ MatrixEv.At(order - 1, j, 0.0f);
}
- MatrixEv[order - 1, order - 1] = 1.0f;
+ MatrixEv.At(order - 1, order - 1, 1.0f);
e[0] = 0.0f;
}
@@ -374,9 +374,9 @@ namespace MathNet.Numerics.LinearAlgebra.Single.Factorization
// Accumulate transformation.
for (var k = 0; k < order; k++)
{
- h = MatrixEv[k, i + 1];
- MatrixEv[k, i + 1] = (s * MatrixEv[k, i]) + (c * h);
- MatrixEv[k, i] = (c * MatrixEv[k, i]) - (s * h);
+ h = MatrixEv.At(k, i + 1);
+ MatrixEv.At(k, i + 1, (s * MatrixEv.At(k, i)) + (c * h));
+ MatrixEv.At(k, i, (c * MatrixEv.At(k, i)) - (s * h));
}
}
@@ -418,9 +418,9 @@ namespace MathNet.Numerics.LinearAlgebra.Single.Factorization
d[i] = p;
for (var j = 0; j < order; j++)
{
- p = MatrixEv[j, i];
- MatrixEv[j, i] = MatrixEv[j, k];
- MatrixEv[j, k] = p;
+ p = MatrixEv.At(j, i);
+ MatrixEv.At(j, i, MatrixEv.At(j, k));
+ MatrixEv.At(j, k, p);
}
}
}
@@ -509,7 +509,7 @@ namespace MathNet.Numerics.LinearAlgebra.Single.Factorization
{
for (var j = 0; j < order; j++)
{
- MatrixEv[i, j] = i == j ? 1.0f : 0.0f;
+ MatrixEv.At(i, j, i == j ? 1.0f : 0.0f);
}
}
@@ -527,14 +527,14 @@ namespace MathNet.Numerics.LinearAlgebra.Single.Factorization
var g = 0.0f;
for (var i = m; i < order; i++)
{
- g += ort[i] * MatrixEv[i, j];
+ g += ort[i] * MatrixEv.At(i, j);
}
// Double division avoids possible underflow
g = (g / ort[m]) / matrixH[m, m - 1];
for (var i = m; i < order; i++)
{
- MatrixEv[i, j] += g * ort[i];
+ MatrixEv.At(i, j, MatrixEv.At(i, j) + g * ort[i]);
}
}
}
@@ -664,9 +664,9 @@ namespace MathNet.Numerics.LinearAlgebra.Single.Factorization
// Accumulate transformations
for (var i = 0; i < order; i++)
{
- z = MatrixEv[i, n - 1];
- MatrixEv[i, n - 1] = (q * z) + (p * MatrixEv[i, n]);
- MatrixEv[i, n] = (q * MatrixEv[i, n]) - (p * z);
+ z = MatrixEv.At(i, n - 1);
+ MatrixEv.At(i, n - 1, (q * z) + (p * MatrixEv.At(i, n)));
+ MatrixEv.At(i, n, (q * MatrixEv.At(i, n)) - (p * z));
}
// Complex pair
@@ -854,16 +854,16 @@ namespace MathNet.Numerics.LinearAlgebra.Single.Factorization
// Accumulate transformations
for (var i = 0; i < order; i++)
{
- p = (x * MatrixEv[i, k]) + (y * MatrixEv[i, k + 1]);
+ p = (x * MatrixEv.At(i, k)) + (y * MatrixEv.At(i, k + 1));
if (notlast)
{
- p = p + (z * MatrixEv[i, k + 2]);
- MatrixEv[i, k + 2] = MatrixEv[i, k + 2] - (p * r);
+ p = p + (z * MatrixEv.At(i, k + 2));
+ MatrixEv.At(i, k + 2, MatrixEv.At(i, k + 2) - (p * r));
}
- MatrixEv[i, k] = MatrixEv[i, k] - p;
- MatrixEv[i, k + 1] = MatrixEv[i, k + 1] - (p * q);
+ MatrixEv.At(i, k, MatrixEv.At(i, k) - p);
+ MatrixEv.At(i, k + 1, MatrixEv.At(i, k + 1) - (p * q));
}
} // (s != 0)
} // k loop
@@ -1047,10 +1047,10 @@ namespace MathNet.Numerics.LinearAlgebra.Single.Factorization
z = 0.0f;
for (var k = 0; k <= j; k++)
{
- z = z + (MatrixEv[i, k] * matrixH[k, j]);
+ z = z + (MatrixEv.At(i, k) * matrixH[k, j]);
}
- MatrixEv[i, j] = z;
+ MatrixEv.At(i, j, z);
}
}
}
diff --git a/src/Numerics/LinearAlgebra/Single/Factorization/GramSchmidt.cs b/src/Numerics/LinearAlgebra/Single/Factorization/UserGramSchmidt.cs
similarity index 87%
rename from src/Numerics/LinearAlgebra/Single/Factorization/GramSchmidt.cs
rename to src/Numerics/LinearAlgebra/Single/Factorization/UserGramSchmidt.cs
index a84c7362..44a20708 100644
--- a/src/Numerics/LinearAlgebra/Single/Factorization/GramSchmidt.cs
+++ b/src/Numerics/LinearAlgebra/Single/Factorization/UserGramSchmidt.cs
@@ -1,4 +1,4 @@
-//
+//
// Math.NET Numerics, part of the Math.NET Project
// http://numerics.mathdotnet.com
// http://github.com/mathnet/mathnet-numerics
@@ -42,17 +42,17 @@ namespace MathNet.Numerics.LinearAlgebra.Single.Factorization
///
/// The computation of the QR decomposition is done at construction time by modified Gram-Schmidt Orthogonalization.
///
- public class GramSchmidt : QR
+ public class UserGramSchmidt : GramSchmidt
{
///
- /// Initializes a new instance of the class. This object creates an orthogonal matrix
+ /// Initializes a new instance of the class. This object creates an orthogonal matrix
/// using the modified Gram-Schmidt method.
///
/// The matrix to factor.
/// If is null.
/// If row count is less then column count
/// If is rank deficient
- public GramSchmidt(Matrix matrix)
+ public UserGramSchmidt(Matrix matrix)
{
if (matrix == null)
{
@@ -94,44 +94,6 @@ namespace MathNet.Numerics.LinearAlgebra.Single.Factorization
}
}
- ///
- /// Gets a value indicating whether the matrix is full rank or not.
- ///
- /// true if the matrix is full rank; otherwise false.
- public override bool IsFullRank
- {
- get
- {
- return true;
- }
- }
-
- ///
- /// Gets the determinant of the matrix for which the QR matrix was computed.
- ///
- public override double Determinant
- {
- get
- {
- if (MatrixQ.RowCount != MatrixQ.ColumnCount)
- {
- throw new ArgumentException(Resources.ArgumentMatrixSquare);
- }
-
- var det = 1.0;
- for (var i = 0; i < MatrixR.ColumnCount; i++)
- {
- det *= MatrixR.At(i, i);
- if (Math.Abs(MatrixR.At(i, i)).AlmostEqualInDecimalPlaces(0.0f, 7))
- {
- return 0;
- }
- }
-
- return Math.Abs(det);
- }
- }
-
///
/// Solves a system of linear equations, AX = B, with A QR factorized.
///
diff --git a/src/Numerics/LinearAlgebra/Single/Solvers/Iterative/MlkBiCgStab.cs b/src/Numerics/LinearAlgebra/Single/Solvers/Iterative/MlkBiCgStab.cs
index 0e3c452a..2b959c28 100644
--- a/src/Numerics/LinearAlgebra/Single/Solvers/Iterative/MlkBiCgStab.cs
+++ b/src/Numerics/LinearAlgebra/Single/Solvers/Iterative/MlkBiCgStab.cs
@@ -34,8 +34,8 @@ namespace MathNet.Numerics.LinearAlgebra.Single.Solvers.Iterative
using System.Collections.Generic;
using System.Diagnostics;
using Distributions;
- using Factorization;
using Generic;
+ using Generic.Factorization;
using Generic.Solvers;
using Generic.Solvers.Preconditioners;
using Generic.Solvers.Status;
@@ -638,7 +638,7 @@ namespace MathNet.Numerics.LinearAlgebra.Single.Solvers.Iterative
}
// Compute the orthogonalization.
- var gs = new GramSchmidt(matrix);
+ var gs = matrix.GramSchmidt();
var orthogonalMatrix = gs.Q;
// Now transfer this to vectors
diff --git a/src/Numerics/Numerics.csproj b/src/Numerics/Numerics.csproj
index 16cc424a..a2f4349a 100644
--- a/src/Numerics/Numerics.csproj
+++ b/src/Numerics/Numerics.csproj
@@ -119,6 +119,7 @@
+
@@ -129,11 +130,13 @@
+
+
-
+
@@ -159,11 +162,13 @@
+
+
-
+
@@ -189,16 +194,21 @@
+
+
+
+
+
-
+
@@ -233,7 +243,7 @@
-
+
diff --git a/src/Numerics/SpecialFunctions/Stability.cs b/src/Numerics/SpecialFunctions/Stability.cs
index 984fe9a1..27e137df 100644
--- a/src/Numerics/SpecialFunctions/Stability.cs
+++ b/src/Numerics/SpecialFunctions/Stability.cs
@@ -31,6 +31,7 @@
namespace MathNet.Numerics
{
using System;
+ using System.Numerics;
public partial class SpecialFunctions
{
@@ -66,6 +67,54 @@ namespace MathNet.Numerics
);
}
+ ///
+ /// Numerically stable hypotenuse of a right angle triangle, i.e. (a,b) -> sqrt(a^2 + b^2)
+ ///
+ /// The length of side a of the triangle.
+ /// The length of side b of the triangle.
+ /// Returns sqrt(a2 + b2) without underflow/overflow.
+ public static Complex Hypotenuse(Complex a, Complex b)
+ {
+ if (a.Magnitude > b.Magnitude)
+ {
+ var r = b.Magnitude / a.Magnitude;
+ return a.Magnitude * Math.Sqrt(1 + (r * r));
+ }
+
+ if (b != 0.0)
+ {
+ // NOTE (ruegg): not "!b.AlmostZero()" to avoid convergence issues (e.g. in SVD algorithm)
+ var r = a.Magnitude / b.Magnitude;
+ return b.Magnitude * Math.Sqrt(1 + (r * r));
+ }
+
+ return 0d;
+ }
+
+ ///
+ /// Numerically stable hypotenuse of a right angle triangle, i.e. (a,b) -> sqrt(a^2 + b^2)
+ ///
+ /// The length of side a of the triangle.
+ /// The length of side b of the triangle.
+ /// Returns sqrt(a2 + b2) without underflow/overflow.
+ public static Complex32 Hypotenuse(Complex32 a, Complex32 b)
+ {
+ if (a.Magnitude > b.Magnitude)
+ {
+ var r = b.Magnitude / a.Magnitude;
+ return a.Magnitude * (float)Math.Sqrt(1 + (r * r));
+ }
+
+ if (b != 0.0f)
+ {
+ // NOTE (ruegg): not "!b.AlmostZero()" to avoid convergence issues (e.g. in SVD algorithm)
+ var r = a.Magnitude / b.Magnitude;
+ return b.Magnitude * (float)Math.Sqrt(1 + (r * r));
+ }
+
+ return 0f;
+ }
+
///
/// Numerically stable hypotenuse of a right angle triangle, i.e. (a,b) -> sqrt(a^2 + b^2)
///
diff --git a/src/Silverlight/Silverlight.csproj b/src/Silverlight/Silverlight.csproj
index f6a2c9a7..405a546a 100644
--- a/src/Silverlight/Silverlight.csproj
+++ b/src/Silverlight/Silverlight.csproj
@@ -284,6 +284,12 @@
LinearAlgebra\Complex32\Factorization\DenseCholesky.cs
+
+ LinearAlgebra\Complex32\Factorization\DenseEvd.cs
+
+
+ LinearAlgebra\Complex32\Factorization\DenseGramSchmidt.cs
+
LinearAlgebra\Complex32\Factorization\DenseLU.cs
@@ -293,15 +299,15 @@
LinearAlgebra\Complex32\Factorization\DenseSvd.cs
-
- LinearAlgebra\Complex32\Factorization\GramSchmidt.cs
-
LinearAlgebra\Complex32\Factorization\UserCholesky.cs
LinearAlgebra\Complex32\Factorization\UserEvd.cs
+
+ LinearAlgebra\Complex32\Factorization\UserGramSchmidt.cs
+
LinearAlgebra\Complex32\Factorization\UserLU.cs
@@ -374,6 +380,12 @@
LinearAlgebra\Complex\Factorization\DenseCholesky.cs
+
+ LinearAlgebra\Complex\Factorization\DenseEvd.cs
+
+
+ LinearAlgebra\Complex\Factorization\DenseGramSchmidt.cs
+
LinearAlgebra\Complex\Factorization\DenseLU.cs
@@ -383,15 +395,15 @@
LinearAlgebra\Complex\Factorization\DenseSvd.cs
-
- LinearAlgebra\Complex\Factorization\GramSchmidt.cs
-
LinearAlgebra\Complex\Factorization\UserCholesky.cs
LinearAlgebra\Complex\Factorization\UserEvd.cs
+
+ LinearAlgebra\Complex\Factorization\UserGramSchmidt.cs
+
LinearAlgebra\Complex\Factorization\UserLU.cs
@@ -464,6 +476,12 @@
LinearAlgebra\Double\Factorization\DenseCholesky.cs
+
+ LinearAlgebra\Double\Factorization\DenseEvd.cs
+
+
+ LinearAlgebra\Double\Factorization\DenseGramSchmidt.cs
+
LinearAlgebra\Double\Factorization\DenseLU.cs
@@ -473,9 +491,6 @@
LinearAlgebra\Double\Factorization\DenseSvd.cs
-
- LinearAlgebra\Double\Factorization\GramSchmidt.cs
-
LinearAlgebra\Double\Factorization\SparseCholesky.cs
@@ -494,6 +509,9 @@
LinearAlgebra\Double\Factorization\UserEvd.cs
+
+ LinearAlgebra\Double\Factorization\UserGramSchmidt.cs
+
LinearAlgebra\Double\Factorization\UserLU.cs
@@ -569,6 +587,9 @@
LinearAlgebra\Generic\Factorization\Evd.cs
+
+ LinearAlgebra\Generic\Factorization\GramSchmidt.cs
+
LinearAlgebra\Single\DenseMatrix.cs
@@ -581,6 +602,12 @@
LinearAlgebra\Single\Factorization\DenseCholesky.cs
+
+ LinearAlgebra\Single\Factorization\DenseEvd.cs
+
+
+ LinearAlgebra\Single\Factorization\DenseGramSchmidt.cs
+
LinearAlgebra\Single\Factorization\DenseLU.cs
@@ -590,15 +617,15 @@
LinearAlgebra\Single\Factorization\DenseSvd.cs
-
- LinearAlgebra\Single\Factorization\GramSchmidt.cs
-
LinearAlgebra\Single\Factorization\UserCholesky.cs
LinearAlgebra\Single\Factorization\UserEvd.cs
+
+ LinearAlgebra\Single\Factorization\UserGramSchmidt.cs
+
LinearAlgebra\Single\Factorization\UserLU.cs
diff --git a/src/UnitTests/DistributionTests/Discrete/PoissonTests.cs b/src/UnitTests/DistributionTests/Discrete/PoissonTests.cs
new file mode 100644
index 00000000..93058ad3
--- /dev/null
+++ b/src/UnitTests/DistributionTests/Discrete/PoissonTests.cs
@@ -0,0 +1,219 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+// http://mathnetnumerics.codeplex.com
+//
+// Copyright (c) 2009-2010 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+namespace MathNet.Numerics.UnitTests.DistributionTests.Discrete
+{
+ using System;
+ using System.Linq;
+ using MbUnit.Framework;
+ using Distributions;
+
+ [TestFixture]
+ public class PoissonTests
+ {
+ [SetUp]
+ public void SetUp()
+ {
+ Control.CheckDistributionParameters = true;
+ }
+
+ [Test]
+ [Row(1.5)]
+ [Row(5.4)]
+ [Row(10.8)]
+ public void CanCreatePoisson(double lambda)
+ {
+ var d = new Poisson(lambda);
+ Assert.AreEqual(lambda, d.Lambda);
+ }
+
+ [Test]
+ [ExpectedException(typeof(ArgumentOutOfRangeException))]
+ [Row(Double.NaN)]
+ [Row(-1.5)]
+ [Row(0.0)]
+ public void PoissonCreateFailsWithBadParameters(double lambda)
+ {
+ var d = new Poisson(lambda);
+ }
+
+ [Test]
+ public void ValidateToString()
+ {
+ var d = new Poisson(0.3);
+ Assert.AreEqual(String.Format("Poisson(λ = {0})", 0.3), d.ToString());
+ }
+
+ [Test]
+ [Row(1.5)]
+ [Row(5.4)]
+ [Row(10.8)]
+ public void CanSetProbabilityOfOne(double lambda)
+ {
+ var d = new Poisson(0.3)
+ {
+ Lambda = lambda
+ };
+ }
+
+ [Test]
+ [ExpectedException(typeof(ArgumentOutOfRangeException))]
+ [Row(Double.NaN)]
+ [Row(-1.5)]
+ [Row(0.0)]
+ public void SetProbabilityOfOneFails(double lambda)
+ {
+ var d = new Poisson(0.3)
+ {
+ Lambda = lambda
+ };
+ }
+
+ [Test]
+ [Row(1.5)]
+ [Row(5.4)]
+ [Row(10.8)]
+ public void ValidateEntropy(double lambda)
+ {
+ var d = new Poisson(lambda);
+ Assert.AreEqual((0.5 * Math.Log(2 * Math.PI * Math.E * lambda)) - (1.0 / (12.0 * lambda)) - 1.0 / (24.0 * lambda * lambda) - (19.0 / (360.0 * lambda * lambda * lambda)), d.Entropy);
+ }
+
+ [Test]
+ [Row(1.5)]
+ [Row(5.4)]
+ [Row(10.8)]
+ public void ValidateSkewness(double lambda)
+ {
+ var d = new Poisson(lambda);
+ Assert.AreEqual(1.0 / Math.Sqrt(lambda), d.Skewness);
+ }
+
+ [Test]
+ [Row(1.5)]
+ [Row(5.4)]
+ [Row(10.8)]
+ public void ValidateMode(double lambda)
+ {
+ var d = new Poisson(lambda);
+ Assert.AreEqual((int)Math.Floor(lambda), d.Mode);
+ }
+
+ [Test]
+ [Row(1.5)]
+ [Row(5.4)]
+ [Row(10.8)]
+ public void ValidateMedian(double lambda)
+ {
+ var d = new Poisson(lambda);
+ Assert.AreEqual((int)Math.Floor(lambda + (1.0 / 3.0) - (0.02 / lambda)), d.Median);
+ }
+
+ [Test]
+ public void ValidateMinimum()
+ {
+ var d = new Poisson(0.3);
+ Assert.AreEqual(0, d.Minimum);
+ }
+
+ [Test]
+ public void ValidateMaximum()
+ {
+ var d = new Poisson(0.3);
+ Assert.AreEqual(int.MaxValue, d.Maximum);
+ }
+
+ [Test]
+ [Row(1.5, 1, 0.334695240222645000000000000000)]
+ [Row(1.5, 10, 0.000003545747740570180000000000)]
+ [Row(1.5, 20, 0.000000000000000304971208961018)]
+ [Row(5.4, 1, 0.024389537090108400000000000000)]
+ [Row(5.4, 10, 0.026241240591792300000000000000)]
+ [Row(5.4, 20, 0.000000825202200316548000000000)]
+ [Row(10.8, 1, 0.000220314636840657000000000000)]
+ [Row(10.8, 10, 0.121365183659420000000000000000)]
+ [Row(10.8, 20, 0.003908139778574110000000000000)]
+ public void ValidateProbability(double lambda, int x, double result)
+ {
+ var d = new Poisson(lambda);
+ Assert.AreApproximatelyEqual(d.Probability(x), result, 1e-12);
+ }
+
+ [Test]
+ [Row(1.5, 1, 0.334695240222645000000000000000)]
+ [Row(1.5, 10, 0.000003545747740570180000000000)]
+ [Row(1.5, 20, 0.000000000000000304971208961018)]
+ [Row(5.4, 1, 0.024389537090108400000000000000)]
+ [Row(5.4, 10, 0.026241240591792300000000000000)]
+ [Row(5.4, 20, 0.000000825202200316548000000000)]
+ [Row(10.8, 1, 0.000220314636840657000000000000)]
+ [Row(10.8, 10, 0.121365183659420000000000000000)]
+ [Row(10.8, 20, 0.003908139778574110000000000000)]
+ public void ValidateProbabilityLn(double lambda, int x, double result)
+ {
+ var d = new Poisson(lambda);
+ Assert.AreApproximatelyEqual(d.ProbabilityLn(x), Math.Log(result), 1e-12);
+ }
+
+ [Test]
+ public void CanSample()
+ {
+ var d = new Poisson(0.3);
+ var s = d.Sample();
+ }
+
+ [Test]
+ public void CanSampleSequence()
+ {
+ var d = new Poisson(0.3);
+ var ied = d.Samples();
+ var e = ied.Take(5).ToArray();
+ }
+
+ [Test]
+ [Row(1.5, 1, 0.5578254003710750000000)]
+ [Row(1.5, 10, 0.9999994482467640000000)]
+ [Row(1.5, 20, 1.0000000000000000000000)]
+ [Row(5.4, 1, 0.0289061180327211000000)]
+ [Row(5.4, 10, 0.9774863006897650000000)]
+ [Row(5.4, 20, 0.9999997199928290000000)]
+ [Row(10.8, 1, 0.0002407141402518290000)]
+ [Row(10.8, 10, 0.4839692359955690000000)]
+ [Row(10.8, 20, 0.9961800769608090000000)]
+ [Row(20.1, 1, 0.0000000393516882521484)]
+ [Row(20.1, 10, 0.0102444128791257000000)]
+ [Row(20.1, 20, 0.5502097908860160000000)]
+ public void ValidateCumulativeDistribution(double lambda, int x, double result)
+ {
+ var d = new Poisson(lambda);
+ Assert.AreApproximatelyEqual(d.CumulativeDistribution(x), result, 1e-12);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/UnitTests/LinearAlgebraTests/Complex/Factorization/EvdTests.cs b/src/UnitTests/LinearAlgebraTests/Complex/Factorization/EvdTests.cs
new file mode 100644
index 00000000..cba81eae
--- /dev/null
+++ b/src/UnitTests/LinearAlgebraTests/Complex/Factorization/EvdTests.cs
@@ -0,0 +1,364 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+// http://mathnetnumerics.codeplex.com
+//
+// Copyright (c) 2009-2010 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Factorization
+{
+ using System.Numerics;
+ using LinearAlgebra.Complex;
+ using LinearAlgebra.Generic.Factorization;
+ using MbUnit.Framework;
+ using LinearAlgebra.Complex.Factorization;
+
+ public class EvdTests
+ {
+
+ [Test]
+ [ExpectedArgumentNullException]
+ public void ConstructorNull()
+ {
+ new DenseEvd(null);
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(10)]
+ [Row(100)]
+ public void CanFactorizeIdentity(int order)
+ {
+ var I = DenseMatrix.Identity(order);
+ var factorEvd = I.Evd();
+
+ Assert.AreEqual(I.RowCount, factorEvd.EVectors().RowCount);
+ Assert.AreEqual(I.RowCount, factorEvd.EVectors().ColumnCount);
+
+ Assert.AreEqual(I.ColumnCount, factorEvd.D().RowCount);
+ Assert.AreEqual(I.ColumnCount, factorEvd.D().ColumnCount);
+
+ for (var i = 0; i < factorEvd.EValues().Count; i++)
+ {
+ Assert.AreEqual(Complex.One, factorEvd.EValues()[i]);
+ }
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(2)]
+ [Row(5)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanFactorizeRandomMatrix(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
+ var factorEvd = matrixA.Evd();
+
+ Assert.AreEqual(order, factorEvd.EVectors().RowCount);
+ Assert.AreEqual(order, factorEvd.EVectors().ColumnCount);
+
+ Assert.AreEqual(order, factorEvd.D().RowCount);
+ Assert.AreEqual(order, factorEvd.D().ColumnCount);
+
+ // Make sure the A*V = λ*V
+ var matrixAv = matrixA * factorEvd.EVectors();
+ var matrixLv = factorEvd.EVectors() * factorEvd.D();
+
+ for (var i = 0; i < matrixAv.RowCount; i++)
+ {
+ for (var j = 0; j < matrixAv.ColumnCount; j++)
+ {
+ Assert.AreApproximatelyEqual(matrixAv[i, j].Real, matrixLv[i, j].Real, 1e-9);
+ Assert.AreApproximatelyEqual(matrixAv[i, j].Imaginary, matrixLv[i, j].Imaginary, 1e-9);
+ }
+ }
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(2)]
+ [Row(5)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanFactorizeRandomSymmetricMatrix(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteHermitianDenseMatrix(order);
+ var factorEvd = matrixA.Evd();
+
+ Assert.AreEqual(order, factorEvd.EVectors().RowCount);
+ Assert.AreEqual(order, factorEvd.EVectors().ColumnCount);
+
+ Assert.AreEqual(order, factorEvd.D().RowCount);
+ Assert.AreEqual(order, factorEvd.D().ColumnCount);
+
+ // Make sure the A = V*λ*VT
+ var matrix = factorEvd.EVectors() * factorEvd.D() * factorEvd.EVectors().ConjugateTranspose();
+
+ for (var i = 0; i < matrix.RowCount; i++)
+ {
+ for (var j = 0; j < matrix.ColumnCount; j++)
+ {
+ Assert.AreApproximatelyEqual(matrix[i, j].Real, matrixA[i, j].Real, 1e-9);
+ Assert.AreApproximatelyEqual(matrix[i, j].Imaginary, matrixA[i, j].Imaginary, 1e-9);
+ }
+ }
+ }
+
+ [Test]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CheckRankSquare(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
+ var factorEvd = matrixA.Evd();
+
+ Assert.AreEqual(factorEvd.Rank, order);
+ }
+
+
+ [Test]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CheckRankOfSquareSingular(int order)
+ {
+ var matrixA = new DenseMatrix(order, order);
+ matrixA[0, 0] = 1;
+ matrixA[order - 1, order - 1] = 1;
+ for (var i = 1; i < order - 1; i++)
+ {
+ matrixA[i, i - 1] = 1;
+ matrixA[i, i + 1] = 1;
+ matrixA[i - 1, i] = 1;
+ matrixA[i + 1, i] = 1;
+ }
+ var factorEvd = matrixA.Evd();
+
+ Assert.AreEqual(factorEvd.Determinant, 0);
+ Assert.AreEqual(factorEvd.Rank, order - 1);
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(10)]
+ [Row(100)]
+ public void IdentityDeterminantIsOne(int order)
+ {
+ var I = DenseMatrix.Identity(order);
+ var factorEvd = I.Evd();
+ Assert.AreEqual(1.0, factorEvd.Determinant);
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(2)]
+ [Row(5)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanSolveForRandomVectorAndSymmetricMatrix(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteHermitianDenseMatrix(order);
+ var matrixACopy = matrixA.Clone();
+ var factorEvd = matrixA.Evd();
+
+ var vectorb = MatrixLoader.GenerateRandomDenseVector(order);
+ var resultx = factorEvd.Solve(vectorb);
+
+ Assert.AreEqual(matrixA.ColumnCount, resultx.Count);
+
+ var bReconstruct = matrixA * resultx;
+
+ // Check the reconstruction.
+ for (var i = 0; i < vectorb.Count; i++)
+ {
+ Assert.AreApproximatelyEqual(vectorb[i].Real, bReconstruct[i].Real, 1e-9);
+ Assert.AreApproximatelyEqual(vectorb[i].Imaginary, bReconstruct[i].Imaginary, 1e-9);
+ }
+
+ // Make sure A didn't change.
+ for (var i = 0; i < matrixA.RowCount; i++)
+ {
+ for (var j = 0; j < matrixA.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
+ }
+ }
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(2)]
+ [Row(5)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanSolveForRandomMatrixAndSymmetricMatrix(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteHermitianDenseMatrix(order);
+ var matrixACopy = matrixA.Clone();
+ var factorEvd = matrixA.Evd();
+
+ var matrixB = MatrixLoader.GenerateRandomDenseMatrix(order, order);
+ var matrixX = factorEvd.Solve(matrixB);
+
+ // The solution X row dimension is equal to the column dimension of A
+ Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
+ // The solution X has the same number of columns as B
+ Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
+
+ var matrixBReconstruct = matrixA * matrixX;
+
+ // Check the reconstruction.
+ for (var i = 0; i < matrixB.RowCount; i++)
+ {
+ for (var j = 0; j < matrixB.ColumnCount; j++)
+ {
+ Assert.AreApproximatelyEqual(matrixB[i, j].Real, matrixBReconstruct[i, j].Real, 1e-9);
+ Assert.AreApproximatelyEqual(matrixB[i, j].Imaginary, matrixBReconstruct[i, j].Imaginary, 1e-9);
+ }
+ }
+
+ // Make sure A didn't change.
+ for (var i = 0; i < matrixA.RowCount; i++)
+ {
+ for (var j = 0; j < matrixA.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
+ }
+ }
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(2)]
+ [Row(5)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanSolveForRandomVectorAndSymmetricMatrixWhenResultVectorGiven(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteHermitianDenseMatrix(order);
+ var matrixACopy = matrixA.Clone();
+ var factorEvd = matrixA.Evd();
+ var vectorb = MatrixLoader.GenerateRandomDenseVector(order);
+ var vectorbCopy = vectorb.Clone();
+ var resultx = new DenseVector(order);
+ factorEvd.Solve(vectorb, resultx);
+
+ var bReconstruct = matrixA * resultx;
+
+ // Check the reconstruction.
+ for (var i = 0; i < vectorb.Count; i++)
+ {
+ Assert.AreApproximatelyEqual(vectorb[i].Real, bReconstruct[i].Real, 1e-9);
+ Assert.AreApproximatelyEqual(vectorb[i].Imaginary, bReconstruct[i].Imaginary, 1e-9);
+ }
+
+ // Make sure A didn't change.
+ for (var i = 0; i < matrixA.RowCount; i++)
+ {
+ for (var j = 0; j < matrixA.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
+ }
+ }
+
+ // Make sure b didn't change.
+ for (var i = 0; i < vectorb.Count; i++)
+ {
+ Assert.AreEqual(vectorbCopy[i], vectorb[i]);
+ }
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(2)]
+ [Row(5)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanSolveForRandomMatrixAndSymmetricMatrixWhenResultMatrixGiven(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteHermitianDenseMatrix(order);
+ var matrixACopy = matrixA.Clone();
+ var factorEvd = matrixA.Evd();
+
+ var matrixB = MatrixLoader.GenerateRandomDenseMatrix(order, order);
+ var matrixBCopy = matrixB.Clone();
+
+ var matrixX = new DenseMatrix(order, order);
+ factorEvd.Solve(matrixB, matrixX);
+
+ // The solution X row dimension is equal to the column dimension of A
+ Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
+ // The solution X has the same number of columns as B
+ Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
+
+ var matrixBReconstruct = matrixA * matrixX;
+
+ // Check the reconstruction.
+ for (var i = 0; i < matrixB.RowCount; i++)
+ {
+ for (var j = 0; j < matrixB.ColumnCount; j++)
+ {
+ Assert.AreApproximatelyEqual(matrixB[i, j].Real, matrixBReconstruct[i, j].Real, 1e-9);
+ Assert.AreApproximatelyEqual(matrixB[i, j].Imaginary, matrixBReconstruct[i, j].Imaginary, 1e-9);
+ }
+ }
+
+ // Make sure A didn't change.
+ for (var i = 0; i < matrixA.RowCount; i++)
+ {
+ for (var j = 0; j < matrixA.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
+ }
+ }
+
+ // Make sure B didn't change.
+ for (var i = 0; i < matrixB.RowCount; i++)
+ {
+ for (var j = 0; j < matrixB.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixBCopy[i, j], matrixB[i, j]);
+ }
+ }
+ }
+ }
+}
diff --git a/src/UnitTests/LinearAlgebraTests/Complex/Factorization/GramSchmidtTests.cs b/src/UnitTests/LinearAlgebraTests/Complex/Factorization/GramSchmidtTests.cs
index 5ef1872b..783ad427 100644
--- a/src/UnitTests/LinearAlgebraTests/Complex/Factorization/GramSchmidtTests.cs
+++ b/src/UnitTests/LinearAlgebraTests/Complex/Factorization/GramSchmidtTests.cs
@@ -30,6 +30,7 @@
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Factorization
{
+ using LinearAlgebra.Complex;
using LinearAlgebra.Generic.Factorization;
using MbUnit.Framework;
using LinearAlgebra.Complex.Factorization;
@@ -40,14 +41,14 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Factorization
[ExpectedArgumentNullException]
public void ConstructorNull()
{
- new GramSchmidt(null);
+ new DenseGramSchmidt(null);
}
[Test]
[ExpectedArgumentException]
public void WideMatrixThrowsInvalidMatrixOperationException()
{
- new GramSchmidt(new UserDefinedMatrix(3, 4));
+ new DenseGramSchmidt(new DenseMatrix(3, 4));
}
[Test]
@@ -56,7 +57,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Factorization
[Row(100)]
public void CanFactorizeIdentity(int order)
{
- var I = UserDefinedMatrix.Identity(order);
+ var I = DenseMatrix.Identity(order);
var factorGramSchmidt = I.GramSchmidt();
Assert.AreEqual(I.RowCount, factorGramSchmidt.Q.RowCount);
@@ -100,7 +101,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Factorization
[Row(100)]
public void IdentityDeterminantIsOne(int order)
{
- var I = UserDefinedMatrix.Identity(order);
+ var I = DenseMatrix.Identity(order);
var factorGramSchmidt = I.GramSchmidt();
Assert.AreEqual(1.0, factorGramSchmidt.Determinant);
}
@@ -115,7 +116,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Factorization
[MultipleAsserts]
public void CanFactorizeRandomMatrix(int row, int column)
{
- var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(row, column);
+ var matrixA = MatrixLoader.GenerateRandomDenseMatrix(row, column);
var factorGramSchmidt = matrixA.GramSchmidt();
// Make sure the Q has the right dimensions.
@@ -180,11 +181,11 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Factorization
[MultipleAsserts]
public void CanSolveForRandomVector(int order)
{
- var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorGramSchmidt = matrixA.GramSchmidt();
- var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order);
+ var vectorb = MatrixLoader.GenerateRandomDenseVector(order);
var resultx = factorGramSchmidt.Solve(vectorb);
Assert.AreEqual(matrixA.ColumnCount, resultx.Count);
@@ -218,11 +219,11 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Factorization
[MultipleAsserts]
public void CanSolveForRandomMatrix(int order)
{
- var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorGramSchmidt = matrixA.GramSchmidt();
- var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixB = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixX = factorGramSchmidt.Solve(matrixB);
// The solution X row dimension is equal to the column dimension of A
@@ -262,12 +263,12 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Factorization
[MultipleAsserts]
public void CanSolveForRandomVectorWhenResultVectorGiven(int order)
{
- var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorGramSchmidt = matrixA.GramSchmidt();
- var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order);
+ var vectorb = MatrixLoader.GenerateRandomDenseVector(order);
var vectorbCopy = vectorb.Clone();
- var resultx = new UserDefinedVector(order);
+ var resultx = new DenseVector(order);
factorGramSchmidt.Solve(vectorb,resultx);
Assert.AreEqual(vectorb.Count, resultx.Count);
@@ -307,14 +308,14 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Factorization
[MultipleAsserts]
public void CanSolveForRandomMatrixWhenResultMatrixGiven(int order)
{
- var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorGramSchmidt = matrixA.GramSchmidt();
- var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixB = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixBCopy = matrixB.Clone();
- var matrixX = new UserDefinedMatrix(order, order);
+ var matrixX = new DenseMatrix(order, order);
factorGramSchmidt.Solve(matrixB,matrixX);
// The solution X row dimension is equal to the column dimension of A
diff --git a/src/UnitTests/LinearAlgebraTests/Complex/Factorization/UserEvdTests.cs b/src/UnitTests/LinearAlgebraTests/Complex/Factorization/UserEvdTests.cs
index b7012729..7d2b7bc2 100644
--- a/src/UnitTests/LinearAlgebraTests/Complex/Factorization/UserEvdTests.cs
+++ b/src/UnitTests/LinearAlgebraTests/Complex/Factorization/UserEvdTests.cs
@@ -191,10 +191,10 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Factorization
{
var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteHermitianUserDefinedMatrix(order);
var matrixACopy = matrixA.Clone();
- var factorSvd = matrixA.Svd(true);
+ var factorEvd = matrixA.Evd();
var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order);
- var resultx = factorSvd.Solve(vectorb);
+ var resultx = factorEvd.Solve(vectorb);
Assert.AreEqual(matrixA.ColumnCount, resultx.Count);
@@ -229,10 +229,10 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Factorization
{
var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteHermitianUserDefinedMatrix(order);
var matrixACopy = matrixA.Clone();
- var factorSvd = matrixA.Svd(true);
+ var factorEvd = matrixA.Evd();
var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
- var matrixX = factorSvd.Solve(matrixB);
+ var matrixX = factorEvd.Solve(matrixB);
// The solution X row dimension is equal to the column dimension of A
Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
@@ -273,11 +273,11 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Factorization
{
var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteHermitianUserDefinedMatrix(order);
var matrixACopy = matrixA.Clone();
- var factorSvd = matrixA.Svd(true);
+ var factorEvd = matrixA.Evd();
var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order);
var vectorbCopy = vectorb.Clone();
var resultx = new UserDefinedVector(order);
- factorSvd.Solve(vectorb, resultx);
+ factorEvd.Solve(vectorb, resultx);
var bReconstruct = matrixA * resultx;
@@ -316,13 +316,13 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Factorization
{
var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteHermitianUserDefinedMatrix(order);
var matrixACopy = matrixA.Clone();
- var factorSvd = matrixA.Svd(true);
+ var factorEvd = matrixA.Evd();
var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
var matrixBCopy = matrixB.Clone();
var matrixX = new UserDefinedMatrix(order, order);
- factorSvd.Solve(matrixB, matrixX);
+ factorEvd.Solve(matrixB, matrixX);
// The solution X row dimension is equal to the column dimension of A
Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
diff --git a/src/UnitTests/LinearAlgebraTests/Complex/Factorization/UserGramSchmidtTests.cs b/src/UnitTests/LinearAlgebraTests/Complex/Factorization/UserGramSchmidtTests.cs
new file mode 100644
index 00000000..9eb64a57
--- /dev/null
+++ b/src/UnitTests/LinearAlgebraTests/Complex/Factorization/UserGramSchmidtTests.cs
@@ -0,0 +1,356 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+// http://mathnetnumerics.codeplex.com
+//
+// Copyright (c) 2009-2010 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.Factorization
+{
+ using LinearAlgebra.Generic.Factorization;
+ using MbUnit.Framework;
+ using LinearAlgebra.Complex.Factorization;
+
+ public class UserGramSchmidtTests
+ {
+ [Test]
+ [ExpectedArgumentNullException]
+ public void ConstructorNull()
+ {
+ new UserGramSchmidt(null);
+ }
+
+ [Test]
+ [ExpectedArgumentException]
+ public void WideMatrixThrowsInvalidMatrixOperationException()
+ {
+ new UserGramSchmidt(new UserDefinedMatrix(3, 4));
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(10)]
+ [Row(100)]
+ public void CanFactorizeIdentity(int order)
+ {
+ var I = UserDefinedMatrix.Identity(order);
+ var factorGramSchmidt = I.GramSchmidt();
+
+ Assert.AreEqual(I.RowCount, factorGramSchmidt.Q.RowCount);
+ Assert.AreEqual(I.ColumnCount, factorGramSchmidt.Q.ColumnCount);
+
+ for (var i = 0; i < factorGramSchmidt.R.RowCount; i++)
+ {
+ for (var j = 0; j < factorGramSchmidt.R.ColumnCount; j++)
+ {
+ if (i == j)
+ {
+ Assert.AreEqual(1.0, factorGramSchmidt.R[i, j]);
+ }
+ else
+ {
+ Assert.AreEqual(0.0, factorGramSchmidt.R[i, j]);
+ }
+ }
+ }
+
+ for (var i = 0; i < factorGramSchmidt.Q.RowCount; i++)
+ {
+ for (var j = 0; j < factorGramSchmidt.Q.ColumnCount; j++)
+ {
+ if (i == j)
+ {
+ Assert.AreEqual(1.0, factorGramSchmidt.Q[i, j]);
+ }
+ else
+ {
+ Assert.AreEqual(0.0, factorGramSchmidt.Q[i, j]);
+ }
+ }
+ }
+ }
+
+
+ [Test]
+ [Row(1)]
+ [Row(10)]
+ [Row(100)]
+ public void IdentityDeterminantIsOne(int order)
+ {
+ var I = UserDefinedMatrix.Identity(order);
+ var factorGramSchmidt = I.GramSchmidt();
+ Assert.AreEqual(1.0, factorGramSchmidt.Determinant);
+ }
+
+ [Test]
+ [Row(1,1)]
+ [Row(2,2)]
+ [Row(5,5)]
+ [Row(10,6)]
+ [Row(50,48)]
+ [Row(100,98)]
+ [MultipleAsserts]
+ public void CanFactorizeRandomMatrix(int row, int column)
+ {
+ var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(row, column);
+ var factorGramSchmidt = matrixA.GramSchmidt();
+
+ // Make sure the Q has the right dimensions.
+ Assert.AreEqual(row, factorGramSchmidt.Q.RowCount);
+ Assert.AreEqual(column, factorGramSchmidt.Q.ColumnCount);
+
+ // Make sure the R has the right dimensions.
+ Assert.AreEqual(column, factorGramSchmidt.R.RowCount);
+ Assert.AreEqual(column, factorGramSchmidt.R.ColumnCount);
+
+ // Make sure the R factor is upper triangular.
+ for (var i = 0; i < factorGramSchmidt.R.RowCount; i++)
+ {
+ for (var j = 0; j < factorGramSchmidt.R.ColumnCount; j++)
+ {
+ if (i > j)
+ {
+ Assert.AreEqual(0.0, factorGramSchmidt.R[i, j]);
+ }
+ }
+ }
+
+ // Make sure the Q*R is the original matrix.
+ var matrixQfromR = factorGramSchmidt.Q * factorGramSchmidt.R;
+ for (var i = 0; i < matrixQfromR.RowCount; i++)
+ {
+ for (var j = 0; j < matrixQfromR.ColumnCount; j++)
+ {
+ Assert.AreApproximatelyEqual(matrixA[i, j].Real, matrixQfromR[i, j].Real, 1.0e-9);
+ Assert.AreApproximatelyEqual(matrixA[i, j].Imaginary, matrixQfromR[i, j].Imaginary, 1.0e-9);
+ }
+ }
+
+ // Make sure the Q is unitary --> (Q*)x(Q) = I
+ var matrixQсtQ = factorGramSchmidt.Q.ConjugateTranspose() * factorGramSchmidt.Q;
+ for (var i = 0; i < matrixQсtQ.RowCount; i++)
+ {
+ for (var j = 0; j < matrixQсtQ.ColumnCount; j++)
+ {
+ if (i == j)
+ {
+ Assert.AreApproximatelyEqual(matrixQсtQ[i, j].Real, 1.0, 1.0e-9);
+ Assert.AreApproximatelyEqual(matrixQсtQ[i, j].Imaginary, 0.0, 1.0e-9);
+ }
+ else
+ {
+ Assert.AreApproximatelyEqual(matrixQсtQ[i, j].Real, 0.0, 1.0e-9);
+ Assert.AreApproximatelyEqual(matrixQсtQ[i, j].Imaginary, 0.0, 1.0e-9);
+ }
+ }
+ }
+
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(2)]
+ [Row(5)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanSolveForRandomVector(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixACopy = matrixA.Clone();
+ var factorGramSchmidt = matrixA.GramSchmidt();
+
+ var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order);
+ var resultx = factorGramSchmidt.Solve(vectorb);
+
+ Assert.AreEqual(matrixA.ColumnCount, resultx.Count);
+
+ var bReconstruct = matrixA * resultx;
+
+ // Check the reconstruction.
+ for (var i = 0; i < order; i++)
+ {
+ Assert.AreApproximatelyEqual(vectorb[i].Real, bReconstruct[i].Real, 1.0e-9);
+ Assert.AreApproximatelyEqual(vectorb[i].Imaginary, bReconstruct[i].Imaginary, 1.0e-9);
+ }
+
+ // Make sure A didn't change.
+ for (var i = 0; i < matrixA.RowCount; i++)
+ {
+ for (var j = 0; j < matrixA.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
+ }
+ }
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(4)]
+ [Row(8)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanSolveForRandomMatrix(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixACopy = matrixA.Clone();
+ var factorGramSchmidt = matrixA.GramSchmidt();
+
+ var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixX = factorGramSchmidt.Solve(matrixB);
+
+ // The solution X row dimension is equal to the column dimension of A
+ Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
+ // The solution X has the same number of columns as B
+ Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
+
+ var matrixBReconstruct = matrixA * matrixX;
+
+ // Check the reconstruction.
+ for (var i = 0; i < matrixB.RowCount; i++)
+ {
+ for (var j = 0; j < matrixB.ColumnCount; j++)
+ {
+ Assert.AreApproximatelyEqual(matrixB[i, j].Real, matrixBReconstruct[i, j].Real, 1.0e-9);
+ Assert.AreApproximatelyEqual(matrixB[i, j].Imaginary, matrixBReconstruct[i, j].Imaginary, 1.0e-9);
+ }
+ }
+
+ // Make sure A didn't change.
+ for (var i = 0; i < matrixA.RowCount; i++)
+ {
+ for (var j = 0; j < matrixA.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
+ }
+ }
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(2)]
+ [Row(5)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanSolveForRandomVectorWhenResultVectorGiven(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixACopy = matrixA.Clone();
+ var factorGramSchmidt = matrixA.GramSchmidt();
+ var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order);
+ var vectorbCopy = vectorb.Clone();
+ var resultx = new UserDefinedVector(order);
+ factorGramSchmidt.Solve(vectorb,resultx);
+
+ Assert.AreEqual(vectorb.Count, resultx.Count);
+
+ var bReconstruct = matrixA * resultx;
+
+ // Check the reconstruction.
+ for (var i = 0; i < vectorb.Count; i++)
+ {
+ Assert.AreApproximatelyEqual(vectorb[i].Real, bReconstruct[i].Real, 1.0e-9);
+ Assert.AreApproximatelyEqual(vectorb[i].Imaginary, bReconstruct[i].Imaginary, 1.0e-9);
+ }
+
+ // Make sure A didn't change.
+ for (var i = 0; i < matrixA.RowCount; i++)
+ {
+ for (var j = 0; j < matrixA.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
+ }
+ }
+
+ // Make sure b didn't change.
+ for (var i = 0; i < vectorb.Count; i++)
+ {
+ Assert.AreEqual(vectorbCopy[i], vectorb[i]);
+ }
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(4)]
+ [Row(8)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanSolveForRandomMatrixWhenResultMatrixGiven(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixACopy = matrixA.Clone();
+ var factorGramSchmidt = matrixA.GramSchmidt();
+
+ var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixBCopy = matrixB.Clone();
+
+ var matrixX = new UserDefinedMatrix(order, order);
+ factorGramSchmidt.Solve(matrixB,matrixX);
+
+ // The solution X row dimension is equal to the column dimension of A
+ Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
+ // The solution X has the same number of columns as B
+ Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
+
+ var matrixBReconstruct = matrixA * matrixX;
+
+ // Check the reconstruction.
+ for (var i = 0; i < matrixB.RowCount; i++)
+ {
+ for (var j = 0; j < matrixB.ColumnCount; j++)
+ {
+ Assert.AreApproximatelyEqual(matrixB[i, j].Real, matrixBReconstruct[i, j].Real, 1.0e-9);
+ Assert.AreApproximatelyEqual(matrixB[i, j].Imaginary, matrixBReconstruct[i, j].Imaginary, 1.0e-9);
+ }
+ }
+
+ // Make sure A didn't change.
+ for (var i = 0; i < matrixA.RowCount; i++)
+ {
+ for (var j = 0; j < matrixA.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
+ }
+ }
+
+ // Make sure B didn't change.
+ for (var i = 0; i < matrixB.RowCount; i++)
+ {
+ for (var j = 0; j < matrixB.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixBCopy[i, j], matrixB[i, j]);
+ }
+ }
+ }
+ }
+}
diff --git a/src/UnitTests/LinearAlgebraTests/Complex32/Factorization/EvdTests.cs b/src/UnitTests/LinearAlgebraTests/Complex32/Factorization/EvdTests.cs
new file mode 100644
index 00000000..9adede82
--- /dev/null
+++ b/src/UnitTests/LinearAlgebraTests/Complex32/Factorization/EvdTests.cs
@@ -0,0 +1,365 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+// http://mathnetnumerics.codeplex.com
+//
+// Copyright (c) 2009-2010 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32.Factorization
+{
+ using System.Numerics;
+ using LinearAlgebra.Complex32;
+ using LinearAlgebra.Generic.Factorization;
+ using MbUnit.Framework;
+ using LinearAlgebra.Complex.Factorization;
+
+ public class EvdTests
+ {
+
+ [Test]
+ [ExpectedArgumentNullException]
+ public void ConstructorNull()
+ {
+ new DenseEvd(null);
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(10)]
+ [Row(100)]
+ public void CanFactorizeIdentity(int order)
+ {
+ var I = DenseMatrix.Identity(order);
+ var factorEvd = I.Evd();
+
+ Assert.AreEqual(I.RowCount, factorEvd.EVectors().RowCount);
+ Assert.AreEqual(I.RowCount, factorEvd.EVectors().ColumnCount);
+
+ Assert.AreEqual(I.ColumnCount, factorEvd.D().RowCount);
+ Assert.AreEqual(I.ColumnCount, factorEvd.D().ColumnCount);
+
+ for (var i = 0; i < factorEvd.EValues().Count; i++)
+ {
+ Assert.AreEqual(Complex.One, factorEvd.EValues()[i]);
+ }
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(2)]
+ [Row(5)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanFactorizeRandomMatrix(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
+ var factorEvd = matrixA.Evd();
+
+ Assert.AreEqual(order, factorEvd.EVectors().RowCount);
+ Assert.AreEqual(order, factorEvd.EVectors().ColumnCount);
+
+ Assert.AreEqual(order, factorEvd.D().RowCount);
+ Assert.AreEqual(order, factorEvd.D().ColumnCount);
+
+ // Make sure the A*V = λ*V
+ var matrixAv = matrixA * factorEvd.EVectors();
+ var matrixLv = factorEvd.EVectors() * factorEvd.D();
+
+ for (var i = 0; i < matrixAv.RowCount; i++)
+ {
+ for (var j = 0; j < matrixAv.ColumnCount; j++)
+ {
+ Assert.AreApproximatelyEqual(matrixAv[i, j].Real, matrixLv[i, j].Real, 1e-4f);
+ Assert.AreApproximatelyEqual(matrixAv[i, j].Imaginary, matrixLv[i, j].Imaginary, 1e-4f);
+ }
+ }
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(2)]
+ [Row(5)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ [Ignore]
+ public void CanFactorizeRandomSymmetricMatrix(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteHermitianDenseMatrix(order);
+ var factorEvd = matrixA.Evd();
+
+ Assert.AreEqual(order, factorEvd.EVectors().RowCount);
+ Assert.AreEqual(order, factorEvd.EVectors().ColumnCount);
+
+ Assert.AreEqual(order, factorEvd.D().RowCount);
+ Assert.AreEqual(order, factorEvd.D().ColumnCount);
+
+ // Make sure the A = V*λ*VT
+ var matrix = factorEvd.EVectors() * factorEvd.D() * factorEvd.EVectors().ConjugateTranspose();
+
+ for (var i = 0; i < matrix.RowCount; i++)
+ {
+ for (var j = 0; j < matrix.ColumnCount; j++)
+ {
+ Assert.AreApproximatelyEqual(matrix[i, j].Real, matrixA[i, j].Real, 1e-3f);
+ Assert.AreApproximatelyEqual(matrix[i, j].Imaginary, matrixA[i, j].Imaginary, 1e-3f);
+ }
+ }
+ }
+
+ [Test]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CheckRankSquare(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
+ var factorEvd = matrixA.Evd();
+
+ Assert.AreEqual(factorEvd.Rank, order);
+ }
+
+
+ [Test]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CheckRankOfSquareSingular(int order)
+ {
+ var matrixA = new DenseMatrix(order, order);
+ matrixA[0, 0] = 1;
+ matrixA[order - 1, order - 1] = 1;
+ for (var i = 1; i < order - 1; i++)
+ {
+ matrixA[i, i - 1] = 1;
+ matrixA[i, i + 1] = 1;
+ matrixA[i - 1, i] = 1;
+ matrixA[i + 1, i] = 1;
+ }
+ var factorEvd = matrixA.Evd();
+
+ Assert.AreEqual(factorEvd.Determinant, 0);
+ Assert.AreEqual(factorEvd.Rank, order - 1);
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(10)]
+ [Row(100)]
+ public void IdentityDeterminantIsOne(int order)
+ {
+ var I = DenseMatrix.Identity(order);
+ var factorEvd = I.Evd();
+ Assert.AreEqual(1.0, factorEvd.Determinant);
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(2)]
+ [Row(5)]
+ [Row(10)]
+ [Row(50)]
+ //[Row(100)]
+ [MultipleAsserts]
+ public void CanSolveForRandomVectorAndSymmetricMatrix(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteHermitianDenseMatrix(order);
+ var matrixACopy = matrixA.Clone();
+ var factorEvd = matrixA.Evd();
+
+ var vectorb = MatrixLoader.GenerateRandomDenseVector(order);
+ var resultx = factorEvd.Solve(vectorb);
+
+ Assert.AreEqual(matrixA.ColumnCount, resultx.Count);
+
+ var bReconstruct = matrixA * resultx;
+
+ // Check the reconstruction.
+ for (var i = 0; i < vectorb.Count; i++)
+ {
+ Assert.AreApproximatelyEqual(vectorb[i].Real, bReconstruct[i].Real, 1e-3f);
+ Assert.AreApproximatelyEqual(vectorb[i].Imaginary, bReconstruct[i].Imaginary, 1e-3f);
+ }
+
+ // Make sure A didn't change.
+ for (var i = 0; i < matrixA.RowCount; i++)
+ {
+ for (var j = 0; j < matrixA.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
+ }
+ }
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(2)]
+ [Row(5)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanSolveForRandomMatrixAndSymmetricMatrix(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteHermitianDenseMatrix(order);
+ var matrixACopy = matrixA.Clone();
+ var factorEvd = matrixA.Evd();
+
+ var matrixB = MatrixLoader.GenerateRandomDenseMatrix(order, order);
+ var matrixX = factorEvd.Solve(matrixB);
+
+ // The solution X row dimension is equal to the column dimension of A
+ Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
+ // The solution X has the same number of columns as B
+ Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
+
+ var matrixBReconstruct = matrixA * matrixX;
+
+ // Check the reconstruction.
+ for (var i = 0; i < matrixB.RowCount; i++)
+ {
+ for (var j = 0; j < matrixB.ColumnCount; j++)
+ {
+ Assert.AreApproximatelyEqual(matrixB[i, j].Real, matrixBReconstruct[i, j].Real, 1e-2f);
+ Assert.AreApproximatelyEqual(matrixB[i, j].Imaginary, matrixBReconstruct[i, j].Imaginary, 1e-2f);
+ }
+ }
+
+ // Make sure A didn't change.
+ for (var i = 0; i < matrixA.RowCount; i++)
+ {
+ for (var j = 0; j < matrixA.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
+ }
+ }
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(2)]
+ [Row(5)]
+ [Row(10)]
+ [Row(50)]
+ // [Row(100)]
+ [MultipleAsserts]
+ public void CanSolveForRandomVectorAndSymmetricMatrixWhenResultVectorGiven(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteHermitianDenseMatrix(order);
+ var matrixACopy = matrixA.Clone();
+ var factorEvd = matrixA.Evd();
+ var vectorb = MatrixLoader.GenerateRandomDenseVector(order);
+ var vectorbCopy = vectorb.Clone();
+ var resultx = new DenseVector(order);
+ factorEvd.Solve(vectorb, resultx);
+
+ var bReconstruct = matrixA * resultx;
+
+ // Check the reconstruction.
+ for (var i = 0; i < vectorb.Count; i++)
+ {
+ Assert.AreApproximatelyEqual(vectorb[i].Real, bReconstruct[i].Real, 1e-3f);
+ Assert.AreApproximatelyEqual(vectorb[i].Imaginary, bReconstruct[i].Imaginary, 1e-3f);
+ }
+
+ // Make sure A didn't change.
+ for (var i = 0; i < matrixA.RowCount; i++)
+ {
+ for (var j = 0; j < matrixA.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
+ }
+ }
+
+ // Make sure b didn't change.
+ for (var i = 0; i < vectorb.Count; i++)
+ {
+ Assert.AreEqual(vectorbCopy[i], vectorb[i]);
+ }
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(2)]
+ [Row(5)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanSolveForRandomMatrixAndSymmetricMatrixWhenResultMatrixGiven(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteHermitianDenseMatrix(order);
+ var matrixACopy = matrixA.Clone();
+ var factorEvd = matrixA.Evd();
+
+ var matrixB = MatrixLoader.GenerateRandomDenseMatrix(order, order);
+ var matrixBCopy = matrixB.Clone();
+
+ var matrixX = new DenseMatrix(order, order);
+ factorEvd.Solve(matrixB, matrixX);
+
+ // The solution X row dimension is equal to the column dimension of A
+ Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
+ // The solution X has the same number of columns as B
+ Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
+
+ var matrixBReconstruct = matrixA * matrixX;
+
+ // Check the reconstruction.
+ for (var i = 0; i < matrixB.RowCount; i++)
+ {
+ for (var j = 0; j < matrixB.ColumnCount; j++)
+ {
+ Assert.AreApproximatelyEqual(matrixB[i, j].Real, matrixBReconstruct[i, j].Real, 1e-2f);
+ Assert.AreApproximatelyEqual(matrixB[i, j].Imaginary, matrixBReconstruct[i, j].Imaginary, 1e-2f);
+ }
+ }
+
+ // Make sure A didn't change.
+ for (var i = 0; i < matrixA.RowCount; i++)
+ {
+ for (var j = 0; j < matrixA.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
+ }
+ }
+
+ // Make sure B didn't change.
+ for (var i = 0; i < matrixB.RowCount; i++)
+ {
+ for (var j = 0; j < matrixB.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixBCopy[i, j], matrixB[i, j]);
+ }
+ }
+ }
+ }
+}
diff --git a/src/UnitTests/LinearAlgebraTests/Complex32/Factorization/GramSchmidtTests.cs b/src/UnitTests/LinearAlgebraTests/Complex32/Factorization/GramSchmidtTests.cs
index f2a7daec..0a4dd1ac 100644
--- a/src/UnitTests/LinearAlgebraTests/Complex32/Factorization/GramSchmidtTests.cs
+++ b/src/UnitTests/LinearAlgebraTests/Complex32/Factorization/GramSchmidtTests.cs
@@ -30,6 +30,7 @@
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32.Factorization
{
+ using LinearAlgebra.Complex32;
using LinearAlgebra.Generic.Factorization;
using MbUnit.Framework;
using LinearAlgebra.Complex32.Factorization;
@@ -40,14 +41,14 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32.Factorization
[ExpectedArgumentNullException]
public void ConstructorNull()
{
- new GramSchmidt(null);
+ new DenseGramSchmidt(null);
}
[Test]
[ExpectedArgumentException]
public void WideMatrixThrowsInvalidMatrixOperationException()
{
- new GramSchmidt(new UserDefinedMatrix(3, 4));
+ new DenseGramSchmidt(new DenseMatrix(3, 4));
}
[Test]
@@ -56,7 +57,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32.Factorization
[Row(100)]
public void CanFactorizeIdentity(int order)
{
- var I = UserDefinedMatrix.Identity(order);
+ var I = DenseMatrix.Identity(order);
var factorGramSchmidt = I.GramSchmidt();
Assert.AreEqual(I.RowCount, factorGramSchmidt.Q.RowCount);
@@ -100,7 +101,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32.Factorization
[Row(100)]
public void IdentityDeterminantIsOne(int order)
{
- var I = UserDefinedMatrix.Identity(order);
+ var I = DenseMatrix.Identity(order);
var factorGramSchmidt = I.GramSchmidt();
Assert.AreEqual(1.0f, factorGramSchmidt.Determinant);
}
@@ -115,7 +116,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32.Factorization
[MultipleAsserts]
public void CanFactorizeRandomMatrix(int row, int column)
{
- var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(row, column);
+ var matrixA = MatrixLoader.GenerateRandomDenseMatrix(row, column);
var factorGramSchmidt = matrixA.GramSchmidt();
// Make sure the Q has the right dimensions.
@@ -180,11 +181,11 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32.Factorization
[MultipleAsserts]
public void CanSolveForRandomVector(int order)
{
- var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorGramSchmidt = matrixA.GramSchmidt();
- var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order);
+ var vectorb = MatrixLoader.GenerateRandomDenseVector(order);
var resultx = factorGramSchmidt.Solve(vectorb);
Assert.AreEqual(matrixA.ColumnCount, resultx.Count);
@@ -218,11 +219,11 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32.Factorization
[MultipleAsserts]
public void CanSolveForRandomMatrix(int order)
{
- var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorGramSchmidt = matrixA.GramSchmidt();
- var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixB = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixX = factorGramSchmidt.Solve(matrixB);
// The solution X row dimension is equal to the column dimension of A
@@ -262,12 +263,12 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32.Factorization
[MultipleAsserts]
public void CanSolveForRandomVectorWhenResultVectorGiven(int order)
{
- var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorGramSchmidt = matrixA.GramSchmidt();
- var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order);
+ var vectorb = MatrixLoader.GenerateRandomDenseVector(order);
var vectorbCopy = vectorb.Clone();
- var resultx = new UserDefinedVector(order);
+ var resultx = new DenseVector(order);
factorGramSchmidt.Solve(vectorb,resultx);
Assert.AreEqual(vectorb.Count, resultx.Count);
@@ -307,14 +308,14 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32.Factorization
[MultipleAsserts]
public void CanSolveForRandomMatrixWhenResultMatrixGiven(int order)
{
- var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorGramSchmidt = matrixA.GramSchmidt();
- var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixB = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixBCopy = matrixB.Clone();
- var matrixX = new UserDefinedMatrix(order, order);
+ var matrixX = new DenseMatrix(order, order);
factorGramSchmidt.Solve(matrixB,matrixX);
// The solution X row dimension is equal to the column dimension of A
diff --git a/src/UnitTests/LinearAlgebraTests/Complex32/Factorization/UserEvdTests.cs b/src/UnitTests/LinearAlgebraTests/Complex32/Factorization/UserEvdTests.cs
index 9a1493f3..35f561dd 100644
--- a/src/UnitTests/LinearAlgebraTests/Complex32/Factorization/UserEvdTests.cs
+++ b/src/UnitTests/LinearAlgebraTests/Complex32/Factorization/UserEvdTests.cs
@@ -186,16 +186,16 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32.Factorization
[Row(5)]
[Row(10)]
[Row(50)]
- [Row(100)]
+ // [Row(100)]
[MultipleAsserts]
public void CanSolveForRandomVectorAndSymmetricMatrix(int order)
{
var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteHermitianUserDefinedMatrix(order);
var matrixACopy = matrixA.Clone();
- var factorSvd = matrixA.Svd(true);
+ var factorEvd = matrixA.Evd();
var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order);
- var resultx = factorSvd.Solve(vectorb);
+ var resultx = factorEvd.Solve(vectorb);
Assert.AreEqual(matrixA.ColumnCount, resultx.Count);
@@ -230,10 +230,10 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32.Factorization
{
var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteHermitianUserDefinedMatrix(order);
var matrixACopy = matrixA.Clone();
- var factorSvd = matrixA.Svd(true);
+ var factorEvd = matrixA.Evd();
var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
- var matrixX = factorSvd.Solve(matrixB);
+ var matrixX = factorEvd.Solve(matrixB);
// The solution X row dimension is equal to the column dimension of A
Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
@@ -268,17 +268,17 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32.Factorization
[Row(5)]
[Row(10)]
[Row(50)]
- [Row(100)]
+ // [Row(100)]
[MultipleAsserts]
public void CanSolveForRandomVectorAndSymmetricMatrixWhenResultVectorGiven(int order)
{
var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteHermitianUserDefinedMatrix(order);
var matrixACopy = matrixA.Clone();
- var factorSvd = matrixA.Svd(true);
+ var factorEvd = matrixA.Evd();
var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order);
var vectorbCopy = vectorb.Clone();
var resultx = new UserDefinedVector(order);
- factorSvd.Solve(vectorb, resultx);
+ factorEvd.Solve(vectorb, resultx);
var bReconstruct = matrixA * resultx;
@@ -317,13 +317,13 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32.Factorization
{
var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteHermitianUserDefinedMatrix(order);
var matrixACopy = matrixA.Clone();
- var factorSvd = matrixA.Svd(true);
+ var factorEvd = matrixA.Evd();
var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
var matrixBCopy = matrixB.Clone();
var matrixX = new UserDefinedMatrix(order, order);
- factorSvd.Solve(matrixB, matrixX);
+ factorEvd.Solve(matrixB, matrixX);
// The solution X row dimension is equal to the column dimension of A
Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
diff --git a/src/UnitTests/LinearAlgebraTests/Complex32/Factorization/UserGramSchmidtTests.cs b/src/UnitTests/LinearAlgebraTests/Complex32/Factorization/UserGramSchmidtTests.cs
new file mode 100644
index 00000000..e570e764
--- /dev/null
+++ b/src/UnitTests/LinearAlgebraTests/Complex32/Factorization/UserGramSchmidtTests.cs
@@ -0,0 +1,356 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+// http://mathnetnumerics.codeplex.com
+//
+// Copyright (c) 2009-2010 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32.Factorization
+{
+ using LinearAlgebra.Generic.Factorization;
+ using MbUnit.Framework;
+ using LinearAlgebra.Complex32.Factorization;
+
+ public class UserGramSchmidtTests
+ {
+ [Test]
+ [ExpectedArgumentNullException]
+ public void ConstructorNull()
+ {
+ new UserGramSchmidt(null);
+ }
+
+ [Test]
+ [ExpectedArgumentException]
+ public void WideMatrixThrowsInvalidMatrixOperationException()
+ {
+ new UserGramSchmidt(new UserDefinedMatrix(3, 4));
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(10)]
+ [Row(100)]
+ public void CanFactorizeIdentity(int order)
+ {
+ var I = UserDefinedMatrix.Identity(order);
+ var factorGramSchmidt = I.GramSchmidt();
+
+ Assert.AreEqual(I.RowCount, factorGramSchmidt.Q.RowCount);
+ Assert.AreEqual(I.ColumnCount, factorGramSchmidt.Q.ColumnCount);
+
+ for (var i = 0; i < factorGramSchmidt.R.RowCount; i++)
+ {
+ for (var j = 0; j < factorGramSchmidt.R.ColumnCount; j++)
+ {
+ if (i == j)
+ {
+ Assert.AreEqual(1.0f, factorGramSchmidt.R[i, j]);
+ }
+ else
+ {
+ Assert.AreEqual(0.0f, factorGramSchmidt.R[i, j]);
+ }
+ }
+ }
+
+ for (var i = 0; i < factorGramSchmidt.Q.RowCount; i++)
+ {
+ for (var j = 0; j < factorGramSchmidt.Q.ColumnCount; j++)
+ {
+ if (i == j)
+ {
+ Assert.AreEqual(1.0f, factorGramSchmidt.Q[i, j]);
+ }
+ else
+ {
+ Assert.AreEqual(0.0f, factorGramSchmidt.Q[i, j]);
+ }
+ }
+ }
+ }
+
+
+ [Test]
+ [Row(1)]
+ [Row(10)]
+ [Row(100)]
+ public void IdentityDeterminantIsOne(int order)
+ {
+ var I = UserDefinedMatrix.Identity(order);
+ var factorGramSchmidt = I.GramSchmidt();
+ Assert.AreEqual(1.0f, factorGramSchmidt.Determinant);
+ }
+
+ [Test]
+ [Row(1,1)]
+ [Row(2,2)]
+ [Row(5,5)]
+ [Row(10,6)]
+ [Row(50,48)]
+ [Row(100,98)]
+ [MultipleAsserts]
+ public void CanFactorizeRandomMatrix(int row, int column)
+ {
+ var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(row, column);
+ var factorGramSchmidt = matrixA.GramSchmidt();
+
+ // Make sure the Q has the right dimensions.
+ Assert.AreEqual(row, factorGramSchmidt.Q.RowCount);
+ Assert.AreEqual(column, factorGramSchmidt.Q.ColumnCount);
+
+ // Make sure the R has the right dimensions.
+ Assert.AreEqual(column, factorGramSchmidt.R.RowCount);
+ Assert.AreEqual(column, factorGramSchmidt.R.ColumnCount);
+
+ // Make sure the R factor is upper triangular.
+ for (var i = 0; i < factorGramSchmidt.R.RowCount; i++)
+ {
+ for (var j = 0; j < factorGramSchmidt.R.ColumnCount; j++)
+ {
+ if (i > j)
+ {
+ Assert.AreEqual(0.0f, factorGramSchmidt.R[i, j]);
+ }
+ }
+ }
+
+ // Make sure the Q*R is the original matrix.
+ var matrixQfromR = factorGramSchmidt.Q * factorGramSchmidt.R;
+ for (var i = 0; i < matrixQfromR.RowCount; i++)
+ {
+ for (var j = 0; j < matrixQfromR.ColumnCount; j++)
+ {
+ Assert.AreApproximatelyEqual(matrixA[i, j].Real, matrixQfromR[i, j].Real, 1e-3f);
+ Assert.AreApproximatelyEqual(matrixA[i, j].Imaginary, matrixQfromR[i, j].Imaginary, 1e-3f);
+ }
+ }
+
+ // Make sure the Q is unitary --> (Q*)x(Q) = I
+ var matrixQсtQ = factorGramSchmidt.Q.ConjugateTranspose() * factorGramSchmidt.Q;
+ for (var i = 0; i < matrixQсtQ.RowCount; i++)
+ {
+ for (var j = 0; j < matrixQсtQ.ColumnCount; j++)
+ {
+ if (i == j)
+ {
+ Assert.AreApproximatelyEqual(matrixQсtQ[i, j].Real, 1.0f, 1e-3f);
+ Assert.AreApproximatelyEqual(matrixQсtQ[i, j].Imaginary, 0.0f, 1e-3f);
+ }
+ else
+ {
+ Assert.AreApproximatelyEqual(matrixQсtQ[i, j].Real, 0.0f, 1e-3f);
+ Assert.AreApproximatelyEqual(matrixQсtQ[i, j].Imaginary, 0.0f, 1e-3f);
+ }
+ }
+ }
+
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(2)]
+ [Row(5)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanSolveForRandomVector(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixACopy = matrixA.Clone();
+ var factorGramSchmidt = matrixA.GramSchmidt();
+
+ var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order);
+ var resultx = factorGramSchmidt.Solve(vectorb);
+
+ Assert.AreEqual(matrixA.ColumnCount, resultx.Count);
+
+ var bReconstruct = matrixA * resultx;
+
+ // Check the reconstruction.
+ for (var i = 0; i < order; i++)
+ {
+ Assert.AreApproximatelyEqual(vectorb[i].Real, bReconstruct[i].Real, 1e-3f);
+ Assert.AreApproximatelyEqual(vectorb[i].Imaginary, bReconstruct[i].Imaginary, 1e-3f);
+ }
+
+ // Make sure A didn't change.
+ for (var i = 0; i < matrixA.RowCount; i++)
+ {
+ for (var j = 0; j < matrixA.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
+ }
+ }
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(4)]
+ [Row(8)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanSolveForRandomMatrix(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixACopy = matrixA.Clone();
+ var factorGramSchmidt = matrixA.GramSchmidt();
+
+ var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixX = factorGramSchmidt.Solve(matrixB);
+
+ // The solution X row dimension is equal to the column dimension of A
+ Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
+ // The solution X has the same number of columns as B
+ Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
+
+ var matrixBReconstruct = matrixA * matrixX;
+
+ // Check the reconstruction.
+ for (var i = 0; i < matrixB.RowCount; i++)
+ {
+ for (var j = 0; j < matrixB.ColumnCount; j++)
+ {
+ Assert.AreApproximatelyEqual(matrixB[i, j].Real, matrixBReconstruct[i, j].Real, 1e-3f);
+ Assert.AreApproximatelyEqual(matrixB[i, j].Imaginary, matrixBReconstruct[i, j].Imaginary, 1e-3f);
+ }
+ }
+
+ // Make sure A didn't change.
+ for (var i = 0; i < matrixA.RowCount; i++)
+ {
+ for (var j = 0; j < matrixA.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
+ }
+ }
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(2)]
+ [Row(5)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanSolveForRandomVectorWhenResultVectorGiven(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixACopy = matrixA.Clone();
+ var factorGramSchmidt = matrixA.GramSchmidt();
+ var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order);
+ var vectorbCopy = vectorb.Clone();
+ var resultx = new UserDefinedVector(order);
+ factorGramSchmidt.Solve(vectorb,resultx);
+
+ Assert.AreEqual(vectorb.Count, resultx.Count);
+
+ var bReconstruct = matrixA * resultx;
+
+ // Check the reconstruction.
+ for (var i = 0; i < vectorb.Count; i++)
+ {
+ Assert.AreApproximatelyEqual(vectorb[i].Real, bReconstruct[i].Real, 1e-3f);
+ Assert.AreApproximatelyEqual(vectorb[i].Imaginary, bReconstruct[i].Imaginary, 1e-3f);
+ }
+
+ // Make sure A didn't change.
+ for (var i = 0; i < matrixA.RowCount; i++)
+ {
+ for (var j = 0; j < matrixA.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
+ }
+ }
+
+ // Make sure b didn't change.
+ for (var i = 0; i < vectorb.Count; i++)
+ {
+ Assert.AreEqual(vectorbCopy[i], vectorb[i]);
+ }
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(4)]
+ [Row(8)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanSolveForRandomMatrixWhenResultMatrixGiven(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixACopy = matrixA.Clone();
+ var factorGramSchmidt = matrixA.GramSchmidt();
+
+ var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixBCopy = matrixB.Clone();
+
+ var matrixX = new UserDefinedMatrix(order, order);
+ factorGramSchmidt.Solve(matrixB,matrixX);
+
+ // The solution X row dimension is equal to the column dimension of A
+ Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
+ // The solution X has the same number of columns as B
+ Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
+
+ var matrixBReconstruct = matrixA * matrixX;
+
+ // Check the reconstruction.
+ for (var i = 0; i < matrixB.RowCount; i++)
+ {
+ for (var j = 0; j < matrixB.ColumnCount; j++)
+ {
+ Assert.AreApproximatelyEqual(matrixB[i, j].Real, matrixBReconstruct[i, j].Real, 1e-3f);
+ Assert.AreApproximatelyEqual(matrixB[i, j].Imaginary, matrixBReconstruct[i, j].Imaginary, 1e-3f);
+ }
+ }
+
+ // Make sure A didn't change.
+ for (var i = 0; i < matrixA.RowCount; i++)
+ {
+ for (var j = 0; j < matrixA.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
+ }
+ }
+
+ // Make sure B didn't change.
+ for (var i = 0; i < matrixB.RowCount; i++)
+ {
+ for (var j = 0; j < matrixB.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixBCopy[i, j], matrixB[i, j]);
+ }
+ }
+ }
+ }
+}
diff --git a/src/UnitTests/LinearAlgebraTests/Double/Factorization/EvdTests.cs b/src/UnitTests/LinearAlgebraTests/Double/Factorization/EvdTests.cs
new file mode 100644
index 00000000..090433e2
--- /dev/null
+++ b/src/UnitTests/LinearAlgebraTests/Double/Factorization/EvdTests.cs
@@ -0,0 +1,358 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+// http://mathnetnumerics.codeplex.com
+//
+// Copyright (c) 2009-2010 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization
+{
+ using System.Numerics;
+ using LinearAlgebra.Double;
+ using LinearAlgebra.Generic.Factorization;
+ using MbUnit.Framework;
+ using LinearAlgebra.Double.Factorization;
+
+ public class EvdTests
+ {
+
+ [Test]
+ [ExpectedArgumentNullException]
+ public void ConstructorNull()
+ {
+ new DenseEvd(null);
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(10)]
+ [Row(100)]
+ public void CanFactorizeIdentity(int order)
+ {
+ var I = DenseMatrix.Identity(order);
+ var factorEvd = I.Evd();
+
+ Assert.AreEqual(I.RowCount, factorEvd.EVectors().RowCount);
+ Assert.AreEqual(I.RowCount, factorEvd.EVectors().ColumnCount);
+
+ Assert.AreEqual(I.ColumnCount, factorEvd.D().RowCount);
+ Assert.AreEqual(I.ColumnCount, factorEvd.D().ColumnCount);
+
+ for (var i = 0; i < factorEvd.EValues().Count; i++)
+ {
+ Assert.AreEqual(Complex.One, factorEvd.EValues()[i]);
+ }
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(2)]
+ [Row(5)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanFactorizeRandomMatrix(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
+ var factorEvd = matrixA.Evd();
+
+ Assert.AreEqual(order, factorEvd.EVectors().RowCount);
+ Assert.AreEqual(order, factorEvd.EVectors().ColumnCount);
+
+ Assert.AreEqual(order, factorEvd.D().RowCount);
+ Assert.AreEqual(order, factorEvd.D().ColumnCount);
+
+ // Make sure the A*V = λ*V
+ var matrixAv = matrixA * factorEvd.EVectors();
+ var matrixLv = factorEvd.EVectors() * factorEvd.D();
+
+ for (var i = 0; i < matrixAv.RowCount; i++)
+ {
+ for (var j = 0; j < matrixAv.ColumnCount; j++)
+ {
+ Assert.AreApproximatelyEqual(matrixAv[i, j], matrixLv[i, j], 1.0e-10);
+ }
+ }
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(2)]
+ [Row(5)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanFactorizeRandomSymmetricMatrix(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteDenseMatrix(order);
+ var factorEvd = matrixA.Evd();
+
+ Assert.AreEqual(order, factorEvd.EVectors().RowCount);
+ Assert.AreEqual(order, factorEvd.EVectors().ColumnCount);
+
+ Assert.AreEqual(order, factorEvd.D().RowCount);
+ Assert.AreEqual(order, factorEvd.D().ColumnCount);
+
+ // Make sure the A = V*λ*VT
+ var matrix = factorEvd.EVectors() * factorEvd.D() * factorEvd.EVectors().Transpose();
+
+ for (var i = 0; i < matrix.RowCount; i++)
+ {
+ for (var j = 0; j < matrix.ColumnCount; j++)
+ {
+ Assert.AreApproximatelyEqual(matrix[i, j], matrixA[i, j], 1.0e-10);
+ }
+ }
+ }
+
+ [Test]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CheckRankSquare(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
+ var factorEvd = matrixA.Evd();
+
+ Assert.AreEqual(factorEvd.Rank, order);
+ }
+
+
+ [Test]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CheckRankOfSquareSingular(int order)
+ {
+ var matrixA = new DenseMatrix(order, order);
+ matrixA[0, 0] = 1;
+ matrixA[order - 1, order - 1] = 1;
+ for (var i = 1; i < order - 1; i++)
+ {
+ matrixA[i, i - 1] = 1;
+ matrixA[i, i + 1] = 1;
+ matrixA[i - 1, i] = 1;
+ matrixA[i + 1, i] = 1;
+ }
+ var factorEvd = matrixA.Evd();
+
+ Assert.AreEqual(factorEvd.Determinant, 0);
+ Assert.AreEqual(factorEvd.Rank, order - 1);
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(10)]
+ [Row(100)]
+ public void IdentityDeterminantIsOne(int order)
+ {
+ var I = DenseMatrix.Identity(order);
+ var factorEvd = I.Evd();
+ Assert.AreEqual(1.0, factorEvd.Determinant);
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(2)]
+ [Row(5)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanSolveForRandomVectorAndSymmetricMatrix(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteDenseMatrix(order);
+ var matrixACopy = matrixA.Clone();
+ var factorEvd = matrixA.Evd();
+
+ var vectorb = MatrixLoader.GenerateRandomDenseVector(order);
+ var resultx = factorEvd.Solve(vectorb);
+
+ Assert.AreEqual(matrixA.ColumnCount, resultx.Count);
+
+ var bReconstruct = matrixA * resultx;
+
+ // Check the reconstruction.
+ for (var i = 0; i < vectorb.Count; i++)
+ {
+ Assert.AreApproximatelyEqual(vectorb[i], bReconstruct[i], 1.0e-10);
+ }
+
+ // Make sure A didn't change.
+ for (var i = 0; i < matrixA.RowCount; i++)
+ {
+ for (var j = 0; j < matrixA.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
+ }
+ }
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(2)]
+ [Row(5)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanSolveForRandomMatrixAndSymmetricMatrix(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteDenseMatrix(order);
+ var matrixACopy = matrixA.Clone();
+ var factorEvd = matrixA.Evd();
+
+ var matrixB = MatrixLoader.GenerateRandomDenseMatrix(order, order);
+ var matrixX = factorEvd.Solve(matrixB);
+
+ // The solution X row dimension is equal to the column dimension of A
+ Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
+ // The solution X has the same number of columns as B
+ Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
+
+ var matrixBReconstruct = matrixA * matrixX;
+
+ // Check the reconstruction.
+ for (var i = 0; i < matrixB.RowCount; i++)
+ {
+ for (var j = 0; j < matrixB.ColumnCount; j++)
+ {
+ Assert.AreApproximatelyEqual(matrixB[i, j], matrixBReconstruct[i, j], 1.0e-10);
+ }
+ }
+
+ // Make sure A didn't change.
+ for (var i = 0; i < matrixA.RowCount; i++)
+ {
+ for (var j = 0; j < matrixA.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
+ }
+ }
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(2)]
+ [Row(5)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanSolveForRandomVectorAndSymmetricMatrixWhenResultVectorGiven(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteDenseMatrix(order);
+ var matrixACopy = matrixA.Clone();
+ var factorEvd = matrixA.Evd();
+ var vectorb = MatrixLoader.GenerateRandomDenseVector(order);
+ var vectorbCopy = vectorb.Clone();
+ var resultx = new DenseVector(order);
+ factorEvd.Solve(vectorb, resultx);
+
+ var bReconstruct = matrixA * resultx;
+
+ // Check the reconstruction.
+ for (var i = 0; i < vectorb.Count; i++)
+ {
+ Assert.AreApproximatelyEqual(vectorb[i], bReconstruct[i], 1.0e-10);
+ }
+
+ // Make sure A didn't change.
+ for (var i = 0; i < matrixA.RowCount; i++)
+ {
+ for (var j = 0; j < matrixA.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
+ }
+ }
+
+ // Make sure b didn't change.
+ for (var i = 0; i < vectorb.Count; i++)
+ {
+ Assert.AreEqual(vectorbCopy[i], vectorb[i]);
+ }
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(2)]
+ [Row(5)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanSolveForRandomMatrixAndSymmetricMatrixWhenResultMatrixGiven(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteDenseMatrix(order);
+ var matrixACopy = matrixA.Clone();
+ var factorEvd = matrixA.Evd();
+
+ var matrixB = MatrixLoader.GenerateRandomDenseMatrix(order, order);
+ var matrixBCopy = matrixB.Clone();
+
+ var matrixX = new DenseMatrix(order, order);
+ factorEvd.Solve(matrixB, matrixX);
+
+ // The solution X row dimension is equal to the column dimension of A
+ Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
+ // The solution X has the same number of columns as B
+ Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
+
+ var matrixBReconstruct = matrixA * matrixX;
+
+ // Check the reconstruction.
+ for (var i = 0; i < matrixB.RowCount; i++)
+ {
+ for (var j = 0; j < matrixB.ColumnCount; j++)
+ {
+ Assert.AreApproximatelyEqual(matrixB[i, j], matrixBReconstruct[i, j], 1.0e-10);
+ }
+ }
+
+ // Make sure A didn't change.
+ for (var i = 0; i < matrixA.RowCount; i++)
+ {
+ for (var j = 0; j < matrixA.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
+ }
+ }
+
+ // Make sure B didn't change.
+ for (var i = 0; i < matrixB.RowCount; i++)
+ {
+ for (var j = 0; j < matrixB.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixBCopy[i, j], matrixB[i, j]);
+ }
+ }
+ }
+ }
+}
diff --git a/src/UnitTests/LinearAlgebraTests/Double/Factorization/GramSchmidtTests.cs b/src/UnitTests/LinearAlgebraTests/Double/Factorization/GramSchmidtTests.cs
index 87932471..81d5273e 100644
--- a/src/UnitTests/LinearAlgebraTests/Double/Factorization/GramSchmidtTests.cs
+++ b/src/UnitTests/LinearAlgebraTests/Double/Factorization/GramSchmidtTests.cs
@@ -30,6 +30,7 @@
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization
{
+ using LinearAlgebra.Double;
using LinearAlgebra.Generic.Factorization;
using MbUnit.Framework;
using LinearAlgebra.Double.Factorization;
@@ -40,14 +41,14 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization
[ExpectedArgumentNullException]
public void ConstructorNull()
{
- new GramSchmidt(null);
+ new DenseGramSchmidt(null);
}
[Test]
[ExpectedArgumentException]
public void WideMatrixThrowsInvalidMatrixOperationException()
{
- new GramSchmidt(new UserDefinedMatrix(3, 4));
+ new DenseGramSchmidt(new DenseMatrix(3, 4));
}
[Test]
@@ -56,7 +57,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization
[Row(100)]
public void CanFactorizeIdentity(int order)
{
- var I = UserDefinedMatrix.Identity(order);
+ var I = DenseMatrix.Identity(order);
var factorGramSchmidt = I.GramSchmidt();
Assert.AreEqual(I.RowCount, factorGramSchmidt.Q.RowCount);
@@ -100,7 +101,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization
[Row(100)]
public void IdentityDeterminantIsOne(int order)
{
- var I = UserDefinedMatrix.Identity(order);
+ var I = DenseMatrix.Identity(order);
var factorGramSchmidt = I.GramSchmidt();
Assert.AreEqual(1.0, factorGramSchmidt.Determinant);
}
@@ -115,7 +116,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization
[MultipleAsserts]
public void CanFactorizeRandomMatrix(int row, int column)
{
- var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(row, column);
+ var matrixA = MatrixLoader.GenerateRandomDenseMatrix(row, column);
var factorGramSchmidt = matrixA.GramSchmidt();
// Make sure the Q has the right dimensions.
@@ -159,11 +160,11 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization
[MultipleAsserts]
public void CanSolveForRandomVector(int order)
{
- var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorGramSchmidt = matrixA.GramSchmidt();
- var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order);
+ var vectorb = MatrixLoader.GenerateRandomDenseVector(order);
var resultx = factorGramSchmidt.Solve(vectorb);
Assert.AreEqual(matrixA.ColumnCount, resultx.Count);
@@ -196,11 +197,11 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization
[MultipleAsserts]
public void CanSolveForRandomMatrix(int order)
{
- var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorGramSchmidt = matrixA.GramSchmidt();
- var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixB = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixX = factorGramSchmidt.Solve(matrixB);
// The solution X row dimension is equal to the column dimension of A
@@ -239,12 +240,12 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization
[MultipleAsserts]
public void CanSolveForRandomVectorWhenResultVectorGiven(int order)
{
- var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorGramSchmidt = matrixA.GramSchmidt();
- var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order);
+ var vectorb = MatrixLoader.GenerateRandomDenseVector(order);
var vectorbCopy = vectorb.Clone();
- var resultx = new UserDefinedVector(order);
+ var resultx = new DenseVector(order);
factorGramSchmidt.Solve(vectorb,resultx);
Assert.AreEqual(vectorb.Count, resultx.Count);
@@ -283,14 +284,14 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization
[MultipleAsserts]
public void CanSolveForRandomMatrixWhenResultMatrixGiven(int order)
{
- var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorGramSchmidt = matrixA.GramSchmidt();
- var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixB = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixBCopy = matrixB.Clone();
- var matrixX = new UserDefinedMatrix(order, order);
+ var matrixX = new DenseMatrix(order, order);
factorGramSchmidt.Solve(matrixB,matrixX);
// The solution X row dimension is equal to the column dimension of A
diff --git a/src/UnitTests/LinearAlgebraTests/Double/Factorization/UserEvdTests.cs b/src/UnitTests/LinearAlgebraTests/Double/Factorization/UserEvdTests.cs
index 43f13acf..82b6686a 100644
--- a/src/UnitTests/LinearAlgebraTests/Double/Factorization/UserEvdTests.cs
+++ b/src/UnitTests/LinearAlgebraTests/Double/Factorization/UserEvdTests.cs
@@ -93,7 +93,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization
{
for (var j = 0; j < matrixAv.ColumnCount; j++)
{
- Assert.AreApproximatelyEqual(matrixAv[i, j], matrixLv[i, j], 1.0e-11);
+ Assert.AreApproximatelyEqual(matrixAv[i, j], matrixLv[i, j], 1.0e-10);
}
}
}
@@ -124,7 +124,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization
{
for (var j = 0; j < matrix.ColumnCount; j++)
{
- Assert.AreApproximatelyEqual(matrix[i, j], matrixA[i, j], 1.0e-11);
+ Assert.AreApproximatelyEqual(matrix[i, j], matrixA[i, j], 1.0e-10);
}
}
}
@@ -189,10 +189,10 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization
{
var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteUserDefinedMatrix(order);
var matrixACopy = matrixA.Clone();
- var factorSvd = matrixA.Svd(true);
+ var factorEvd = matrixA.Evd();
var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order);
- var resultx = factorSvd.Solve(vectorb);
+ var resultx = factorEvd.Solve(vectorb);
Assert.AreEqual(matrixA.ColumnCount, resultx.Count);
@@ -201,7 +201,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization
// Check the reconstruction.
for (var i = 0; i < vectorb.Count; i++)
{
- Assert.AreApproximatelyEqual(vectorb[i], bReconstruct[i], 1.0e-11);
+ Assert.AreApproximatelyEqual(vectorb[i], bReconstruct[i], 1.0e-10);
}
// Make sure A didn't change.
@@ -226,10 +226,10 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization
{
var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteUserDefinedMatrix(order);
var matrixACopy = matrixA.Clone();
- var factorSvd = matrixA.Svd(true);
+ var factorEvd = matrixA.Evd();
var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
- var matrixX = factorSvd.Solve(matrixB);
+ var matrixX = factorEvd.Solve(matrixB);
// The solution X row dimension is equal to the column dimension of A
Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
@@ -243,7 +243,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization
{
for (var j = 0; j < matrixB.ColumnCount; j++)
{
- Assert.AreApproximatelyEqual(matrixB[i, j], matrixBReconstruct[i, j], 1.0e-11);
+ Assert.AreApproximatelyEqual(matrixB[i, j], matrixBReconstruct[i, j], 1.0e-10);
}
}
@@ -269,18 +269,18 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization
{
var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteUserDefinedMatrix(order);
var matrixACopy = matrixA.Clone();
- var factorSvd = matrixA.Svd(true);
+ var factorEvd = matrixA.Evd();
var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order);
var vectorbCopy = vectorb.Clone();
var resultx = new UserDefinedVector(order);
- factorSvd.Solve(vectorb, resultx);
+ factorEvd.Solve(vectorb, resultx);
var bReconstruct = matrixA * resultx;
// Check the reconstruction.
for (var i = 0; i < vectorb.Count; i++)
{
- Assert.AreApproximatelyEqual(vectorb[i], bReconstruct[i], 1.0e-11);
+ Assert.AreApproximatelyEqual(vectorb[i], bReconstruct[i], 1.0e-10);
}
// Make sure A didn't change.
@@ -311,13 +311,13 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization
{
var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteUserDefinedMatrix(order);
var matrixACopy = matrixA.Clone();
- var factorSvd = matrixA.Svd(true);
+ var factorEvd = matrixA.Evd();
var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
var matrixBCopy = matrixB.Clone();
var matrixX = new UserDefinedMatrix(order, order);
- factorSvd.Solve(matrixB, matrixX);
+ factorEvd.Solve(matrixB, matrixX);
// The solution X row dimension is equal to the column dimension of A
Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
@@ -331,7 +331,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization
{
for (var j = 0; j < matrixB.ColumnCount; j++)
{
- Assert.AreApproximatelyEqual(matrixB[i, j], matrixBReconstruct[i, j], 1.0e-11);
+ Assert.AreApproximatelyEqual(matrixB[i, j], matrixBReconstruct[i, j], 1.0e-10);
}
}
diff --git a/src/UnitTests/LinearAlgebraTests/Double/Factorization/UserGramSchmidtTests.cs b/src/UnitTests/LinearAlgebraTests/Double/Factorization/UserGramSchmidtTests.cs
new file mode 100644
index 00000000..b300b664
--- /dev/null
+++ b/src/UnitTests/LinearAlgebraTests/Double/Factorization/UserGramSchmidtTests.cs
@@ -0,0 +1,331 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+// http://mathnetnumerics.codeplex.com
+//
+// Copyright (c) 2009-2010 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization
+{
+ using LinearAlgebra.Generic.Factorization;
+ using MbUnit.Framework;
+ using LinearAlgebra.Double.Factorization;
+
+ public class UserGramSchmidtTests
+ {
+ [Test]
+ [ExpectedArgumentNullException]
+ public void ConstructorNull()
+ {
+ new UserGramSchmidt(null);
+ }
+
+ [Test]
+ [ExpectedArgumentException]
+ public void WideMatrixThrowsInvalidMatrixOperationException()
+ {
+ new UserGramSchmidt(new UserDefinedMatrix(3, 4));
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(10)]
+ [Row(100)]
+ public void CanFactorizeIdentity(int order)
+ {
+ var I = UserDefinedMatrix.Identity(order);
+ var factorGramSchmidt = I.GramSchmidt();
+
+ Assert.AreEqual(I.RowCount, factorGramSchmidt.Q.RowCount);
+ Assert.AreEqual(I.ColumnCount, factorGramSchmidt.Q.ColumnCount);
+
+ for (var i = 0; i < factorGramSchmidt.R.RowCount; i++)
+ {
+ for (var j = 0; j < factorGramSchmidt.R.ColumnCount; j++)
+ {
+ if (i == j)
+ {
+ Assert.AreEqual(1.0, factorGramSchmidt.R[i, j]);
+ }
+ else
+ {
+ Assert.AreEqual(0.0, factorGramSchmidt.R[i, j]);
+ }
+ }
+ }
+
+ for (var i = 0; i < factorGramSchmidt.Q.RowCount; i++)
+ {
+ for (var j = 0; j < factorGramSchmidt.Q.ColumnCount; j++)
+ {
+ if (i == j)
+ {
+ Assert.AreEqual(1.0, factorGramSchmidt.Q[i, j]);
+ }
+ else
+ {
+ Assert.AreEqual(0.0, factorGramSchmidt.Q[i, j]);
+ }
+ }
+ }
+ }
+
+
+ [Test]
+ [Row(1)]
+ [Row(10)]
+ [Row(100)]
+ public void IdentityDeterminantIsOne(int order)
+ {
+ var I = UserDefinedMatrix.Identity(order);
+ var factorGramSchmidt = I.GramSchmidt();
+ Assert.AreEqual(1.0, factorGramSchmidt.Determinant);
+ }
+
+ [Test]
+ [Row(1,1)]
+ [Row(2,2)]
+ [Row(5,5)]
+ [Row(10,6)]
+ [Row(50,48)]
+ [Row(100,98)]
+ [MultipleAsserts]
+ public void CanFactorizeRandomMatrix(int row, int column)
+ {
+ var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(row, column);
+ var factorGramSchmidt = matrixA.GramSchmidt();
+
+ // Make sure the Q has the right dimensions.
+ Assert.AreEqual(row, factorGramSchmidt.Q.RowCount);
+ Assert.AreEqual(column, factorGramSchmidt.Q.ColumnCount);
+
+ // Make sure the R has the right dimensions.
+ Assert.AreEqual(column, factorGramSchmidt.R.RowCount);
+ Assert.AreEqual(column, factorGramSchmidt.R.ColumnCount);
+
+ // Make sure the R factor is upper triangular.
+ for (var i = 0; i < factorGramSchmidt.R.RowCount; i++)
+ {
+ for (var j = 0; j < factorGramSchmidt.R.ColumnCount; j++)
+ {
+ if (i > j)
+ {
+ Assert.AreEqual(0.0, factorGramSchmidt.R[i, j]);
+ }
+ }
+ }
+
+ // Make sure the Q*R is the original matrix.
+ var matrixQfromR = factorGramSchmidt.Q * factorGramSchmidt.R;
+ for (var i = 0; i < matrixQfromR.RowCount; i++)
+ {
+ for (var j = 0; j < matrixQfromR.ColumnCount; j++)
+ {
+ Assert.AreApproximatelyEqual(matrixA[i, j], matrixQfromR[i, j], 1.0e-11);
+ }
+ }
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(2)]
+ [Row(5)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanSolveForRandomVector(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixACopy = matrixA.Clone();
+ var factorGramSchmidt = matrixA.GramSchmidt();
+
+ var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order);
+ var resultx = factorGramSchmidt.Solve(vectorb);
+
+ Assert.AreEqual(matrixA.ColumnCount, resultx.Count);
+
+ var bReconstruct = matrixA * resultx;
+
+ // Check the reconstruction.
+ for (var i = 0; i < order; i++)
+ {
+ Assert.AreApproximatelyEqual(vectorb[i], bReconstruct[i], 1.0e-11);
+ }
+
+ // Make sure A didn't change.
+ for (var i = 0; i < matrixA.RowCount; i++)
+ {
+ for (var j = 0; j < matrixA.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
+ }
+ }
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(4)]
+ [Row(8)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanSolveForRandomMatrix(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixACopy = matrixA.Clone();
+ var factorGramSchmidt = matrixA.GramSchmidt();
+
+ var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixX = factorGramSchmidt.Solve(matrixB);
+
+ // The solution X row dimension is equal to the column dimension of A
+ Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
+ // The solution X has the same number of columns as B
+ Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
+
+ var matrixBReconstruct = matrixA * matrixX;
+
+ // Check the reconstruction.
+ for (var i = 0; i < matrixB.RowCount; i++)
+ {
+ for (var j = 0; j < matrixB.ColumnCount; j++)
+ {
+ Assert.AreApproximatelyEqual(matrixB[i, j], matrixBReconstruct[i, j], 1.0e-11);
+ }
+ }
+
+ // Make sure A didn't change.
+ for (var i = 0; i < matrixA.RowCount; i++)
+ {
+ for (var j = 0; j < matrixA.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
+ }
+ }
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(2)]
+ [Row(5)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanSolveForRandomVectorWhenResultVectorGiven(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixACopy = matrixA.Clone();
+ var factorGramSchmidt = matrixA.GramSchmidt();
+ var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order);
+ var vectorbCopy = vectorb.Clone();
+ var resultx = new UserDefinedVector(order);
+ factorGramSchmidt.Solve(vectorb,resultx);
+
+ Assert.AreEqual(vectorb.Count, resultx.Count);
+
+ var bReconstruct = matrixA * resultx;
+
+ // Check the reconstruction.
+ for (var i = 0; i < vectorb.Count; i++)
+ {
+ Assert.AreApproximatelyEqual(vectorb[i], bReconstruct[i], 1.0e-11);
+ }
+
+ // Make sure A didn't change.
+ for (var i = 0; i < matrixA.RowCount; i++)
+ {
+ for (var j = 0; j < matrixA.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
+ }
+ }
+
+ // Make sure b didn't change.
+ for (var i = 0; i < vectorb.Count; i++)
+ {
+ Assert.AreEqual(vectorbCopy[i], vectorb[i]);
+ }
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(4)]
+ [Row(8)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanSolveForRandomMatrixWhenResultMatrixGiven(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixACopy = matrixA.Clone();
+ var factorGramSchmidt = matrixA.GramSchmidt();
+
+ var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixBCopy = matrixB.Clone();
+
+ var matrixX = new UserDefinedMatrix(order, order);
+ factorGramSchmidt.Solve(matrixB,matrixX);
+
+ // The solution X row dimension is equal to the column dimension of A
+ Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
+ // The solution X has the same number of columns as B
+ Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
+
+ var matrixBReconstruct = matrixA * matrixX;
+
+ // Check the reconstruction.
+ for (var i = 0; i < matrixB.RowCount; i++)
+ {
+ for (var j = 0; j < matrixB.ColumnCount; j++)
+ {
+ Assert.AreApproximatelyEqual(matrixB[i, j], matrixBReconstruct[i, j], 1.0e-11);
+ }
+ }
+
+ // Make sure A didn't change.
+ for (var i = 0; i < matrixA.RowCount; i++)
+ {
+ for (var j = 0; j < matrixA.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
+ }
+ }
+
+ // Make sure B didn't change.
+ for (var i = 0; i < matrixB.RowCount; i++)
+ {
+ for (var j = 0; j < matrixB.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixBCopy[i, j], matrixB[i, j]);
+ }
+ }
+ }
+ }
+}
diff --git a/src/UnitTests/LinearAlgebraTests/Single/Factorization/EvdTests.cs b/src/UnitTests/LinearAlgebraTests/Single/Factorization/EvdTests.cs
new file mode 100644
index 00000000..9d3fde4e
--- /dev/null
+++ b/src/UnitTests/LinearAlgebraTests/Single/Factorization/EvdTests.cs
@@ -0,0 +1,359 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+// http://mathnetnumerics.codeplex.com
+//
+// Copyright (c) 2009-2010 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Factorization
+{
+ using System.Numerics;
+ using LinearAlgebra.Generic.Factorization;
+ using LinearAlgebra.Single;
+ using MbUnit.Framework;
+ using LinearAlgebra.Single.Factorization;
+
+ public class EvdTests
+ {
+
+ [Test]
+ [ExpectedArgumentNullException]
+ public void ConstructorNull()
+ {
+ new DenseEvd(null);
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(10)]
+ [Row(100)]
+ public void CanFactorizeIdentity(int order)
+ {
+ var I = DenseMatrix.Identity(order);
+ var factorEvd = I.Evd();
+
+ Assert.AreEqual(I.RowCount, factorEvd.EVectors().RowCount);
+ Assert.AreEqual(I.RowCount, factorEvd.EVectors().ColumnCount);
+
+ Assert.AreEqual(I.ColumnCount, factorEvd.D().RowCount);
+ Assert.AreEqual(I.ColumnCount, factorEvd.D().ColumnCount);
+
+ for (var i = 0; i < factorEvd.EValues().Count; i++)
+ {
+ Assert.AreEqual(Complex.One, factorEvd.EValues()[i]);
+ }
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(2)]
+ [Row(5)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanFactorizeRandomMatrix(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
+ var factorEvd = matrixA.Evd();
+
+ Assert.AreEqual(order, factorEvd.EVectors().RowCount);
+ Assert.AreEqual(order, factorEvd.EVectors().ColumnCount);
+
+ Assert.AreEqual(order, factorEvd.D().RowCount);
+ Assert.AreEqual(order, factorEvd.D().ColumnCount);
+
+ // Make sure the A*V = λ*V
+ var matrixAv = matrixA * factorEvd.EVectors();
+ var matrixLv = factorEvd.EVectors() * factorEvd.D();
+
+ for (var i = 0; i < matrixAv.RowCount; i++)
+ {
+ for (var j = 0; j < matrixAv.ColumnCount; j++)
+ {
+ Assert.AreApproximatelyEqual(matrixAv[i, j], matrixLv[i, j], 1e-3f);
+ }
+ }
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(2)]
+ [Row(5)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [Ignore]
+ [MultipleAsserts]
+ public void CanFactorizeRandomSymmetricMatrix(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteDenseMatrix(order);
+ var factorEvd = matrixA.Evd();
+
+ Assert.AreEqual(order, factorEvd.EVectors().RowCount);
+ Assert.AreEqual(order, factorEvd.EVectors().ColumnCount);
+
+ Assert.AreEqual(order, factorEvd.D().RowCount);
+ Assert.AreEqual(order, factorEvd.D().ColumnCount);
+
+ // Make sure the A = V*λ*VT
+ var matrix = factorEvd.EVectors() * factorEvd.D() * factorEvd.EVectors().Transpose();
+
+ for (var i = 0; i < matrix.RowCount; i++)
+ {
+ for (var j = 0; j < matrix.ColumnCount; j++)
+ {
+ Assert.AreApproximatelyEqual(matrix[i, j], matrixA[i, j], 1e-3f);
+ }
+ }
+ }
+
+ [Test]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CheckRankSquare(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
+ var factorEvd = matrixA.Evd();
+
+ Assert.AreEqual(factorEvd.Rank, order);
+ }
+
+
+ [Test]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CheckRankOfSquareSingular(int order)
+ {
+ var matrixA = new DenseMatrix(order, order);
+ matrixA[0, 0] = 1;
+ matrixA[order - 1, order - 1] = 1;
+ for (var i = 1; i < order - 1; i++)
+ {
+ matrixA[i, i - 1] = 1;
+ matrixA[i, i + 1] = 1;
+ matrixA[i - 1, i] = 1;
+ matrixA[i + 1, i] = 1;
+ }
+ var factorEvd = matrixA.Evd();
+
+ Assert.AreEqual(factorEvd.Determinant, 0);
+ Assert.AreEqual(factorEvd.Rank, order - 1);
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(10)]
+ [Row(100)]
+ public void IdentityDeterminantIsOne(int order)
+ {
+ var I = DenseMatrix.Identity(order);
+ var factorEvd = I.Evd();
+ Assert.AreEqual(1.0, factorEvd.Determinant);
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(2)]
+ [Row(5)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanSolveForRandomVectorAndSymmetricMatrix(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteDenseMatrix(order);
+ var matrixACopy = matrixA.Clone();
+ var factorEvd = matrixA.Evd();
+
+ var vectorb = MatrixLoader.GenerateRandomDenseVector(order);
+ var resultx = factorEvd.Solve(vectorb);
+
+ Assert.AreEqual(matrixA.ColumnCount, resultx.Count);
+
+ var bReconstruct = matrixA * resultx;
+
+ // Check the reconstruction.
+ for (var i = 0; i < vectorb.Count; i++)
+ {
+ Assert.AreApproximatelyEqual(vectorb[i], bReconstruct[i], 1e-3f);
+ }
+
+ // Make sure A didn't change.
+ for (var i = 0; i < matrixA.RowCount; i++)
+ {
+ for (var j = 0; j < matrixA.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
+ }
+ }
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(2)]
+ [Row(5)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanSolveForRandomMatrixAndSymmetricMatrix(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteDenseMatrix(order);
+ var matrixACopy = matrixA.Clone();
+ var factorEvd = matrixA.Evd();
+
+ var matrixB = MatrixLoader.GenerateRandomDenseMatrix(order, order);
+ var matrixX = factorEvd.Solve(matrixB);
+
+ // The solution X row dimension is equal to the column dimension of A
+ Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
+ // The solution X has the same number of columns as B
+ Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
+
+ var matrixBReconstruct = matrixA * matrixX;
+
+ // Check the reconstruction.
+ for (var i = 0; i < matrixB.RowCount; i++)
+ {
+ for (var j = 0; j < matrixB.ColumnCount; j++)
+ {
+ Assert.AreApproximatelyEqual(matrixB[i, j], matrixBReconstruct[i, j], 1e-2f);
+ }
+ }
+
+ // Make sure A didn't change.
+ for (var i = 0; i < matrixA.RowCount; i++)
+ {
+ for (var j = 0; j < matrixA.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
+ }
+ }
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(2)]
+ [Row(5)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanSolveForRandomVectorAndSymmetricMatrixWhenResultVectorGiven(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteDenseMatrix(order);
+ var matrixACopy = matrixA.Clone();
+ var factorEvd = matrixA.Evd();
+ var vectorb = MatrixLoader.GenerateRandomDenseVector(order);
+ var vectorbCopy = vectorb.Clone();
+ var resultx = new DenseVector(order);
+ factorEvd.Solve(vectorb, resultx);
+
+ var bReconstruct = matrixA * resultx;
+
+ // Check the reconstruction.
+ for (var i = 0; i < vectorb.Count; i++)
+ {
+ Assert.AreApproximatelyEqual(vectorb[i], bReconstruct[i], 1e-3f);
+ }
+
+ // Make sure A didn't change.
+ for (var i = 0; i < matrixA.RowCount; i++)
+ {
+ for (var j = 0; j < matrixA.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
+ }
+ }
+
+ // Make sure b didn't change.
+ for (var i = 0; i < vectorb.Count; i++)
+ {
+ Assert.AreEqual(vectorbCopy[i], vectorb[i]);
+ }
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(2)]
+ [Row(5)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanSolveForRandomMatrixAndSymmetricMatrixWhenResultMatrixGiven(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteDenseMatrix(order);
+ var matrixACopy = matrixA.Clone();
+ var factorEvd = matrixA.Evd();
+
+ var matrixB = MatrixLoader.GenerateRandomDenseMatrix(order, order);
+ var matrixBCopy = matrixB.Clone();
+
+ var matrixX = new DenseMatrix(order, order);
+ factorEvd.Solve(matrixB, matrixX);
+
+ // The solution X row dimension is equal to the column dimension of A
+ Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
+ // The solution X has the same number of columns as B
+ Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
+
+ var matrixBReconstruct = matrixA * matrixX;
+
+ // Check the reconstruction.
+ for (var i = 0; i < matrixB.RowCount; i++)
+ {
+ for (var j = 0; j < matrixB.ColumnCount; j++)
+ {
+ Assert.AreApproximatelyEqual(matrixB[i, j], matrixBReconstruct[i, j], 1e-2f);
+ }
+ }
+
+ // Make sure A didn't change.
+ for (var i = 0; i < matrixA.RowCount; i++)
+ {
+ for (var j = 0; j < matrixA.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
+ }
+ }
+
+ // Make sure B didn't change.
+ for (var i = 0; i < matrixB.RowCount; i++)
+ {
+ for (var j = 0; j < matrixB.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixBCopy[i, j], matrixB[i, j]);
+ }
+ }
+ }
+ }
+}
diff --git a/src/UnitTests/LinearAlgebraTests/Single/Factorization/GramSchmidtTests.cs b/src/UnitTests/LinearAlgebraTests/Single/Factorization/GramSchmidtTests.cs
index 608f1954..04d394d4 100644
--- a/src/UnitTests/LinearAlgebraTests/Single/Factorization/GramSchmidtTests.cs
+++ b/src/UnitTests/LinearAlgebraTests/Single/Factorization/GramSchmidtTests.cs
@@ -31,6 +31,7 @@
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Factorization
{
using LinearAlgebra.Generic.Factorization;
+ using LinearAlgebra.Single;
using MbUnit.Framework;
using LinearAlgebra.Single.Factorization;
@@ -40,14 +41,14 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Factorization
[ExpectedArgumentNullException]
public void ConstructorNull()
{
- new GramSchmidt(null);
+ new DenseGramSchmidt(null);
}
[Test]
[ExpectedArgumentException]
public void WideMatrixThrowsInvalidMatrixOperationException()
{
- new GramSchmidt(new UserDefinedMatrix(3, 4));
+ new DenseGramSchmidt(new DenseMatrix(3, 4));
}
[Test]
@@ -56,7 +57,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Factorization
[Row(100)]
public void CanFactorizeIdentity(int order)
{
- var I = UserDefinedMatrix.Identity(order);
+ var I = DenseMatrix.Identity(order);
var factorGramSchmidt = I.GramSchmidt();
Assert.AreEqual(I.RowCount, factorGramSchmidt.Q.RowCount);
@@ -100,7 +101,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Factorization
[Row(100)]
public void IdentityDeterminantIsOne(int order)
{
- var I = UserDefinedMatrix.Identity(order);
+ var I = DenseMatrix.Identity(order);
var factorGramSchmidt = I.GramSchmidt();
Assert.AreEqual(1.0, factorGramSchmidt.Determinant);
}
@@ -115,7 +116,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Factorization
[MultipleAsserts]
public void CanFactorizeRandomMatrix(int row, int column)
{
- var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(row, column);
+ var matrixA = MatrixLoader.GenerateRandomDenseMatrix(row, column);
var factorGramSchmidt = matrixA.GramSchmidt();
// Make sure the Q has the right dimensions.
@@ -159,11 +160,11 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Factorization
[MultipleAsserts]
public void CanSolveForRandomVector(int order)
{
- var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorGramSchmidt = matrixA.GramSchmidt();
- var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order);
+ var vectorb = MatrixLoader.GenerateRandomDenseVector(order);
var resultx = factorGramSchmidt.Solve(vectorb);
Assert.AreEqual(matrixA.ColumnCount, resultx.Count);
@@ -196,11 +197,11 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Factorization
[MultipleAsserts]
public void CanSolveForRandomMatrix(int order)
{
- var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorGramSchmidt = matrixA.GramSchmidt();
- var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixB = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixX = factorGramSchmidt.Solve(matrixB);
// The solution X row dimension is equal to the column dimension of A
@@ -239,12 +240,12 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Factorization
[MultipleAsserts]
public void CanSolveForRandomVectorWhenResultVectorGiven(int order)
{
- var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorGramSchmidt = matrixA.GramSchmidt();
- var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order);
+ var vectorb = MatrixLoader.GenerateRandomDenseVector(order);
var vectorbCopy = vectorb.Clone();
- var resultx = new UserDefinedVector(order);
+ var resultx = new DenseVector(order);
factorGramSchmidt.Solve(vectorb,resultx);
Assert.AreEqual(vectorb.Count, resultx.Count);
@@ -283,14 +284,14 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Factorization
[MultipleAsserts]
public void CanSolveForRandomMatrixWhenResultMatrixGiven(int order)
{
- var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixA = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixACopy = matrixA.Clone();
var factorGramSchmidt = matrixA.GramSchmidt();
- var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixB = MatrixLoader.GenerateRandomDenseMatrix(order, order);
var matrixBCopy = matrixB.Clone();
- var matrixX = new UserDefinedMatrix(order, order);
+ var matrixX = new DenseMatrix(order, order);
factorGramSchmidt.Solve(matrixB,matrixX);
// The solution X row dimension is equal to the column dimension of A
diff --git a/src/UnitTests/LinearAlgebraTests/Single/Factorization/UserEvdTests.cs b/src/UnitTests/LinearAlgebraTests/Single/Factorization/UserEvdTests.cs
index 78936d6b..e37f6bb8 100644
--- a/src/UnitTests/LinearAlgebraTests/Single/Factorization/UserEvdTests.cs
+++ b/src/UnitTests/LinearAlgebraTests/Single/Factorization/UserEvdTests.cs
@@ -190,10 +190,10 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Factorization
{
var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteUserDefinedMatrix(order);
var matrixACopy = matrixA.Clone();
- var factorSvd = matrixA.Svd(true);
+ var factorEvd = matrixA.Evd();
var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order);
- var resultx = factorSvd.Solve(vectorb);
+ var resultx = factorEvd.Solve(vectorb);
Assert.AreEqual(matrixA.ColumnCount, resultx.Count);
@@ -227,10 +227,10 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Factorization
{
var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteUserDefinedMatrix(order);
var matrixACopy = matrixA.Clone();
- var factorSvd = matrixA.Svd(true);
+ var factorEvd = matrixA.Evd();
var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
- var matrixX = factorSvd.Solve(matrixB);
+ var matrixX = factorEvd.Solve(matrixB);
// The solution X row dimension is equal to the column dimension of A
Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
@@ -270,11 +270,11 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Factorization
{
var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteUserDefinedMatrix(order);
var matrixACopy = matrixA.Clone();
- var factorSvd = matrixA.Svd(true);
+ var factorEvd = matrixA.Evd();
var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order);
var vectorbCopy = vectorb.Clone();
var resultx = new UserDefinedVector(order);
- factorSvd.Solve(vectorb, resultx);
+ factorEvd.Solve(vectorb, resultx);
var bReconstruct = matrixA * resultx;
@@ -312,13 +312,13 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Factorization
{
var matrixA = MatrixLoader.GenerateRandomPositiveDefiniteUserDefinedMatrix(order);
var matrixACopy = matrixA.Clone();
- var factorSvd = matrixA.Svd(true);
+ var factorEvd = matrixA.Evd();
var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
var matrixBCopy = matrixB.Clone();
var matrixX = new UserDefinedMatrix(order, order);
- factorSvd.Solve(matrixB, matrixX);
+ factorEvd.Solve(matrixB, matrixX);
// The solution X row dimension is equal to the column dimension of A
Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
diff --git a/src/UnitTests/LinearAlgebraTests/Single/Factorization/UserGramSchmidtTests.cs b/src/UnitTests/LinearAlgebraTests/Single/Factorization/UserGramSchmidtTests.cs
new file mode 100644
index 00000000..90e3cba0
--- /dev/null
+++ b/src/UnitTests/LinearAlgebraTests/Single/Factorization/UserGramSchmidtTests.cs
@@ -0,0 +1,331 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+// http://mathnetnumerics.codeplex.com
+//
+// Copyright (c) 2009-2010 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.Factorization
+{
+ using LinearAlgebra.Generic.Factorization;
+ using MbUnit.Framework;
+ using LinearAlgebra.Single.Factorization;
+
+ public class UserGramSchmidtTests
+ {
+ [Test]
+ [ExpectedArgumentNullException]
+ public void ConstructorNull()
+ {
+ new UserGramSchmidt(null);
+ }
+
+ [Test]
+ [ExpectedArgumentException]
+ public void WideMatrixThrowsInvalidMatrixOperationException()
+ {
+ new UserGramSchmidt(new UserDefinedMatrix(3, 4));
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(10)]
+ [Row(100)]
+ public void CanFactorizeIdentity(int order)
+ {
+ var I = UserDefinedMatrix.Identity(order);
+ var factorGramSchmidt = I.GramSchmidt();
+
+ Assert.AreEqual(I.RowCount, factorGramSchmidt.Q.RowCount);
+ Assert.AreEqual(I.ColumnCount, factorGramSchmidt.Q.ColumnCount);
+
+ for (var i = 0; i < factorGramSchmidt.R.RowCount; i++)
+ {
+ for (var j = 0; j < factorGramSchmidt.R.ColumnCount; j++)
+ {
+ if (i == j)
+ {
+ Assert.AreEqual(1.0, factorGramSchmidt.R[i, j]);
+ }
+ else
+ {
+ Assert.AreEqual(0.0, factorGramSchmidt.R[i, j]);
+ }
+ }
+ }
+
+ for (var i = 0; i < factorGramSchmidt.Q.RowCount; i++)
+ {
+ for (var j = 0; j < factorGramSchmidt.Q.ColumnCount; j++)
+ {
+ if (i == j)
+ {
+ Assert.AreEqual(1.0, factorGramSchmidt.Q[i, j]);
+ }
+ else
+ {
+ Assert.AreEqual(0.0, factorGramSchmidt.Q[i, j]);
+ }
+ }
+ }
+ }
+
+
+ [Test]
+ [Row(1)]
+ [Row(10)]
+ [Row(100)]
+ public void IdentityDeterminantIsOne(int order)
+ {
+ var I = UserDefinedMatrix.Identity(order);
+ var factorGramSchmidt = I.GramSchmidt();
+ Assert.AreEqual(1.0, factorGramSchmidt.Determinant);
+ }
+
+ [Test]
+ [Row(1,1)]
+ [Row(2,2)]
+ [Row(5,5)]
+ [Row(10,6)]
+ [Row(50,48)]
+ [Row(100,98)]
+ [MultipleAsserts]
+ public void CanFactorizeRandomMatrix(int row, int column)
+ {
+ var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(row, column);
+ var factorGramSchmidt = matrixA.GramSchmidt();
+
+ // Make sure the Q has the right dimensions.
+ Assert.AreEqual(row, factorGramSchmidt.Q.RowCount);
+ Assert.AreEqual(column, factorGramSchmidt.Q.ColumnCount);
+
+ // Make sure the R has the right dimensions.
+ Assert.AreEqual(column, factorGramSchmidt.R.RowCount);
+ Assert.AreEqual(column, factorGramSchmidt.R.ColumnCount);
+
+ // Make sure the R factor is upper triangular.
+ for (var i = 0; i < factorGramSchmidt.R.RowCount; i++)
+ {
+ for (var j = 0; j < factorGramSchmidt.R.ColumnCount; j++)
+ {
+ if (i > j)
+ {
+ Assert.AreEqual(0.0, factorGramSchmidt.R[i, j]);
+ }
+ }
+ }
+
+ // Make sure the Q*R is the original matrix.
+ var matrixQfromR = factorGramSchmidt.Q * factorGramSchmidt.R;
+ for (var i = 0; i < matrixQfromR.RowCount; i++)
+ {
+ for (var j = 0; j < matrixQfromR.ColumnCount; j++)
+ {
+ Assert.AreApproximatelyEqual(matrixA[i, j], matrixQfromR[i, j], 10e-5f);
+ }
+ }
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(2)]
+ [Row(5)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanSolveForRandomVector(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixACopy = matrixA.Clone();
+ var factorGramSchmidt = matrixA.GramSchmidt();
+
+ var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order);
+ var resultx = factorGramSchmidt.Solve(vectorb);
+
+ Assert.AreEqual(matrixA.ColumnCount, resultx.Count);
+
+ var bReconstruct = matrixA * resultx;
+
+ // Check the reconstruction.
+ for (var i = 0; i < order; i++)
+ {
+ Assert.AreApproximatelyEqual(vectorb[i], bReconstruct[i], 10e-5f);
+ }
+
+ // Make sure A didn't change.
+ for (var i = 0; i < matrixA.RowCount; i++)
+ {
+ for (var j = 0; j < matrixA.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
+ }
+ }
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(4)]
+ [Row(8)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanSolveForRandomMatrix(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixACopy = matrixA.Clone();
+ var factorGramSchmidt = matrixA.GramSchmidt();
+
+ var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixX = factorGramSchmidt.Solve(matrixB);
+
+ // The solution X row dimension is equal to the column dimension of A
+ Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
+ // The solution X has the same number of columns as B
+ Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
+
+ var matrixBReconstruct = matrixA * matrixX;
+
+ // Check the reconstruction.
+ for (var i = 0; i < matrixB.RowCount; i++)
+ {
+ for (var j = 0; j < matrixB.ColumnCount; j++)
+ {
+ Assert.AreApproximatelyEqual(matrixB[i, j], matrixBReconstruct[i, j], 10e-5f);
+ }
+ }
+
+ // Make sure A didn't change.
+ for (var i = 0; i < matrixA.RowCount; i++)
+ {
+ for (var j = 0; j < matrixA.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
+ }
+ }
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(2)]
+ [Row(5)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanSolveForRandomVectorWhenResultVectorGiven(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixACopy = matrixA.Clone();
+ var factorGramSchmidt = matrixA.GramSchmidt();
+ var vectorb = MatrixLoader.GenerateRandomUserDefinedVector(order);
+ var vectorbCopy = vectorb.Clone();
+ var resultx = new UserDefinedVector(order);
+ factorGramSchmidt.Solve(vectorb,resultx);
+
+ Assert.AreEqual(vectorb.Count, resultx.Count);
+
+ var bReconstruct = matrixA * resultx;
+
+ // Check the reconstruction.
+ for (var i = 0; i < vectorb.Count; i++)
+ {
+ Assert.AreApproximatelyEqual(vectorb[i], bReconstruct[i], 10e-5f);
+ }
+
+ // Make sure A didn't change.
+ for (var i = 0; i < matrixA.RowCount; i++)
+ {
+ for (var j = 0; j < matrixA.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
+ }
+ }
+
+ // Make sure b didn't change.
+ for (var i = 0; i < vectorb.Count; i++)
+ {
+ Assert.AreEqual(vectorbCopy[i], vectorb[i]);
+ }
+ }
+
+ [Test]
+ [Row(1)]
+ [Row(4)]
+ [Row(8)]
+ [Row(10)]
+ [Row(50)]
+ [Row(100)]
+ [MultipleAsserts]
+ public void CanSolveForRandomMatrixWhenResultMatrixGiven(int order)
+ {
+ var matrixA = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixACopy = matrixA.Clone();
+ var factorGramSchmidt = matrixA.GramSchmidt();
+
+ var matrixB = MatrixLoader.GenerateRandomUserDefinedMatrix(order, order);
+ var matrixBCopy = matrixB.Clone();
+
+ var matrixX = new UserDefinedMatrix(order, order);
+ factorGramSchmidt.Solve(matrixB,matrixX);
+
+ // The solution X row dimension is equal to the column dimension of A
+ Assert.AreEqual(matrixA.ColumnCount, matrixX.RowCount);
+ // The solution X has the same number of columns as B
+ Assert.AreEqual(matrixB.ColumnCount, matrixX.ColumnCount);
+
+ var matrixBReconstruct = matrixA * matrixX;
+
+ // Check the reconstruction.
+ for (var i = 0; i < matrixB.RowCount; i++)
+ {
+ for (var j = 0; j < matrixB.ColumnCount; j++)
+ {
+ Assert.AreApproximatelyEqual(matrixB[i, j], matrixBReconstruct[i, j], 10e-5f);
+ }
+ }
+
+ // Make sure A didn't change.
+ for (var i = 0; i < matrixA.RowCount; i++)
+ {
+ for (var j = 0; j < matrixA.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixACopy[i, j], matrixA[i, j]);
+ }
+ }
+
+ // Make sure B didn't change.
+ for (var i = 0; i < matrixB.RowCount; i++)
+ {
+ for (var j = 0; j < matrixB.ColumnCount; j++)
+ {
+ Assert.AreEqual(matrixBCopy[i, j], matrixB[i, j]);
+ }
+ }
+ }
+ }
+}
diff --git a/src/UnitTests/UnitTests.csproj b/src/UnitTests/UnitTests.csproj
index 27aee4ab..08ec4cf3 100644
--- a/src/UnitTests/UnitTests.csproj
+++ b/src/UnitTests/UnitTests.csproj
@@ -110,6 +110,7 @@
+
@@ -125,6 +126,8 @@
+
+
@@ -165,6 +168,8 @@
+
+
@@ -200,8 +205,10 @@
-
+
+
+
@@ -233,6 +240,8 @@
+
+