From 22f65667a8ec3a36d87f95c410ebd671efc409f9 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Sun, 3 May 2020 15:39:21 +0200 Subject: [PATCH] Managed Provider: move depending algorithms into provider; providers must be self-contained --- .../Complex/Factorization/DenseEvd.cs | 720 +----------- .../Complex32/Factorization/DenseEvd.cs | 720 +----------- .../Double/Factorization/DenseEvd.cs | 1012 +--------------- .../Single/Factorization/DenseEvd.cs | 1012 +--------------- .../ManagedLinearAlgebraProvider.Complex.cs | 733 +++++++++++- .../ManagedLinearAlgebraProvider.Complex32.cs | 737 +++++++++++- .../ManagedLinearAlgebraProvider.Double.cs | 1022 ++++++++++++++++- .../ManagedLinearAlgebraProvider.Single.cs | 1022 ++++++++++++++++- .../Managed/ManagedLinearAlgebraProvider.cs | 6 +- ...dReferenceLinearAlgebraProvider.Complex.cs | 15 +- ...eferenceLinearAlgebraProvider.Complex32.cs | 19 +- ...edReferenceLinearAlgebraProvider.Double.cs | 12 +- ...edReferenceLinearAlgebraProvider.Single.cs | 12 +- .../ManagedReferenceLinearAlgebraProvider.cs | 40 +- 14 files changed, 3520 insertions(+), 3562 deletions(-) diff --git a/src/Numerics/LinearAlgebra/Complex/Factorization/DenseEvd.cs b/src/Numerics/LinearAlgebra/Complex/Factorization/DenseEvd.cs index b873c9fc..bb65e2bb 100644 --- a/src/Numerics/LinearAlgebra/Complex/Factorization/DenseEvd.cs +++ b/src/Numerics/LinearAlgebra/Complex/Factorization/DenseEvd.cs @@ -3,7 +3,7 @@ // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // -// Copyright (c) 2009-2013 Math.NET +// Copyright (c) 2009-2020 Math.NET // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation @@ -98,724 +98,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization { } - /// - /// 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. - internal 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*order + 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[k*order + i].Real) + Math.Abs(matrixA[k*order + i].Imaginary); - } - - if (scale == 0.0) - { - tau[i - 1] = Complex.One; - e[i] = 0.0; - } - else - { - for (var k = 0; k < i; k++) - { - matrixA[k*order + i] /= scale; - h += matrixA[k*order + i].MagnitudeSquared(); - } - - Complex g = Math.Sqrt(h); - e[i] = scale*g.Real; - - Complex temp; - var im1Oi = (i - 1)*order + i; - var f = matrixA[im1Oi]; - if (f.Magnitude != 0) - { - temp = -(matrixA[im1Oi].Conjugate()*tau[i].Conjugate())/f.Magnitude; - h += f.Magnitude*g.Real; - g = 1.0 + (g/f.Magnitude); - matrixA[im1Oi] *= g; - } - else - { - temp = -tau[i].Conjugate(); - matrixA[im1Oi] = g; - } - - if ((f.Magnitude == 0) || (i != 1)) - { - f = Complex.Zero; - for (var j = 0; j < i; j++) - { - var tmp = Complex.Zero; - var jO = j*order; - // Form element of A*U. - for (var k = 0; k <= j; k++) - { - tmp += matrixA[k*order + j]*matrixA[k*order + i].Conjugate(); - } - - for (var k = j + 1; k <= i - 1; k++) - { - tmp += matrixA[jO + k].Conjugate()*matrixA[k*order + i].Conjugate(); - } - - // Form element of P - tau[j] = tmp/h; - f += (tmp/h)*matrixA[jO + i]; - } - - hh = f.Real/(h + h); - - // Form the reduced A. - for (var j = 0; j < i; j++) - { - f = matrixA[j*order + i].Conjugate(); - g = tau[j] - (hh*f); - tau[j] = g.Conjugate(); - - for (var k = 0; k <= j; k++) - { - matrixA[k*order + j] -= (f*tau[k]) + (g*matrixA[k*order + i]); - } - } - } - - for (var k = 0; k < i; k++) - { - matrixA[k*order + i] *= scale; - } - - tau[i - 1] = temp.Conjugate(); - } - - hh = d[i]; - d[i] = matrixA[i*order + i].Real; - matrixA[i*order + i] = new Complex(hh, scale*Math.Sqrt(h)); - } - - hh = d[0]; - d[0] = matrixA[0].Real; - matrixA[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. - /// - internal 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.DoublePrecision; - 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 NonConvergenceException(); - } - } 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. - internal 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*order + 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[k*order + i]; - } - - s = (s/h)/h; - - for (var k = 0; k < i; k++) - { - dataEv[(j*order) + k] -= s*matrixA[k*order + i].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. - internal 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; - var mm1O = (m - 1)*order; - for (var i = m; i < order; i++) - { - scale += Math.Abs(matrixH[mm1O + i].Real) + Math.Abs(matrixH[mm1O + i].Imaginary); - } - - if (scale != 0.0) - { - // Compute Householder transformation. - var h = 0.0; - for (var i = order - 1; i >= m; i--) - { - ort[i] = matrixH[mm1O + i]/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[mm1O + m] = 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; - var jO = j*order; - for (var i = order - 1; i >= m; i--) - { - f += ort[i].Conjugate()*matrixH[jO + i]; - } - - f = f/h; - for (var i = m; i < order; i++) - { - matrixH[jO + i] -= 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[j*order + i]; - } - - f = f/h; - for (var j = m; j < order; j++) - { - matrixH[j*order + i] -= f*ort[j].Conjugate(); - } - } - - ort[m] = scale*ort[m]; - matrixH[mm1O + m] *= -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--) - { - var mm1O = (m - 1)*order; - var mm1Om = mm1O + m; - if (matrixH[mm1Om] != Complex.Zero && ort[m] != Complex.Zero) - { - var norm = (matrixH[mm1Om].Real*ort[m].Real) + (matrixH[mm1Om].Imaginary*ort[m].Imaginary); - - for (var i = m + 1; i < order; i++) - { - ort[i] = matrixH[mm1O + i]; - } - - 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++) - { - var im1 = i - 1; - var im1O = im1*order; - var im1Oi = im1O + i; - var iO = i*order; - if (matrixH[im1Oi].Imaginary != 0.0) - { - var y = matrixH[im1Oi]/matrixH[im1Oi].Magnitude; - matrixH[im1Oi] = matrixH[im1Oi].Magnitude; - for (var j = i; j < order; j++) - { - matrixH[j*order + i] *= y.Conjugate(); - } - - for (var j = 0; j <= Math.Min(i + 1, order - 1); j++) - { - matrixH[iO + j] *= 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. - internal static void NonsymmetricReduceHessenberToRealSchur(Complex[] vectorV, Complex[] dataEv, Complex[] matrixH, int order) - { - // Initialize - var n = order - 1; - var eps = Precision.DoublePrecision; - - double norm; - Complex 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 lm1 = l - 1; - var lm1O = lm1*order; - var lO = l*order; - var tst1 = Math.Abs(matrixH[lm1O + lm1].Real) + Math.Abs(matrixH[lm1O + lm1].Imaginary) + Math.Abs(matrixH[lO + l].Real) + Math.Abs(matrixH[lO + l].Imaginary); - if (Math.Abs(matrixH[lm1O + l].Real) < eps*tst1) - { - break; - } - - l--; - } - - var nm1 = n - 1; - var nm1O = nm1*order; - var nO = n*order; - var nOn = nO + n; - // Check for convergence - // One root found - if (l == n) - { - matrixH[nOn] += exshift; - vectorV[n] = matrixH[nOn]; - n--; - iter = 0; - } - else - { - // Form shift - Complex s; - if (iter != 10 && iter != 20) - { - s = matrixH[nOn]; - x = matrixH[nO + nm1]*matrixH[nm1O + n].Real; - - if (x.Real != 0.0 || x.Imaginary != 0.0) - { - y = (matrixH[nm1O + nm1] - 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[nm1O + n].Real) + Math.Abs(matrixH[(n - 2)*order + nm1].Real); - } - - for (var i = 0; i <= n; i++) - { - matrixH[i*order + i] -= s; - } - - exshift += s; - iter++; - - // Reduce to triangle (rows) - for (var i = l + 1; i <= n; i++) - { - var im1 = i - 1; - var im1O = im1*order; - var im1Oim1 = im1O + im1; - s = matrixH[im1O + i].Real; - norm = SpecialFunctions.Hypotenuse(matrixH[im1Oim1].Magnitude, s.Real); - x = matrixH[im1Oim1]/norm; - vectorV[i - 1] = x; - matrixH[im1Oim1] = norm; - matrixH[im1O + i] = new Complex(0.0, s.Real/norm); - - for (var j = i; j < order; j++) - { - var jO = j*order; - y = matrixH[jO + im1]; - z = matrixH[jO + i]; - matrixH[jO + im1] = (x.Conjugate()*y) + (matrixH[im1O + i].Imaginary*z); - matrixH[jO + i] = (x*z) - (matrixH[im1O + i].Imaginary*y); - } - } - - s = matrixH[nOn]; - if (s.Imaginary != 0.0) - { - s /= matrixH[nOn].Magnitude; - matrixH[nOn] = matrixH[nOn].Magnitude; - - for (var j = n + 1; j < order; j++) - { - matrixH[j*order + n] *= s.Conjugate(); - } - } - - // Inverse operation (columns). - for (var j = l + 1; j <= n; j++) - { - x = vectorV[j - 1]; - var jO = j*order; - var jm1 = j - 1; - var jm1O = jm1*order; - var jm1Oj = jm1O + j; - for (var i = 0; i <= j; i++) - { - var jm1Oi = jm1O + i; - z = matrixH[jO + i]; - if (i != j) - { - y = matrixH[jm1Oi]; - matrixH[jm1Oi] = (x*y) + (matrixH[jm1O + j].Imaginary*z); - } - else - { - y = matrixH[jm1Oi].Real; - matrixH[jm1Oi] = new Complex((x.Real*y.Real) - (x.Imaginary*y.Imaginary) + (matrixH[jm1O + j].Imaginary*z.Real), matrixH[jm1Oi].Imaginary); - } - - matrixH[jO + i] = (x.Conjugate()*z) - (matrixH[jm1O + j].Imaginary*y); - } - - for (var i = 0; i < order; i++) - { - y = dataEv[((j - 1)*order) + i]; - z = dataEv[(j*order) + i]; - dataEv[jm1O + i] = (x*y) + (matrixH[jm1Oj].Imaginary*z); - dataEv[jO + i] = (x.Conjugate()*z) - (matrixH[jm1Oj].Imaginary*y); - } - } - - if (s.Imaginary != 0.0) - { - for (var i = 0; i <= n; i++) - { - matrixH[nO + i] *= s; - } - - for (var i = 0; i < order; i++) - { - dataEv[nO + 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[j*order + i].Real) + Math.Abs(matrixH[j*order + i].Imaginary)); - } - } - - if (order == 1) - { - return; - } - - if (norm == 0.0) - { - return; - } - - for (n = order - 1; n > 0; n--) - { - var nO = n*order; - var nOn = nO + n; - x = vectorV[n]; - matrixH[nOn] = 1.0; - - for (var i = n - 1; i >= 0; i--) - { - z = 0.0; - for (var j = i + 1; j <= n; j++) - { - z += matrixH[j*order + i]*matrixH[nO + j]; - } - - y = x - vectorV[i]; - if (y.Real == 0.0 && y.Imaginary == 0.0) - { - y = eps*norm; - } - - matrixH[nO + i] = z/y; - - // Overflow control - var tr = Math.Abs(matrixH[nO + i].Real) + Math.Abs(matrixH[nO + i].Imaginary); - if ((eps*tr)*tr > 1) - { - for (var j = i; j <= n; j++) - { - matrixH[nO + j] = matrixH[nO + j]/tr; - } - } - } - } - - // Back transformation to get eigenvectors of original matrix - for (var j = order - 1; j > 0; j--) - { - var jO = j*order; - for (var i = 0; i < order; i++) - { - z = Complex.Zero; - for (var k = 0; k <= j; k++) - { - z += dataEv[(k*order) + i]*matrixH[jO + k]; - } - - dataEv[jO + i] = z; - } - } - } - /// /// Solves a system of linear equations, AX = B, with A SVD factorized. /// diff --git a/src/Numerics/LinearAlgebra/Complex32/Factorization/DenseEvd.cs b/src/Numerics/LinearAlgebra/Complex32/Factorization/DenseEvd.cs index 951c4bad..e54487f7 100644 --- a/src/Numerics/LinearAlgebra/Complex32/Factorization/DenseEvd.cs +++ b/src/Numerics/LinearAlgebra/Complex32/Factorization/DenseEvd.cs @@ -3,7 +3,7 @@ // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // -// Copyright (c) 2009-2013 Math.NET +// Copyright (c) 2009-2020 Math.NET // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation @@ -99,724 +99,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization { } - /// - /// 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. - internal 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*order + 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[k*order + i].Real) + Math.Abs(matrixA[k*order + i].Imaginary); - } - - if (scale == 0.0f) - { - tau[i - 1] = Complex32.One; - e[i] = 0.0f; - } - else - { - for (var k = 0; k < i; k++) - { - matrixA[k*order + i] /= scale; - h += matrixA[k*order + i].MagnitudeSquared; - } - - Complex32 g = (float) Math.Sqrt(h); - e[i] = scale*g.Real; - - Complex32 temp; - var im1Oi = (i - 1)*order + i; - var f = matrixA[im1Oi]; - if (f.Magnitude != 0.0f) - { - temp = -(matrixA[im1Oi].Conjugate()*tau[i].Conjugate())/f.Magnitude; - h += f.Magnitude*g.Real; - g = 1.0f + (g/f.Magnitude); - matrixA[im1Oi] *= g; - } - else - { - temp = -tau[i].Conjugate(); - matrixA[im1Oi] = g; - } - - if ((f.Magnitude == 0.0f) || (i != 1)) - { - f = Complex32.Zero; - for (var j = 0; j < i; j++) - { - var tmp = Complex32.Zero; - var jO = j*order; - // Form element of A*U. - for (var k = 0; k <= j; k++) - { - tmp += matrixA[k*order + j]*matrixA[k*order + i].Conjugate(); - } - - for (var k = j + 1; k <= i - 1; k++) - { - tmp += matrixA[jO + k].Conjugate()*matrixA[k*order + i].Conjugate(); - } - - // Form element of P - tau[j] = tmp/h; - f += (tmp/h)*matrixA[jO + i]; - } - - hh = f.Real/(h + h); - - // Form the reduced A. - for (var j = 0; j < i; j++) - { - f = matrixA[j*order + i].Conjugate(); - g = tau[j] - (hh*f); - tau[j] = g.Conjugate(); - - for (var k = 0; k <= j; k++) - { - matrixA[k*order + j] -= (f*tau[k]) + (g*matrixA[k*order + i]); - } - } - } - - for (var k = 0; k < i; k++) - { - matrixA[k*order + i] *= scale; - } - - tau[i - 1] = temp.Conjugate(); - } - - hh = d[i]; - d[i] = matrixA[i*order + i].Real; - matrixA[i*order + i] = new Complex32(hh, scale*(float) Math.Sqrt(h)); - } - - hh = d[0]; - d[0] = matrixA[0].Real; - matrixA[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. - /// - internal 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.DoublePrecision; - 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 NonConvergenceException(); - } - } 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. - internal 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*order + 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[k*order + i]; - } - - s = (s/h)/h; - - for (var k = 0; k < i; k++) - { - dataEv[(j*order) + k] -= s*matrixA[k*order + i].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. - internal 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; - var mm1O = (m - 1)*order; - for (var i = m; i < order; i++) - { - scale += Math.Abs(matrixH[mm1O + i].Real) + Math.Abs(matrixH[mm1O + i].Imaginary); - } - - if (scale != 0.0f) - { - // Compute Householder transformation. - var h = 0.0f; - for (var i = order - 1; i >= m; i--) - { - ort[i] = matrixH[mm1O + i]/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[mm1O + m] = 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; - var jO = j*order; - for (var i = order - 1; i >= m; i--) - { - f += ort[i].Conjugate()*matrixH[jO + i]; - } - - f = f/h; - for (var i = m; i < order; i++) - { - matrixH[jO + i] -= 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[j*order + i]; - } - - f = f/h; - for (var j = m; j < order; j++) - { - matrixH[j*order + i] -= f*ort[j].Conjugate(); - } - } - - ort[m] = scale*ort[m]; - matrixH[mm1O + m] *= -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--) - { - var mm1O = (m - 1)*order; - var mm1Om = mm1O + m; - if (matrixH[mm1Om] != Complex32.Zero && ort[m] != Complex32.Zero) - { - var norm = (matrixH[mm1Om].Real*ort[m].Real) + (matrixH[mm1Om].Imaginary*ort[m].Imaginary); - - for (var i = m + 1; i < order; i++) - { - ort[i] = matrixH[mm1O + i]; - } - - 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++) - { - var im1 = i - 1; - var im1O = im1*order; - var im1Oi = im1O + i; - var iO = i*order; - if (matrixH[im1Oi].Imaginary != 0.0f) - { - var y = matrixH[im1Oi]/matrixH[im1Oi].Magnitude; - matrixH[im1Oi] = matrixH[im1Oi].Magnitude; - for (var j = i; j < order; j++) - { - matrixH[j*order + i] *= y.Conjugate(); - } - - for (var j = 0; j <= Math.Min(i + 1, order - 1); j++) - { - matrixH[iO + j] *= 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. - internal static void NonsymmetricReduceHessenberToRealSchur(Complex32[] vectorV, Complex32[] dataEv, Complex32[] matrixH, int order) - { - // Initialize - var n = order - 1; - var eps = (float) Precision.SinglePrecision; - - float norm; - Complex32 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 lm1 = l - 1; - var lm1O = lm1*order; - var lO = l*order; - var tst1 = Math.Abs(matrixH[lm1O + lm1].Real) + Math.Abs(matrixH[lm1O + lm1].Imaginary) + Math.Abs(matrixH[lO + l].Real) + Math.Abs(matrixH[lO + l].Imaginary); - if (Math.Abs(matrixH[lm1O + l].Real) < eps*tst1) - { - break; - } - - l--; - } - - var nm1 = n - 1; - var nm1O = nm1*order; - var nO = n*order; - var nOn = nO + n; - // Check for convergence - // One root found - if (l == n) - { - matrixH[nOn] += exshift; - vectorV[n] = matrixH[nOn]; - n--; - iter = 0; - } - else - { - // Form shift - Complex32 s; - if (iter != 10 && iter != 20) - { - s = matrixH[nOn]; - x = matrixH[nO + nm1]*matrixH[nm1O + n].Real; - - if (x.Real != 0.0f || x.Imaginary != 0.0f) - { - y = (matrixH[nm1O + nm1] - s)/2.0f; - z = ((y*y) + x).SquareRoot(); - if ((y.Real*z.Real) + (y.Imaginary*z.Imaginary) < 0.0) - { - z *= -1.0f; - } - - x /= y + z; - s = s - x; - } - } - else - { - // Form exceptional shift - s = Math.Abs(matrixH[nm1O + n].Real) + Math.Abs(matrixH[(n - 2)*order + nm1].Real); - } - - for (var i = 0; i <= n; i++) - { - matrixH[i*order + i] -= s; - } - - exshift += s; - iter++; - - // Reduce to triangle (rows) - for (var i = l + 1; i <= n; i++) - { - var im1 = i - 1; - var im1O = im1*order; - var im1Oim1 = im1O + im1; - s = matrixH[im1O + i].Real; - norm = SpecialFunctions.Hypotenuse(matrixH[im1Oim1].Magnitude, s.Real); - x = matrixH[im1Oim1]/norm; - vectorV[i - 1] = x; - matrixH[im1Oim1] = norm; - matrixH[im1O + i] = new Complex32(0.0f, s.Real/norm); - - for (var j = i; j < order; j++) - { - var jO = j*order; - y = matrixH[jO + im1]; - z = matrixH[jO + i]; - matrixH[jO + im1] = (x.Conjugate()*y) + (matrixH[im1O + i].Imaginary*z); - matrixH[jO + i] = (x*z) - (matrixH[im1O + i].Imaginary*y); - } - } - - s = matrixH[nOn]; - if (s.Imaginary != 0.0f) - { - s /= matrixH[nOn].Magnitude; - matrixH[nOn] = matrixH[nOn].Magnitude; - - for (var j = n + 1; j < order; j++) - { - matrixH[j*order + n] *= s.Conjugate(); - } - } - - // Inverse operation (columns). - for (var j = l + 1; j <= n; j++) - { - x = vectorV[j - 1]; - var jO = j*order; - var jm1 = j - 1; - var jm1O = jm1*order; - var jm1Oj = jm1O + j; - for (var i = 0; i <= j; i++) - { - var jm1Oi = jm1O + i; - z = matrixH[jO + i]; - if (i != j) - { - y = matrixH[jm1Oi]; - matrixH[jm1Oi] = (x*y) + (matrixH[jm1O + j].Imaginary*z); - } - else - { - y = matrixH[jm1Oi].Real; - matrixH[jm1Oi] = new Complex32((x.Real*y.Real) - (x.Imaginary*y.Imaginary) + (matrixH[jm1O + j].Imaginary*z.Real), matrixH[jm1Oi].Imaginary); - } - - matrixH[jO + i] = (x.Conjugate()*z) - (matrixH[jm1O + j].Imaginary*y); - } - - for (var i = 0; i < order; i++) - { - y = dataEv[((j - 1)*order) + i]; - z = dataEv[(j*order) + i]; - dataEv[jm1O + i] = (x*y) + (matrixH[jm1Oj].Imaginary*z); - dataEv[jO + i] = (x.Conjugate()*z) - (matrixH[jm1Oj].Imaginary*y); - } - } - - if (s.Imaginary != 0.0f) - { - for (var i = 0; i <= n; i++) - { - matrixH[nO + i] *= s; - } - - for (var i = 0; i < order; i++) - { - dataEv[nO + 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[j*order + i].Real) + Math.Abs(matrixH[j*order + i].Imaginary)); - } - } - - if (order == 1) - { - return; - } - - if (norm == 0.0) - { - return; - } - - for (n = order - 1; n > 0; n--) - { - var nO = n*order; - var nOn = nO + n; - x = vectorV[n]; - matrixH[nOn] = 1.0f; - - for (var i = n - 1; i >= 0; i--) - { - z = 0.0f; - for (var j = i + 1; j <= n; j++) - { - z += matrixH[j*order + i]*matrixH[nO + j]; - } - - y = x - vectorV[i]; - if (y.Real == 0.0f && y.Imaginary == 0.0f) - { - y = eps*norm; - } - - matrixH[nO + i] = z/y; - - // Overflow control - var tr = Math.Abs(matrixH[nO + i].Real) + Math.Abs(matrixH[nO + i].Imaginary); - if ((eps*tr)*tr > 1) - { - for (var j = i; j <= n; j++) - { - matrixH[nO + j] = matrixH[nO + j]/tr; - } - } - } - } - - // Back transformation to get eigenvectors of original matrix - for (var j = order - 1; j > 0; j--) - { - var jO = j*order; - for (var i = 0; i < order; i++) - { - z = Complex32.Zero; - for (var k = 0; k <= j; k++) - { - z += dataEv[(k*order) + i]*matrixH[jO + k]; - } - - dataEv[jO + i] = z; - } - } - } - /// /// Solves a system of linear equations, AX = B, with A SVD factorized. /// diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/DenseEvd.cs b/src/Numerics/LinearAlgebra/Double/Factorization/DenseEvd.cs index a41a8b02..b61da00c 100644 --- a/src/Numerics/LinearAlgebra/Double/Factorization/DenseEvd.cs +++ b/src/Numerics/LinearAlgebra/Double/Factorization/DenseEvd.cs @@ -3,7 +3,7 @@ // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // -// Copyright (c) 2009-2013 Math.NET +// Copyright (c) 2009-2020 Math.NET // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation @@ -99,1016 +99,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization { } - /// - /// 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. - internal 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. - /// - internal 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.DoublePrecision; - 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 NonConvergenceException(); - } - } 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. - internal static void NonsymmetricReduceToHessenberg(double[] a, double[] matrixH, int order) - { - var ort = new double[order]; - var high = order - 1; - for (var m = 1; m <= high - 1; m++) - { - var mm1 = m - 1; - var mm1O = mm1*order; - // Scale column. - var scale = 0.0; - for (var i = m; i <= high; i++) - { - scale += Math.Abs(matrixH[mm1O + i]); - } - - if (scale != 0.0) - { - // Compute Householder transformation. - var h = 0.0; - for (var i = high; i >= m; i--) - { - ort[i] = matrixH[mm1O + i]/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 jO = j*order; - var f = 0.0; - for (var i = order - 1; i >= m; i--) - { - f += ort[i]*matrixH[jO + i]; - } - - f = f/h; - - for (var i = m; i <= high; i++) - { - matrixH[jO + i] -= f*ort[i]; - } - } - - for (var i = 0; i <= high; i++) - { - var f = 0.0; - for (var j = high; j >= m; j--) - { - f += ort[j]*matrixH[j*order + i]; - } - f = f/h; - - for (var j = m; j <= high; j++) - { - matrixH[j*order + i] -= f*ort[j]; - } - } - - ort[m] = scale*ort[m]; - matrixH[mm1O + m] = 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 = high - 1; m >= 1; m--) - { - var mm1 = m - 1; - var mm1O = mm1*order; - var mm1Om = mm1O + m; - if (matrixH[mm1Om] != 0.0) - { - for (var i = m + 1; i <= high; i++) - { - ort[i] = matrixH[mm1O + i]; - } - - for (var j = m; j <= high; j++) - { - var g = 0.0; - var jO = j*order; - for (var i = m; i <= high; i++) - { - g += ort[i]*a[jO + i]; - } - - // Double division avoids possible underflow - g = (g/ort[m])/matrixH[mm1Om]; - - for (var i = m; i <= high; i++) - { - a[jO + 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. - /// - internal static void NonsymmetricReduceHessenberToRealSchur(double[] a, double[] matrixH, double[] d, double[] e, int order) - { - // Initialize - var n = order - 1; - var eps = Math.Pow(2.0, -52.0); - var exshift = 0.0; - double p = 0, q = 0, r = 0, s = 0, z = 0; - double 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[j*order + i]); - } - } - - // 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 lm1 = l - 1; - var lm1O = lm1*order; - s = Math.Abs(matrixH[lm1O + lm1]) + Math.Abs(matrixH[l*order + l]); - - if (s == 0.0) - { - s = norm; - } - - if (Math.Abs(matrixH[lm1O + l]) < eps*s) - { - break; - } - - l--; - } - - // Check for convergence - // One root found - if (l == n) - { - var index = n*order + n; - matrixH[index] += exshift; - d[n] = matrixH[index]; - e[n] = 0.0; - n--; - iter = 0; - - // Two roots found - } - else if (l == n - 1) - { - var nO = n*order; - var nm1 = n - 1; - var nm1O = nm1*order; - var nOn = nO + n; - - w = matrixH[nm1O + n]*matrixH[nO + nm1]; - p = (matrixH[nm1O + nm1] - matrixH[nOn])/2.0; - q = (p*p) + w; - z = Math.Sqrt(Math.Abs(q)); - - matrixH[nOn] += exshift; - matrixH[nm1O + nm1] += exshift; - x = matrixH[nOn]; - - // Real pair - if (q >= 0) - { - if (p >= 0) - { - z = p + z; - } - else - { - z = p - z; - } - - d[nm1] = x + z; - - d[n] = d[nm1]; - if (z != 0.0) - { - d[n] = x - (w/z); - } - - e[n - 1] = 0.0; - e[n] = 0.0; - x = matrixH[nm1O + n]; - 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++) - { - var jO = j*order; - var jOn = jO + n; - z = matrixH[jO + nm1]; - matrixH[jO + nm1] = (q*z) + (p*matrixH[jOn]); - matrixH[jOn] = (q*matrixH[jOn]) - (p*z); - } - - // Column modification - for (var i = 0; i <= n; i++) - { - var nOi = nO + i; - z = matrixH[nm1O + i]; - matrixH[nm1O + i] = (q*z) + (p*matrixH[nOi]); - matrixH[nOi] = (q*matrixH[nOi]) - (p*z); - } - - // Accumulate transformations - for (var i = 0; i < order; i++) - { - var nOi = nO + i; - z = a[nm1O + i]; - a[nm1O + i] = (q*z) + (p*a[nOi]); - a[nOi] = (q*a[nOi]) - (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 - { - var nO = n*order; - var nm1 = n - 1; - var nm1O = nm1*order; - var nOn = nO + n; - - // Form shift - x = matrixH[nOn]; - y = 0.0; - w = 0.0; - if (l < n) - { - y = matrixH[nm1O + nm1]; - w = matrixH[nm1O + n]*matrixH[nO + nm1]; - } - - // Wilkinson's original ad hoc shift - if (iter == 10) - { - exshift += x; - for (var i = 0; i <= n; i++) - { - matrixH[i*order + i] -= x; - } - - s = Math.Abs(matrixH[nm1O + n]) + Math.Abs(matrixH[(n - 2)*order + nm1]); - 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*order + i] -= s; - } - - exshift += s; - x = y = w = 0.964; - } - } - - iter = iter + 1; - if (iter >= 30*order) - { - throw new NonConvergenceException(); - } - - // Look for two consecutive small sub-diagonal elements - var m = n - 2; - while (m >= l) - { - var mp1 = m + 1; - var mm1 = m - 1; - var mO = m*order; - var mp1O = mp1*order; - var mm1O = mm1*order; - - z = matrixH[mO + m]; - r = x - z; - s = y - z; - p = (((r*s) - w)/matrixH[mO + mp1]) + matrixH[mp1O + m]; - q = matrixH[mp1O + mp1] - z - r - s; - r = matrixH[mp1O + (m + 2)]; - 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[mm1O + m])*(Math.Abs(q) + Math.Abs(r)) < eps*(Math.Abs(p)*(Math.Abs(matrixH[mm1O + mm1]) + Math.Abs(z) + Math.Abs(matrixH[mp1O + mp1])))) - { - break; - } - - m--; - } - - var mp2 = m + 2; - for (var i = mp2; i <= n; i++) - { - matrixH[(i - 2)*order + i] = 0.0; - if (i > mp2) - { - matrixH[(i - 3)*order + i] = 0.0; - } - } - - // Double QR step involving rows l:n and columns m:n - for (var k = m; k <= n - 1; k++) - { - var notlast = k != n - 1; - var kO = k*order; - var km1 = k - 1; - var kp1 = k + 1; - var kp2 = k + 2; - var kp1O = kp1*order; - var kp2O = kp2*order; - var km1O = km1*order; - if (k != m) - { - p = matrixH[km1O + k]; - q = matrixH[km1O + kp1]; - r = notlast ? matrixH[km1O + kp2] : 0.0; - x = Math.Abs(p) + Math.Abs(q) + Math.Abs(r); - if (x == 0.0) - { - continue; - } - - p = p/x; - q = q/x; - r = r/x; - } - - s = Math.Sqrt((p*p) + (q*q) + (r*r)); - - if (p < 0) - { - s = -s; - } - - if (s != 0.0) - { - if (k != m) - { - matrixH[km1O + k] = (-s)*x; - } - else if (l != m) - { - matrixH[km1O + k] = -matrixH[km1O + k]; - } - - 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++) - { - var jO = j*order; - var jOk = jO + k; - var jOkp1 = jO + kp1; - var jOkp2 = jO + kp2; - p = matrixH[jOk] + (q*matrixH[jOkp1]); - if (notlast) - { - p = p + (r*matrixH[jOkp2]); - matrixH[jOkp2] -= (p*z); - } - - matrixH[jOk] -= (p*x); - matrixH[jOkp1] -= (p*y); - } - - // Column modification - for (var i = 0; i <= Math.Min(n, k + 3); i++) - { - p = (x*matrixH[kO + i]) + (y*matrixH[kp1O + i]); - - if (notlast) - { - p = p + (z*matrixH[kp2O + i]); - matrixH[kp2O + i] -= (p*r); - } - - matrixH[kO + i] -= p; - matrixH[kp1O + i] -= (p*q); - } - - // Accumulate transformations - for (var i = 0; i < order; i++) - { - p = (x*a[kO + i]) + (y*a[kp1O + i]); - - if (notlast) - { - p = p + (z*a[kp2O + i]); - a[kp2O + i] -= p*r; - } - - a[kO + i] -= p; - a[kp1O + 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--) - { - var nO = n*order; - var nm1 = n - 1; - var nm1O = nm1*order; - - p = d[n]; - q = e[n]; - - - // Real vector - double t; - if (q == 0.0) - { - var l = n; - matrixH[nO + n] = 1.0; - for (var i = n - 1; i >= 0; i--) - { - var ip1 = i + 1; - var iO = i*order; - var ip1O = ip1*order; - - w = matrixH[iO + i] - p; - r = 0.0; - for (var j = l; j <= n; j++) - { - r = r + (matrixH[j*order + i]*matrixH[nO + j]); - } - - if (e[i] < 0.0) - { - z = w; - s = r; - } - else - { - l = i; - if (e[i] == 0.0) - { - if (w != 0.0) - { - matrixH[nO + i] = (-r)/w; - } - else - { - matrixH[nO + i] = (-r)/(eps*norm); - } - - // Solve real equations - } - else - { - x = matrixH[ip1O + i]; - y = matrixH[iO + ip1]; - q = ((d[i] - p)*(d[i] - p)) + (e[i]*e[i]); - t = ((x*s) - (z*r))/q; - matrixH[nO + i] = t; - if (Math.Abs(x) > Math.Abs(z)) - { - matrixH[nO + ip1] = (-r - (w*t))/x; - } - else - { - matrixH[nO + ip1] = (-s - (y*t))/z; - } - } - - // Overflow control - t = Math.Abs(matrixH[nO + i]); - if ((eps*t)*t > 1) - { - for (var j = i; j <= n; j++) - { - matrixH[nO + j] /= t; - } - } - } - } - - // Complex vector - } - else if (q < 0) - { - var l = n - 1; - - // Last vector component imaginary so matrix is triangular - if (Math.Abs(matrixH[nm1O + n]) > Math.Abs(matrixH[nO + nm1])) - { - matrixH[nm1O + nm1] = q/matrixH[nm1O + n]; - matrixH[nO + nm1] = (-(matrixH[nO + n] - p))/matrixH[nm1O + n]; - } - else - { - var res = Cdiv(0.0, -matrixH[nO + nm1], matrixH[nm1O + nm1] - p, q); - matrixH[nm1O + nm1] = res.Real; - matrixH[nO + nm1] = res.Imaginary; - } - - matrixH[nm1O + n] = 0.0; - matrixH[nO + n] = 1.0; - for (var i = n - 2; i >= 0; i--) - { - var ip1 = i + 1; - var iO = i*order; - var ip1O = ip1*order; - var ra = 0.0; - var sa = 0.0; - for (var j = l; j <= n; j++) - { - var jO = j*order; - var jOi = jO + i; - ra = ra + (matrixH[jOi]*matrixH[nm1O + j]); - sa = sa + (matrixH[jOi]*matrixH[nO + j]); - } - - w = matrixH[iO + 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[nm1O + i] = res.Real; - matrixH[nO + i] = res.Imaginary; - } - else - { - // Solve complex equations - x = matrixH[ip1O + i]; - y = matrixH[iO + ip1]; - - var vr = ((d[i] - p)*(d[i] - p)) + (e[i]*e[i]) - (q*q); - var 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[nm1O + i] = res.Real; - matrixH[nO + i] = res.Imaginary; - if (Math.Abs(x) > (Math.Abs(z) + Math.Abs(q))) - { - matrixH[nm1O + ip1] = (-ra - (w*matrixH[nm1O + i]) + (q*matrixH[nO + i]))/x; - matrixH[nO + ip1] = (-sa - (w*matrixH[nO + i]) - (q*matrixH[nm1O + i]))/x; - } - else - { - res = Cdiv(-r - (y*matrixH[nm1O + i]), -s - (y*matrixH[nO + i]), z, q); - matrixH[nm1O + ip1] = res.Real; - matrixH[nO + ip1] = res.Imaginary; - } - } - - // Overflow control - t = Math.Max(Math.Abs(matrixH[nm1O + i]), Math.Abs(matrixH[nO + i])); - if ((eps*t)*t > 1) - { - for (var j = i; j <= n; j++) - { - matrixH[nm1O + j] /= t; - matrixH[nO + j] /= t; - } - } - } - } - } - } - - // Back transformation to get eigenvectors of original matrix - for (var j = order - 1; j >= 0; j--) - { - var jO = j*order; - for (var i = 0; i < order; i++) - { - z = 0.0; - for (var k = 0; k <= j; k++) - { - z = z + (a[k*order + i]*matrixH[jO + k]); - } - - a[jO + 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. - 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. /// diff --git a/src/Numerics/LinearAlgebra/Single/Factorization/DenseEvd.cs b/src/Numerics/LinearAlgebra/Single/Factorization/DenseEvd.cs index f684f2ab..2dda05a9 100644 --- a/src/Numerics/LinearAlgebra/Single/Factorization/DenseEvd.cs +++ b/src/Numerics/LinearAlgebra/Single/Factorization/DenseEvd.cs @@ -3,7 +3,7 @@ // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // -// Copyright (c) 2009-2013 Math.NET +// Copyright (c) 2009-2020 Math.NET // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation @@ -99,1016 +99,6 @@ namespace MathNet.Numerics.LinearAlgebra.Single.Factorization { } - /// - /// 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. - internal 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. - /// - internal 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.SinglePrecision; - 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 NonConvergenceException(); - } - } 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. - internal static void NonsymmetricReduceToHessenberg(float[] a, float[] matrixH, int order) - { - var ort = new float[order]; - var high = order - 1; - for (var m = 1; m <= high - 1; m++) - { - var mm1 = m - 1; - var mm1O = mm1*order; - // Scale column. - var scale = 0.0f; - for (var i = m; i <= high; i++) - { - scale += Math.Abs(matrixH[mm1O + i]); - } - - if (scale != 0.0f) - { - // Compute Householder transformation. - var h = 0.0f; - for (var i = high; i >= m; i--) - { - ort[i] = matrixH[mm1O + i]/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 jO = j*order; - var f = 0.0f; - for (var i = order - 1; i >= m; i--) - { - f += ort[i]*matrixH[jO + i]; - } - - f = f/h; - - for (var i = m; i <= high; i++) - { - matrixH[jO + i] -= f*ort[i]; - } - } - - for (var i = 0; i <= high; i++) - { - var f = 0.0f; - for (var j = high; j >= m; j--) - { - f += ort[j]*matrixH[j*order + i]; - } - f = f/h; - - for (var j = m; j <= high; j++) - { - matrixH[j*order + i] -= f*ort[j]; - } - } - - ort[m] = scale*ort[m]; - matrixH[mm1O + m] = 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 = high - 1; m >= 1; m--) - { - var mm1 = m - 1; - var mm1O = mm1*order; - var mm1Om = mm1O + m; - if (matrixH[mm1Om] != 0.0) - { - for (var i = m + 1; i <= high; i++) - { - ort[i] = matrixH[mm1O + i]; - } - - for (var j = m; j <= high; j++) - { - var g = 0.0f; - var jO = j*order; - for (var i = m; i <= high; i++) - { - g += ort[i]*a[jO + i]; - } - - // Double division avoids possible underflow - g = (g/ort[m])/matrixH[mm1Om]; - - for (var i = m; i <= high; i++) - { - a[jO + 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. - /// - internal static void NonsymmetricReduceHessenberToRealSchur(float[] a, float[] matrixH, float[] d, float[] e, int order) - { - // Initialize - var n = order - 1; - var eps = (float) Precision.SinglePrecision; - var exshift = 0.0f; - float p = 0, q = 0, r = 0, s = 0, z = 0; - float 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[j*order + i]); - } - } - - // 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 lm1 = l - 1; - var lm1O = lm1*order; - s = Math.Abs(matrixH[lm1O + lm1]) + Math.Abs(matrixH[l*order + l]); - - if (s == 0.0) - { - s = norm; - } - - if (Math.Abs(matrixH[lm1O + l]) < eps*s) - { - break; - } - - l--; - } - - // Check for convergence - // One root found - if (l == n) - { - var index = n*order + n; - matrixH[index] += exshift; - d[n] = matrixH[index]; - e[n] = 0.0f; - n--; - iter = 0; - - // Two roots found - } - else if (l == n - 1) - { - var nO = n*order; - var nm1 = n - 1; - var nm1O = nm1*order; - var nOn = nO + n; - - w = matrixH[nm1O + n]*matrixH[nO + nm1]; - p = (matrixH[nm1O + nm1] - matrixH[nOn])/2.0f; - q = (p*p) + w; - z = (float) Math.Sqrt(Math.Abs(q)); - - matrixH[nOn] += exshift; - matrixH[nm1O + nm1] += exshift; - x = matrixH[nOn]; - - // Real pair - if (q >= 0) - { - if (p >= 0) - { - z = p + z; - } - else - { - z = p - z; - } - - d[nm1] = x + z; - - d[n] = d[nm1]; - if (z != 0.0) - { - d[n] = x - (w/z); - } - - e[n - 1] = 0.0f; - e[n] = 0.0f; - x = matrixH[nm1O + n]; - 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++) - { - var jO = j*order; - var jOn = jO + n; - z = matrixH[jO + nm1]; - matrixH[jO + nm1] = (q*z) + (p*matrixH[jOn]); - matrixH[jOn] = (q*matrixH[jOn]) - (p*z); - } - - // Column modification - for (var i = 0; i <= n; i++) - { - var nOi = nO + i; - z = matrixH[nm1O + i]; - matrixH[nm1O + i] = (q*z) + (p*matrixH[nOi]); - matrixH[nOi] = (q*matrixH[nOi]) - (p*z); - } - - // Accumulate transformations - for (var i = 0; i < order; i++) - { - var nOi = nO + i; - z = a[nm1O + i]; - a[nm1O + i] = (q*z) + (p*a[nOi]); - a[nOi] = (q*a[nOi]) - (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 - { - var nO = n*order; - var nm1 = n - 1; - var nm1O = nm1*order; - var nOn = nO + n; - - // Form shift - x = matrixH[nOn]; - y = 0.0f; - w = 0.0f; - if (l < n) - { - y = matrixH[nm1O + nm1]; - w = matrixH[nm1O + n]*matrixH[nO + nm1]; - } - - // Wilkinson's original ad hoc shift - if (iter == 10) - { - exshift += x; - for (var i = 0; i <= n; i++) - { - matrixH[i*order + i] -= x; - } - - s = Math.Abs(matrixH[nm1O + n]) + Math.Abs(matrixH[(n - 2)*order + nm1]); - 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*order + i] -= s; - } - - exshift += s; - x = y = w = 0.964f; - } - } - - iter = iter + 1; - if (iter >= 30*order) - { - throw new NonConvergenceException(); - } - - // Look for two consecutive small sub-diagonal elements - var m = n - 2; - while (m >= l) - { - var mp1 = m + 1; - var mm1 = m - 1; - var mO = m*order; - var mp1O = mp1*order; - var mm1O = mm1*order; - - z = matrixH[mO + m]; - r = x - z; - s = y - z; - p = (((r*s) - w)/matrixH[mO + mp1]) + matrixH[mp1O + m]; - q = matrixH[mp1O + mp1] - z - r - s; - r = matrixH[mp1O + (m + 2)]; - 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[mm1O + m])*(Math.Abs(q) + Math.Abs(r)) < eps*(Math.Abs(p)*(Math.Abs(matrixH[mm1O + mm1]) + Math.Abs(z) + Math.Abs(matrixH[mp1O + mp1])))) - { - break; - } - - m--; - } - - var mp2 = m + 2; - for (var i = mp2; i <= n; i++) - { - matrixH[(i - 2)*order + i] = 0.0f; - if (i > mp2) - { - matrixH[(i - 3)*order + i] = 0.0f; - } - } - - // Double QR step involving rows l:n and columns m:n - for (var k = m; k <= n - 1; k++) - { - var notlast = k != n - 1; - var kO = k*order; - var km1 = k - 1; - var kp1 = k + 1; - var kp2 = k + 2; - var kp1O = kp1*order; - var kp2O = kp2*order; - var km1O = km1*order; - if (k != m) - { - p = matrixH[km1O + k]; - q = matrixH[km1O + kp1]; - r = notlast ? matrixH[km1O + kp2] : 0.0f; - x = Math.Abs(p) + Math.Abs(q) + Math.Abs(r); - if (x == 0.0f) - { - continue; - } - - p = p/x; - q = q/x; - r = r/x; - } - - s = (float) Math.Sqrt((p*p) + (q*q) + (r*r)); - - if (p < 0) - { - s = -s; - } - - if (s != 0.0f) - { - if (k != m) - { - matrixH[km1O + k] = (-s)*x; - } - else if (l != m) - { - matrixH[km1O + k] = -matrixH[km1O + k]; - } - - 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++) - { - var jO = j*order; - var jOk = jO + k; - var jOkp1 = jO + kp1; - var jOkp2 = jO + kp2; - p = matrixH[jOk] + (q*matrixH[jOkp1]); - if (notlast) - { - p = p + (r*matrixH[jOkp2]); - matrixH[jOkp2] -= (p*z); - } - - matrixH[jOk] -= (p*x); - matrixH[jOkp1] -= (p*y); - } - - // Column modification - for (var i = 0; i <= Math.Min(n, k + 3); i++) - { - p = (x*matrixH[kO + i]) + (y*matrixH[kp1O + i]); - - if (notlast) - { - p = p + (z*matrixH[kp2O + i]); - matrixH[kp2O + i] -= (p*r); - } - - matrixH[kO + i] -= p; - matrixH[kp1O + i] -= (p*q); - } - - // Accumulate transformations - for (var i = 0; i < order; i++) - { - p = (x*a[kO + i]) + (y*a[kp1O + i]); - - if (notlast) - { - p = p + (z*a[kp2O + i]); - a[kp2O + i] -= p*r; - } - - a[kO + i] -= p; - a[kp1O + 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--) - { - var nO = n*order; - var nm1 = n - 1; - var nm1O = nm1*order; - - p = d[n]; - q = e[n]; - - - // Real vector - float t; - if (q == 0.0f) - { - var l = n; - matrixH[nO + n] = 1.0f; - for (var i = n - 1; i >= 0; i--) - { - var ip1 = i + 1; - var iO = i*order; - var ip1O = ip1*order; - - w = matrixH[iO + i] - p; - r = 0.0f; - for (var j = l; j <= n; j++) - { - r = r + (matrixH[j*order + i]*matrixH[nO + j]); - } - - if (e[i] < 0.0) - { - z = w; - s = r; - } - else - { - l = i; - if (e[i] == 0.0f) - { - if (w != 0.0f) - { - matrixH[nO + i] = (-r)/w; - } - else - { - matrixH[nO + i] = (-r)/(eps*norm); - } - - // Solve real equations - } - else - { - x = matrixH[ip1O + i]; - y = matrixH[iO + ip1]; - q = ((d[i] - p)*(d[i] - p)) + (e[i]*e[i]); - t = ((x*s) - (z*r))/q; - matrixH[nO + i] = t; - if (Math.Abs(x) > Math.Abs(z)) - { - matrixH[nO + ip1] = (-r - (w*t))/x; - } - else - { - matrixH[nO + ip1] = (-s - (y*t))/z; - } - } - - // Overflow control - t = Math.Abs(matrixH[nO + i]); - if ((eps*t)*t > 1) - { - for (var j = i; j <= n; j++) - { - matrixH[nO + j] /= t; - } - } - } - } - - // Complex vector - } - else if (q < 0) - { - var l = n - 1; - - // Last vector component imaginary so matrix is triangular - if (Math.Abs(matrixH[nm1O + n]) > Math.Abs(matrixH[nO + nm1])) - { - matrixH[nm1O + nm1] = q/matrixH[nm1O + n]; - matrixH[nO + nm1] = (-(matrixH[nO + n] - p))/matrixH[nm1O + n]; - } - else - { - var res = Cdiv(0.0f, -matrixH[nO + nm1], matrixH[nm1O + nm1] - p, q); - matrixH[nm1O + nm1] = res.Real; - matrixH[nO + nm1] = res.Imaginary; - } - - matrixH[nm1O + n] = 0.0f; - matrixH[nO + n] = 1.0f; - for (var i = n - 2; i >= 0; i--) - { - var ip1 = i + 1; - var iO = i*order; - var ip1O = ip1*order; - var ra = 0.0f; - var sa = 0.0f; - for (var j = l; j <= n; j++) - { - var jO = j*order; - var jOi = jO + i; - ra = ra + (matrixH[jOi]*matrixH[nm1O + j]); - sa = sa + (matrixH[jOi]*matrixH[nO + j]); - } - - w = matrixH[iO + 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[nm1O + i] = res.Real; - matrixH[nO + i] = res.Imaginary; - } - else - { - // Solve complex equations - x = matrixH[ip1O + i]; - y = matrixH[iO + ip1]; - - var vr = ((d[i] - p)*(d[i] - p)) + (e[i]*e[i]) - (q*q); - var 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[nm1O + i] = res.Real; - matrixH[nO + i] = res.Imaginary; - if (Math.Abs(x) > (Math.Abs(z) + Math.Abs(q))) - { - matrixH[nm1O + ip1] = (-ra - (w*matrixH[nm1O + i]) + (q*matrixH[nO + i]))/x; - matrixH[nO + ip1] = (-sa - (w*matrixH[nO + i]) - (q*matrixH[nm1O + i]))/x; - } - else - { - res = Cdiv(-r - (y*matrixH[nm1O + i]), -s - (y*matrixH[nO + i]), z, q); - matrixH[nm1O + ip1] = res.Real; - matrixH[nO + ip1] = res.Imaginary; - } - } - - // Overflow control - t = Math.Max(Math.Abs(matrixH[nm1O + i]), Math.Abs(matrixH[nO + i])); - if ((eps*t)*t > 1) - { - for (var j = i; j <= n; j++) - { - matrixH[nm1O + j] /= t; - matrixH[nO + j] /= t; - } - } - } - } - } - } - - // Back transformation to get eigenvectors of original matrix - for (var j = order - 1; j >= 0; j--) - { - var jO = j*order; - for (var i = 0; i < order; i++) - { - z = 0.0f; - for (var k = 0; k <= j; k++) - { - z = z + (a[k*order + i]*matrixH[jO + k]); - } - - a[jO + 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. - static Numerics.Complex32 Cdiv(float xreal, float ximag, float yreal, float yimag) - { - if (Math.Abs(yimag) < Math.Abs(yreal)) - { - return new Numerics.Complex32((xreal + (ximag*(yimag/yreal)))/(yreal + (yimag*(yimag/yreal))), (ximag - (xreal*(yimag/yreal)))/(yreal + (yimag*(yimag/yreal)))); - } - - return new Numerics.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. /// diff --git a/src/Numerics/Providers/LinearAlgebra/Managed/ManagedLinearAlgebraProvider.Complex.cs b/src/Numerics/Providers/LinearAlgebra/Managed/ManagedLinearAlgebraProvider.Complex.cs index 6a7d05f4..17d3a3c7 100644 --- a/src/Numerics/Providers/LinearAlgebra/Managed/ManagedLinearAlgebraProvider.Complex.cs +++ b/src/Numerics/Providers/LinearAlgebra/Managed/ManagedLinearAlgebraProvider.Complex.cs @@ -3,7 +3,7 @@ // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // -// Copyright (c) 2009-2018 Math.NET +// Copyright (c) 2009-2020 Math.NET // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation @@ -28,11 +28,10 @@ // using System; -using MathNet.Numerics.LinearAlgebra.Complex.Factorization; -using MathNet.Numerics.LinearAlgebra.Factorization; using MathNet.Numerics.Properties; using MathNet.Numerics.Threading; using Complex = System.Numerics.Complex; +using QRMethod = MathNet.Numerics.LinearAlgebra.Factorization.QRMethod; namespace MathNet.Numerics.Providers.LinearAlgebra.Managed { @@ -2488,9 +2487,9 @@ namespace MathNet.Numerics.Providers.LinearAlgebra.Managed var d = new double[order]; var e = new double[order]; - DenseEvd.SymmetricTridiagonalize(matrixCopy, d, e, tau, order); - DenseEvd.SymmetricDiagonalize(matrixEv, d, e, order); - DenseEvd.SymmetricUntridiagonalize(matrixEv, matrixCopy, tau, order); + SymmetricTridiagonalize(matrixCopy, d, e, tau, order); + SymmetricDiagonalize(matrixEv, d, e, order); + SymmetricUntridiagonalize(matrixEv, matrixCopy, tau, order); for (var i = 0; i < order; i++) { @@ -2499,8 +2498,8 @@ namespace MathNet.Numerics.Providers.LinearAlgebra.Managed } else { - DenseEvd.NonsymmetricReduceToHessenberg(matrixEv, matrixCopy, order); - DenseEvd.NonsymmetricReduceHessenberToRealSchur(vectorEv, matrixEv, matrixCopy, order); + NonsymmetricReduceToHessenberg(matrixEv, matrixCopy, order); + NonsymmetricReduceHessenberToRealSchur(vectorEv, matrixEv, matrixCopy, order); } for (var i = 0; i < order; i++) @@ -2509,6 +2508,724 @@ namespace MathNet.Numerics.Providers.LinearAlgebra.Managed } } + /// + /// 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. + internal 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*order + 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[k*order + i].Real) + Math.Abs(matrixA[k*order + i].Imaginary); + } + + if (scale == 0.0) + { + tau[i - 1] = Complex.One; + e[i] = 0.0; + } + else + { + for (var k = 0; k < i; k++) + { + matrixA[k*order + i] /= scale; + h += matrixA[k*order + i].MagnitudeSquared(); + } + + Complex g = Math.Sqrt(h); + e[i] = scale*g.Real; + + Complex temp; + var im1Oi = (i - 1)*order + i; + var f = matrixA[im1Oi]; + if (f.Magnitude != 0) + { + temp = -(matrixA[im1Oi].Conjugate()*tau[i].Conjugate())/f.Magnitude; + h += f.Magnitude*g.Real; + g = 1.0 + (g/f.Magnitude); + matrixA[im1Oi] *= g; + } + else + { + temp = -tau[i].Conjugate(); + matrixA[im1Oi] = g; + } + + if ((f.Magnitude == 0) || (i != 1)) + { + f = Complex.Zero; + for (var j = 0; j < i; j++) + { + var tmp = Complex.Zero; + var jO = j*order; + // Form element of A*U. + for (var k = 0; k <= j; k++) + { + tmp += matrixA[k*order + j]*matrixA[k*order + i].Conjugate(); + } + + for (var k = j + 1; k <= i - 1; k++) + { + tmp += matrixA[jO + k].Conjugate()*matrixA[k*order + i].Conjugate(); + } + + // Form element of P + tau[j] = tmp/h; + f += (tmp/h)*matrixA[jO + i]; + } + + hh = f.Real/(h + h); + + // Form the reduced A. + for (var j = 0; j < i; j++) + { + f = matrixA[j*order + i].Conjugate(); + g = tau[j] - (hh*f); + tau[j] = g.Conjugate(); + + for (var k = 0; k <= j; k++) + { + matrixA[k*order + j] -= (f*tau[k]) + (g*matrixA[k*order + i]); + } + } + } + + for (var k = 0; k < i; k++) + { + matrixA[k*order + i] *= scale; + } + + tau[i - 1] = temp.Conjugate(); + } + + hh = d[i]; + d[i] = matrixA[i*order + i].Real; + matrixA[i*order + i] = new Complex(hh, scale*Math.Sqrt(h)); + } + + hh = d[0]; + d[0] = matrixA[0].Real; + matrixA[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. + /// + internal 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.DoublePrecision; + 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 NonConvergenceException(); + } + } 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 SymmetricTridiagonalize. + /// 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. + internal 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*order + 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[k*order + i]; + } + + s = (s/h)/h; + + for (var k = 0; k < i; k++) + { + dataEv[(j*order) + k] -= s*matrixA[k*order + i].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. + internal 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; + var mm1O = (m - 1)*order; + for (var i = m; i < order; i++) + { + scale += Math.Abs(matrixH[mm1O + i].Real) + Math.Abs(matrixH[mm1O + i].Imaginary); + } + + if (scale != 0.0) + { + // Compute Householder transformation. + var h = 0.0; + for (var i = order - 1; i >= m; i--) + { + ort[i] = matrixH[mm1O + i]/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[mm1O + m] = 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; + var jO = j*order; + for (var i = order - 1; i >= m; i--) + { + f += ort[i].Conjugate()*matrixH[jO + i]; + } + + f = f/h; + for (var i = m; i < order; i++) + { + matrixH[jO + i] -= 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[j*order + i]; + } + + f = f/h; + for (var j = m; j < order; j++) + { + matrixH[j*order + i] -= f*ort[j].Conjugate(); + } + } + + ort[m] = scale*ort[m]; + matrixH[mm1O + m] *= -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--) + { + var mm1O = (m - 1)*order; + var mm1Om = mm1O + m; + if (matrixH[mm1Om] != Complex.Zero && ort[m] != Complex.Zero) + { + var norm = (matrixH[mm1Om].Real*ort[m].Real) + (matrixH[mm1Om].Imaginary*ort[m].Imaginary); + + for (var i = m + 1; i < order; i++) + { + ort[i] = matrixH[mm1O + i]; + } + + 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++) + { + var im1 = i - 1; + var im1O = im1*order; + var im1Oi = im1O + i; + var iO = i*order; + if (matrixH[im1Oi].Imaginary != 0.0) + { + var y = matrixH[im1Oi]/matrixH[im1Oi].Magnitude; + matrixH[im1Oi] = matrixH[im1Oi].Magnitude; + for (var j = i; j < order; j++) + { + matrixH[j*order + i] *= y.Conjugate(); + } + + for (var j = 0; j <= Math.Min(i + 1, order - 1); j++) + { + matrixH[iO + j] *= 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. + internal static void NonsymmetricReduceHessenberToRealSchur(Complex[] vectorV, Complex[] dataEv, Complex[] matrixH, int order) + { + // Initialize + var n = order - 1; + var eps = Precision.DoublePrecision; + + double norm; + Complex 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 lm1 = l - 1; + var lm1O = lm1*order; + var lO = l*order; + var tst1 = Math.Abs(matrixH[lm1O + lm1].Real) + Math.Abs(matrixH[lm1O + lm1].Imaginary) + Math.Abs(matrixH[lO + l].Real) + Math.Abs(matrixH[lO + l].Imaginary); + if (Math.Abs(matrixH[lm1O + l].Real) < eps*tst1) + { + break; + } + + l--; + } + + var nm1 = n - 1; + var nm1O = nm1*order; + var nO = n*order; + var nOn = nO + n; + // Check for convergence + // One root found + if (l == n) + { + matrixH[nOn] += exshift; + vectorV[n] = matrixH[nOn]; + n--; + iter = 0; + } + else + { + // Form shift + Complex s; + if (iter != 10 && iter != 20) + { + s = matrixH[nOn]; + x = matrixH[nO + nm1]*matrixH[nm1O + n].Real; + + if (x.Real != 0.0 || x.Imaginary != 0.0) + { + y = (matrixH[nm1O + nm1] - 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[nm1O + n].Real) + Math.Abs(matrixH[(n - 2)*order + nm1].Real); + } + + for (var i = 0; i <= n; i++) + { + matrixH[i*order + i] -= s; + } + + exshift += s; + iter++; + + // Reduce to triangle (rows) + for (var i = l + 1; i <= n; i++) + { + var im1 = i - 1; + var im1O = im1*order; + var im1Oim1 = im1O + im1; + s = matrixH[im1O + i].Real; + norm = SpecialFunctions.Hypotenuse(matrixH[im1Oim1].Magnitude, s.Real); + x = matrixH[im1Oim1]/norm; + vectorV[i - 1] = x; + matrixH[im1Oim1] = norm; + matrixH[im1O + i] = new Complex(0.0, s.Real/norm); + + for (var j = i; j < order; j++) + { + var jO = j*order; + y = matrixH[jO + im1]; + z = matrixH[jO + i]; + matrixH[jO + im1] = (x.Conjugate()*y) + (matrixH[im1O + i].Imaginary*z); + matrixH[jO + i] = (x*z) - (matrixH[im1O + i].Imaginary*y); + } + } + + s = matrixH[nOn]; + if (s.Imaginary != 0.0) + { + s /= matrixH[nOn].Magnitude; + matrixH[nOn] = matrixH[nOn].Magnitude; + + for (var j = n + 1; j < order; j++) + { + matrixH[j*order + n] *= s.Conjugate(); + } + } + + // Inverse operation (columns). + for (var j = l + 1; j <= n; j++) + { + x = vectorV[j - 1]; + var jO = j*order; + var jm1 = j - 1; + var jm1O = jm1*order; + var jm1Oj = jm1O + j; + for (var i = 0; i <= j; i++) + { + var jm1Oi = jm1O + i; + z = matrixH[jO + i]; + if (i != j) + { + y = matrixH[jm1Oi]; + matrixH[jm1Oi] = (x*y) + (matrixH[jm1O + j].Imaginary*z); + } + else + { + y = matrixH[jm1Oi].Real; + matrixH[jm1Oi] = new Complex((x.Real*y.Real) - (x.Imaginary*y.Imaginary) + (matrixH[jm1O + j].Imaginary*z.Real), matrixH[jm1Oi].Imaginary); + } + + matrixH[jO + i] = (x.Conjugate()*z) - (matrixH[jm1O + j].Imaginary*y); + } + + for (var i = 0; i < order; i++) + { + y = dataEv[((j - 1)*order) + i]; + z = dataEv[(j*order) + i]; + dataEv[jm1O + i] = (x*y) + (matrixH[jm1Oj].Imaginary*z); + dataEv[jO + i] = (x.Conjugate()*z) - (matrixH[jm1Oj].Imaginary*y); + } + } + + if (s.Imaginary != 0.0) + { + for (var i = 0; i <= n; i++) + { + matrixH[nO + i] *= s; + } + + for (var i = 0; i < order; i++) + { + dataEv[nO + 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[j*order + i].Real) + Math.Abs(matrixH[j*order + i].Imaginary)); + } + } + + if (order == 1) + { + return; + } + + if (norm == 0.0) + { + return; + } + + for (n = order - 1; n > 0; n--) + { + var nO = n*order; + var nOn = nO + n; + x = vectorV[n]; + matrixH[nOn] = 1.0; + + for (var i = n - 1; i >= 0; i--) + { + z = 0.0; + for (var j = i + 1; j <= n; j++) + { + z += matrixH[j*order + i]*matrixH[nO + j]; + } + + y = x - vectorV[i]; + if (y.Real == 0.0 && y.Imaginary == 0.0) + { + y = eps*norm; + } + + matrixH[nO + i] = z/y; + + // Overflow control + var tr = Math.Abs(matrixH[nO + i].Real) + Math.Abs(matrixH[nO + i].Imaginary); + if ((eps*tr)*tr > 1) + { + for (var j = i; j <= n; j++) + { + matrixH[nO + j] = matrixH[nO + j]/tr; + } + } + } + } + + // Back transformation to get eigenvectors of original matrix + for (var j = order - 1; j > 0; j--) + { + var jO = j*order; + for (var i = 0; i < order; i++) + { + z = Complex.Zero; + for (var k = 0; k <= j; k++) + { + z += dataEv[(k*order) + i]*matrixH[jO + k]; + } + + dataEv[jO + i] = z; + } + } + } + /// /// Assumes that and have already been transposed. /// diff --git a/src/Numerics/Providers/LinearAlgebra/Managed/ManagedLinearAlgebraProvider.Complex32.cs b/src/Numerics/Providers/LinearAlgebra/Managed/ManagedLinearAlgebraProvider.Complex32.cs index d9067ede..d2c8d351 100644 --- a/src/Numerics/Providers/LinearAlgebra/Managed/ManagedLinearAlgebraProvider.Complex32.cs +++ b/src/Numerics/Providers/LinearAlgebra/Managed/ManagedLinearAlgebraProvider.Complex32.cs @@ -3,7 +3,7 @@ // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // -// Copyright (c) 2009-2018 Math.NET +// Copyright (c) 2009-2020 Math.NET // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation @@ -28,12 +28,10 @@ // using System; -using MathNet.Numerics.LinearAlgebra.Complex32; -using MathNet.Numerics.LinearAlgebra.Complex32.Factorization; -using MathNet.Numerics.LinearAlgebra.Factorization; using MathNet.Numerics.Properties; using MathNet.Numerics.Threading; using Complex = System.Numerics.Complex; +using QRMethod = MathNet.Numerics.LinearAlgebra.Factorization.QRMethod; namespace MathNet.Numerics.Providers.LinearAlgebra.Managed { @@ -2487,9 +2485,9 @@ namespace MathNet.Numerics.Providers.LinearAlgebra.Managed var d = new float[order]; var e = new float[order]; - DenseEvd.SymmetricTridiagonalize(matrixCopy, d, e, tau, order); - DenseEvd.SymmetricDiagonalize(matrixEv, d, e, order); - DenseEvd.SymmetricUntridiagonalize(matrixEv, matrixCopy, tau, order); + SymmetricTridiagonalize(matrixCopy, d, e, tau, order); + SymmetricDiagonalize(matrixEv, d, e, order); + SymmetricUntridiagonalize(matrixEv, matrixCopy, tau, order); for (var i = 0; i < order; i++) { @@ -2499,10 +2497,11 @@ namespace MathNet.Numerics.Providers.LinearAlgebra.Managed } else { - var v = new DenseVector(order); + var v = new Complex32[order]; + + NonsymmetricReduceToHessenberg(matrixEv, matrixCopy, order); + NonsymmetricReduceHessenberToRealSchur(v, matrixEv, matrixCopy, order); - DenseEvd.NonsymmetricReduceToHessenberg(matrixEv, matrixCopy, order); - DenseEvd.NonsymmetricReduceHessenberToRealSchur(v.Values, matrixEv, matrixCopy, order); for (var i = 0; i < order; i++) { vectorEv[i] = new Complex(v[i].Real, v[i].Imaginary); @@ -2511,6 +2510,724 @@ namespace MathNet.Numerics.Providers.LinearAlgebra.Managed } } + /// + /// 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. + internal 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*order + 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[k*order + i].Real) + Math.Abs(matrixA[k*order + i].Imaginary); + } + + if (scale == 0.0f) + { + tau[i - 1] = Complex32.One; + e[i] = 0.0f; + } + else + { + for (var k = 0; k < i; k++) + { + matrixA[k*order + i] /= scale; + h += matrixA[k*order + i].MagnitudeSquared; + } + + Complex32 g = (float) Math.Sqrt(h); + e[i] = scale*g.Real; + + Complex32 temp; + var im1Oi = (i - 1)*order + i; + var f = matrixA[im1Oi]; + if (f.Magnitude != 0.0f) + { + temp = -(matrixA[im1Oi].Conjugate()*tau[i].Conjugate())/f.Magnitude; + h += f.Magnitude*g.Real; + g = 1.0f + (g/f.Magnitude); + matrixA[im1Oi] *= g; + } + else + { + temp = -tau[i].Conjugate(); + matrixA[im1Oi] = g; + } + + if ((f.Magnitude == 0.0f) || (i != 1)) + { + f = Complex32.Zero; + for (var j = 0; j < i; j++) + { + var tmp = Complex32.Zero; + var jO = j*order; + // Form element of A*U. + for (var k = 0; k <= j; k++) + { + tmp += matrixA[k*order + j]*matrixA[k*order + i].Conjugate(); + } + + for (var k = j + 1; k <= i - 1; k++) + { + tmp += matrixA[jO + k].Conjugate()*matrixA[k*order + i].Conjugate(); + } + + // Form element of P + tau[j] = tmp/h; + f += (tmp/h)*matrixA[jO + i]; + } + + hh = f.Real/(h + h); + + // Form the reduced A. + for (var j = 0; j < i; j++) + { + f = matrixA[j*order + i].Conjugate(); + g = tau[j] - (hh*f); + tau[j] = g.Conjugate(); + + for (var k = 0; k <= j; k++) + { + matrixA[k*order + j] -= (f*tau[k]) + (g*matrixA[k*order + i]); + } + } + } + + for (var k = 0; k < i; k++) + { + matrixA[k*order + i] *= scale; + } + + tau[i - 1] = temp.Conjugate(); + } + + hh = d[i]; + d[i] = matrixA[i*order + i].Real; + matrixA[i*order + i] = new Complex32(hh, scale*(float) Math.Sqrt(h)); + } + + hh = d[0]; + d[0] = matrixA[0].Real; + matrixA[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. + /// + internal 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.DoublePrecision; + 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 NonConvergenceException(); + } + } 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 SymmetricTridiagonalize. + /// 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. + internal 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*order + 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[k*order + i]; + } + + s = (s/h)/h; + + for (var k = 0; k < i; k++) + { + dataEv[(j*order) + k] -= s*matrixA[k*order + i].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. + internal 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; + var mm1O = (m - 1)*order; + for (var i = m; i < order; i++) + { + scale += Math.Abs(matrixH[mm1O + i].Real) + Math.Abs(matrixH[mm1O + i].Imaginary); + } + + if (scale != 0.0f) + { + // Compute Householder transformation. + var h = 0.0f; + for (var i = order - 1; i >= m; i--) + { + ort[i] = matrixH[mm1O + i]/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[mm1O + m] = 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; + var jO = j*order; + for (var i = order - 1; i >= m; i--) + { + f += ort[i].Conjugate()*matrixH[jO + i]; + } + + f = f/h; + for (var i = m; i < order; i++) + { + matrixH[jO + i] -= 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[j*order + i]; + } + + f = f/h; + for (var j = m; j < order; j++) + { + matrixH[j*order + i] -= f*ort[j].Conjugate(); + } + } + + ort[m] = scale*ort[m]; + matrixH[mm1O + m] *= -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--) + { + var mm1O = (m - 1)*order; + var mm1Om = mm1O + m; + if (matrixH[mm1Om] != Complex32.Zero && ort[m] != Complex32.Zero) + { + var norm = (matrixH[mm1Om].Real*ort[m].Real) + (matrixH[mm1Om].Imaginary*ort[m].Imaginary); + + for (var i = m + 1; i < order; i++) + { + ort[i] = matrixH[mm1O + i]; + } + + 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++) + { + var im1 = i - 1; + var im1O = im1*order; + var im1Oi = im1O + i; + var iO = i*order; + if (matrixH[im1Oi].Imaginary != 0.0f) + { + var y = matrixH[im1Oi]/matrixH[im1Oi].Magnitude; + matrixH[im1Oi] = matrixH[im1Oi].Magnitude; + for (var j = i; j < order; j++) + { + matrixH[j*order + i] *= y.Conjugate(); + } + + for (var j = 0; j <= Math.Min(i + 1, order - 1); j++) + { + matrixH[iO + j] *= 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. + internal static void NonsymmetricReduceHessenberToRealSchur(Complex32[] vectorV, Complex32[] dataEv, Complex32[] matrixH, int order) + { + // Initialize + var n = order - 1; + var eps = (float) Precision.SinglePrecision; + + float norm; + Complex32 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 lm1 = l - 1; + var lm1O = lm1*order; + var lO = l*order; + var tst1 = Math.Abs(matrixH[lm1O + lm1].Real) + Math.Abs(matrixH[lm1O + lm1].Imaginary) + Math.Abs(matrixH[lO + l].Real) + Math.Abs(matrixH[lO + l].Imaginary); + if (Math.Abs(matrixH[lm1O + l].Real) < eps*tst1) + { + break; + } + + l--; + } + + var nm1 = n - 1; + var nm1O = nm1*order; + var nO = n*order; + var nOn = nO + n; + // Check for convergence + // One root found + if (l == n) + { + matrixH[nOn] += exshift; + vectorV[n] = matrixH[nOn]; + n--; + iter = 0; + } + else + { + // Form shift + Complex32 s; + if (iter != 10 && iter != 20) + { + s = matrixH[nOn]; + x = matrixH[nO + nm1]*matrixH[nm1O + n].Real; + + if (x.Real != 0.0f || x.Imaginary != 0.0f) + { + y = (matrixH[nm1O + nm1] - s)/2.0f; + z = ((y*y) + x).SquareRoot(); + if ((y.Real*z.Real) + (y.Imaginary*z.Imaginary) < 0.0) + { + z *= -1.0f; + } + + x /= y + z; + s = s - x; + } + } + else + { + // Form exceptional shift + s = Math.Abs(matrixH[nm1O + n].Real) + Math.Abs(matrixH[(n - 2)*order + nm1].Real); + } + + for (var i = 0; i <= n; i++) + { + matrixH[i*order + i] -= s; + } + + exshift += s; + iter++; + + // Reduce to triangle (rows) + for (var i = l + 1; i <= n; i++) + { + var im1 = i - 1; + var im1O = im1*order; + var im1Oim1 = im1O + im1; + s = matrixH[im1O + i].Real; + norm = SpecialFunctions.Hypotenuse(matrixH[im1Oim1].Magnitude, s.Real); + x = matrixH[im1Oim1]/norm; + vectorV[i - 1] = x; + matrixH[im1Oim1] = norm; + matrixH[im1O + i] = new Complex32(0.0f, s.Real/norm); + + for (var j = i; j < order; j++) + { + var jO = j*order; + y = matrixH[jO + im1]; + z = matrixH[jO + i]; + matrixH[jO + im1] = (x.Conjugate()*y) + (matrixH[im1O + i].Imaginary*z); + matrixH[jO + i] = (x*z) - (matrixH[im1O + i].Imaginary*y); + } + } + + s = matrixH[nOn]; + if (s.Imaginary != 0.0f) + { + s /= matrixH[nOn].Magnitude; + matrixH[nOn] = matrixH[nOn].Magnitude; + + for (var j = n + 1; j < order; j++) + { + matrixH[j*order + n] *= s.Conjugate(); + } + } + + // Inverse operation (columns). + for (var j = l + 1; j <= n; j++) + { + x = vectorV[j - 1]; + var jO = j*order; + var jm1 = j - 1; + var jm1O = jm1*order; + var jm1Oj = jm1O + j; + for (var i = 0; i <= j; i++) + { + var jm1Oi = jm1O + i; + z = matrixH[jO + i]; + if (i != j) + { + y = matrixH[jm1Oi]; + matrixH[jm1Oi] = (x*y) + (matrixH[jm1O + j].Imaginary*z); + } + else + { + y = matrixH[jm1Oi].Real; + matrixH[jm1Oi] = new Complex32((x.Real*y.Real) - (x.Imaginary*y.Imaginary) + (matrixH[jm1O + j].Imaginary*z.Real), matrixH[jm1Oi].Imaginary); + } + + matrixH[jO + i] = (x.Conjugate()*z) - (matrixH[jm1O + j].Imaginary*y); + } + + for (var i = 0; i < order; i++) + { + y = dataEv[((j - 1)*order) + i]; + z = dataEv[(j*order) + i]; + dataEv[jm1O + i] = (x*y) + (matrixH[jm1Oj].Imaginary*z); + dataEv[jO + i] = (x.Conjugate()*z) - (matrixH[jm1Oj].Imaginary*y); + } + } + + if (s.Imaginary != 0.0f) + { + for (var i = 0; i <= n; i++) + { + matrixH[nO + i] *= s; + } + + for (var i = 0; i < order; i++) + { + dataEv[nO + 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[j*order + i].Real) + Math.Abs(matrixH[j*order + i].Imaginary)); + } + } + + if (order == 1) + { + return; + } + + if (norm == 0.0) + { + return; + } + + for (n = order - 1; n > 0; n--) + { + var nO = n*order; + var nOn = nO + n; + x = vectorV[n]; + matrixH[nOn] = 1.0f; + + for (var i = n - 1; i >= 0; i--) + { + z = 0.0f; + for (var j = i + 1; j <= n; j++) + { + z += matrixH[j*order + i]*matrixH[nO + j]; + } + + y = x - vectorV[i]; + if (y.Real == 0.0f && y.Imaginary == 0.0f) + { + y = eps*norm; + } + + matrixH[nO + i] = z/y; + + // Overflow control + var tr = Math.Abs(matrixH[nO + i].Real) + Math.Abs(matrixH[nO + i].Imaginary); + if ((eps*tr)*tr > 1) + { + for (var j = i; j <= n; j++) + { + matrixH[nO + j] = matrixH[nO + j]/tr; + } + } + } + } + + // Back transformation to get eigenvectors of original matrix + for (var j = order - 1; j > 0; j--) + { + var jO = j*order; + for (var i = 0; i < order; i++) + { + z = Complex32.Zero; + for (var k = 0; k <= j; k++) + { + z += dataEv[(k*order) + i]*matrixH[jO + k]; + } + + dataEv[jO + i] = z; + } + } + } + /// /// Assumes that and have already been transposed. /// diff --git a/src/Numerics/Providers/LinearAlgebra/Managed/ManagedLinearAlgebraProvider.Double.cs b/src/Numerics/Providers/LinearAlgebra/Managed/ManagedLinearAlgebraProvider.Double.cs index 8ab8ee09..fa628798 100644 --- a/src/Numerics/Providers/LinearAlgebra/Managed/ManagedLinearAlgebraProvider.Double.cs +++ b/src/Numerics/Providers/LinearAlgebra/Managed/ManagedLinearAlgebraProvider.Double.cs @@ -3,7 +3,7 @@ // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // -// Copyright (c) 2009-2018 Math.NET +// Copyright (c) 2009-2020 Math.NET // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation @@ -28,10 +28,10 @@ // using System; -using MathNet.Numerics.LinearAlgebra.Factorization; using MathNet.Numerics.Properties; using MathNet.Numerics.Threading; using Complex = System.Numerics.Complex; +using QRMethod = MathNet.Numerics.LinearAlgebra.Factorization.QRMethod; namespace MathNet.Numerics.Providers.LinearAlgebra.Managed { @@ -2548,15 +2548,15 @@ namespace MathNet.Numerics.Providers.LinearAlgebra.Managed d[i] = matrixEv[i*order + om1]; } - Numerics.LinearAlgebra.Double.Factorization.DenseEvd.SymmetricTridiagonalize(matrixEv, d, e, order); - Numerics.LinearAlgebra.Double.Factorization.DenseEvd.SymmetricDiagonalize(matrixEv, d, e, order); + SymmetricTridiagonalize(matrixEv, d, e, order); + SymmetricDiagonalize(matrixEv, d, e, order); } else { var matrixH = new double[matrix.Length]; Buffer.BlockCopy(matrix, 0, matrixH, 0, matrix.Length*Constants.SizeOfDouble); - Numerics.LinearAlgebra.Double.Factorization.DenseEvd.NonsymmetricReduceToHessenberg(matrixEv, matrixH, order); - Numerics.LinearAlgebra.Double.Factorization.DenseEvd.NonsymmetricReduceHessenberToRealSchur(matrixEv, matrixH, d, e, order); + NonsymmetricReduceToHessenberg(matrixEv, matrixH, order); + NonsymmetricReduceHessenberToRealSchur(matrixEv, matrixH, d, e, order); } for (var i = 0; i < order; i++) @@ -2577,5 +2577,1015 @@ namespace MathNet.Numerics.Providers.LinearAlgebra.Managed } } } + + /// + /// 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. + internal 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. + /// + internal 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.DoublePrecision; + 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 NonConvergenceException(); + } + } 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. + internal static void NonsymmetricReduceToHessenberg(double[] a, double[] matrixH, int order) + { + var ort = new double[order]; + var high = order - 1; + for (var m = 1; m <= high - 1; m++) + { + var mm1 = m - 1; + var mm1O = mm1*order; + // Scale column. + var scale = 0.0; + for (var i = m; i <= high; i++) + { + scale += Math.Abs(matrixH[mm1O + i]); + } + + if (scale != 0.0) + { + // Compute Householder transformation. + var h = 0.0; + for (var i = high; i >= m; i--) + { + ort[i] = matrixH[mm1O + i]/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 jO = j*order; + var f = 0.0; + for (var i = order - 1; i >= m; i--) + { + f += ort[i]*matrixH[jO + i]; + } + + f = f/h; + + for (var i = m; i <= high; i++) + { + matrixH[jO + i] -= f*ort[i]; + } + } + + for (var i = 0; i <= high; i++) + { + var f = 0.0; + for (var j = high; j >= m; j--) + { + f += ort[j]*matrixH[j*order + i]; + } + f = f/h; + + for (var j = m; j <= high; j++) + { + matrixH[j*order + i] -= f*ort[j]; + } + } + + ort[m] = scale*ort[m]; + matrixH[mm1O + m] = 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 = high - 1; m >= 1; m--) + { + var mm1 = m - 1; + var mm1O = mm1*order; + var mm1Om = mm1O + m; + if (matrixH[mm1Om] != 0.0) + { + for (var i = m + 1; i <= high; i++) + { + ort[i] = matrixH[mm1O + i]; + } + + for (var j = m; j <= high; j++) + { + var g = 0.0; + var jO = j*order; + for (var i = m; i <= high; i++) + { + g += ort[i]*a[jO + i]; + } + + // Double division avoids possible underflow + g = (g/ort[m])/matrixH[mm1Om]; + + for (var i = m; i <= high; i++) + { + a[jO + 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. + /// + internal static void NonsymmetricReduceHessenberToRealSchur(double[] a, double[] matrixH, double[] d, double[] e, int order) + { + // Initialize + var n = order - 1; + var eps = Math.Pow(2.0, -52.0); + var exshift = 0.0; + double p = 0, q = 0, r = 0, s = 0, z = 0; + double 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[j*order + i]); + } + } + + // 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 lm1 = l - 1; + var lm1O = lm1*order; + s = Math.Abs(matrixH[lm1O + lm1]) + Math.Abs(matrixH[l*order + l]); + + if (s == 0.0) + { + s = norm; + } + + if (Math.Abs(matrixH[lm1O + l]) < eps*s) + { + break; + } + + l--; + } + + // Check for convergence + // One root found + if (l == n) + { + var index = n*order + n; + matrixH[index] += exshift; + d[n] = matrixH[index]; + e[n] = 0.0; + n--; + iter = 0; + + // Two roots found + } + else if (l == n - 1) + { + var nO = n*order; + var nm1 = n - 1; + var nm1O = nm1*order; + var nOn = nO + n; + + w = matrixH[nm1O + n]*matrixH[nO + nm1]; + p = (matrixH[nm1O + nm1] - matrixH[nOn])/2.0; + q = (p*p) + w; + z = Math.Sqrt(Math.Abs(q)); + + matrixH[nOn] += exshift; + matrixH[nm1O + nm1] += exshift; + x = matrixH[nOn]; + + // Real pair + if (q >= 0) + { + if (p >= 0) + { + z = p + z; + } + else + { + z = p - z; + } + + d[nm1] = x + z; + + d[n] = d[nm1]; + if (z != 0.0) + { + d[n] = x - (w/z); + } + + e[n - 1] = 0.0; + e[n] = 0.0; + x = matrixH[nm1O + n]; + 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++) + { + var jO = j*order; + var jOn = jO + n; + z = matrixH[jO + nm1]; + matrixH[jO + nm1] = (q*z) + (p*matrixH[jOn]); + matrixH[jOn] = (q*matrixH[jOn]) - (p*z); + } + + // Column modification + for (var i = 0; i <= n; i++) + { + var nOi = nO + i; + z = matrixH[nm1O + i]; + matrixH[nm1O + i] = (q*z) + (p*matrixH[nOi]); + matrixH[nOi] = (q*matrixH[nOi]) - (p*z); + } + + // Accumulate transformations + for (var i = 0; i < order; i++) + { + var nOi = nO + i; + z = a[nm1O + i]; + a[nm1O + i] = (q*z) + (p*a[nOi]); + a[nOi] = (q*a[nOi]) - (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 + { + var nO = n*order; + var nm1 = n - 1; + var nm1O = nm1*order; + var nOn = nO + n; + + // Form shift + x = matrixH[nOn]; + y = 0.0; + w = 0.0; + if (l < n) + { + y = matrixH[nm1O + nm1]; + w = matrixH[nm1O + n]*matrixH[nO + nm1]; + } + + // Wilkinson's original ad hoc shift + if (iter == 10) + { + exshift += x; + for (var i = 0; i <= n; i++) + { + matrixH[i*order + i] -= x; + } + + s = Math.Abs(matrixH[nm1O + n]) + Math.Abs(matrixH[(n - 2)*order + nm1]); + 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*order + i] -= s; + } + + exshift += s; + x = y = w = 0.964; + } + } + + iter = iter + 1; + if (iter >= 30*order) + { + throw new NonConvergenceException(); + } + + // Look for two consecutive small sub-diagonal elements + var m = n - 2; + while (m >= l) + { + var mp1 = m + 1; + var mm1 = m - 1; + var mO = m*order; + var mp1O = mp1*order; + var mm1O = mm1*order; + + z = matrixH[mO + m]; + r = x - z; + s = y - z; + p = (((r*s) - w)/matrixH[mO + mp1]) + matrixH[mp1O + m]; + q = matrixH[mp1O + mp1] - z - r - s; + r = matrixH[mp1O + (m + 2)]; + 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[mm1O + m])*(Math.Abs(q) + Math.Abs(r)) < eps*(Math.Abs(p)*(Math.Abs(matrixH[mm1O + mm1]) + Math.Abs(z) + Math.Abs(matrixH[mp1O + mp1])))) + { + break; + } + + m--; + } + + var mp2 = m + 2; + for (var i = mp2; i <= n; i++) + { + matrixH[(i - 2)*order + i] = 0.0; + if (i > mp2) + { + matrixH[(i - 3)*order + i] = 0.0; + } + } + + // Double QR step involving rows l:n and columns m:n + for (var k = m; k <= n - 1; k++) + { + var notlast = k != n - 1; + var kO = k*order; + var km1 = k - 1; + var kp1 = k + 1; + var kp2 = k + 2; + var kp1O = kp1*order; + var kp2O = kp2*order; + var km1O = km1*order; + if (k != m) + { + p = matrixH[km1O + k]; + q = matrixH[km1O + kp1]; + r = notlast ? matrixH[km1O + kp2] : 0.0; + x = Math.Abs(p) + Math.Abs(q) + Math.Abs(r); + if (x == 0.0) + { + continue; + } + + p = p/x; + q = q/x; + r = r/x; + } + + s = Math.Sqrt((p*p) + (q*q) + (r*r)); + + if (p < 0) + { + s = -s; + } + + if (s != 0.0) + { + if (k != m) + { + matrixH[km1O + k] = (-s)*x; + } + else if (l != m) + { + matrixH[km1O + k] = -matrixH[km1O + k]; + } + + 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++) + { + var jO = j*order; + var jOk = jO + k; + var jOkp1 = jO + kp1; + var jOkp2 = jO + kp2; + p = matrixH[jOk] + (q*matrixH[jOkp1]); + if (notlast) + { + p = p + (r*matrixH[jOkp2]); + matrixH[jOkp2] -= (p*z); + } + + matrixH[jOk] -= (p*x); + matrixH[jOkp1] -= (p*y); + } + + // Column modification + for (var i = 0; i <= Math.Min(n, k + 3); i++) + { + p = (x*matrixH[kO + i]) + (y*matrixH[kp1O + i]); + + if (notlast) + { + p = p + (z*matrixH[kp2O + i]); + matrixH[kp2O + i] -= (p*r); + } + + matrixH[kO + i] -= p; + matrixH[kp1O + i] -= (p*q); + } + + // Accumulate transformations + for (var i = 0; i < order; i++) + { + p = (x*a[kO + i]) + (y*a[kp1O + i]); + + if (notlast) + { + p = p + (z*a[kp2O + i]); + a[kp2O + i] -= p*r; + } + + a[kO + i] -= p; + a[kp1O + 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--) + { + var nO = n*order; + var nm1 = n - 1; + var nm1O = nm1*order; + + p = d[n]; + q = e[n]; + + + // Real vector + double t; + if (q == 0.0) + { + var l = n; + matrixH[nO + n] = 1.0; + for (var i = n - 1; i >= 0; i--) + { + var ip1 = i + 1; + var iO = i*order; + var ip1O = ip1*order; + + w = matrixH[iO + i] - p; + r = 0.0; + for (var j = l; j <= n; j++) + { + r = r + (matrixH[j*order + i]*matrixH[nO + j]); + } + + if (e[i] < 0.0) + { + z = w; + s = r; + } + else + { + l = i; + if (e[i] == 0.0) + { + if (w != 0.0) + { + matrixH[nO + i] = (-r)/w; + } + else + { + matrixH[nO + i] = (-r)/(eps*norm); + } + + // Solve real equations + } + else + { + x = matrixH[ip1O + i]; + y = matrixH[iO + ip1]; + q = ((d[i] - p)*(d[i] - p)) + (e[i]*e[i]); + t = ((x*s) - (z*r))/q; + matrixH[nO + i] = t; + if (Math.Abs(x) > Math.Abs(z)) + { + matrixH[nO + ip1] = (-r - (w*t))/x; + } + else + { + matrixH[nO + ip1] = (-s - (y*t))/z; + } + } + + // Overflow control + t = Math.Abs(matrixH[nO + i]); + if ((eps*t)*t > 1) + { + for (var j = i; j <= n; j++) + { + matrixH[nO + j] /= t; + } + } + } + } + + // Complex vector + } + else if (q < 0) + { + var l = n - 1; + + // Last vector component imaginary so matrix is triangular + if (Math.Abs(matrixH[nm1O + n]) > Math.Abs(matrixH[nO + nm1])) + { + matrixH[nm1O + nm1] = q/matrixH[nm1O + n]; + matrixH[nO + nm1] = (-(matrixH[nO + n] - p))/matrixH[nm1O + n]; + } + else + { + var res = Cdiv(0.0, -matrixH[nO + nm1], matrixH[nm1O + nm1] - p, q); + matrixH[nm1O + nm1] = res.Real; + matrixH[nO + nm1] = res.Imaginary; + } + + matrixH[nm1O + n] = 0.0; + matrixH[nO + n] = 1.0; + for (var i = n - 2; i >= 0; i--) + { + var ip1 = i + 1; + var iO = i*order; + var ip1O = ip1*order; + var ra = 0.0; + var sa = 0.0; + for (var j = l; j <= n; j++) + { + var jO = j*order; + var jOi = jO + i; + ra = ra + (matrixH[jOi]*matrixH[nm1O + j]); + sa = sa + (matrixH[jOi]*matrixH[nO + j]); + } + + w = matrixH[iO + 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[nm1O + i] = res.Real; + matrixH[nO + i] = res.Imaginary; + } + else + { + // Solve complex equations + x = matrixH[ip1O + i]; + y = matrixH[iO + ip1]; + + var vr = ((d[i] - p)*(d[i] - p)) + (e[i]*e[i]) - (q*q); + var 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[nm1O + i] = res.Real; + matrixH[nO + i] = res.Imaginary; + if (Math.Abs(x) > (Math.Abs(z) + Math.Abs(q))) + { + matrixH[nm1O + ip1] = (-ra - (w*matrixH[nm1O + i]) + (q*matrixH[nO + i]))/x; + matrixH[nO + ip1] = (-sa - (w*matrixH[nO + i]) - (q*matrixH[nm1O + i]))/x; + } + else + { + res = Cdiv(-r - (y*matrixH[nm1O + i]), -s - (y*matrixH[nO + i]), z, q); + matrixH[nm1O + ip1] = res.Real; + matrixH[nO + ip1] = res.Imaginary; + } + } + + // Overflow control + t = Math.Max(Math.Abs(matrixH[nm1O + i]), Math.Abs(matrixH[nO + i])); + if ((eps*t)*t > 1) + { + for (var j = i; j <= n; j++) + { + matrixH[nm1O + j] /= t; + matrixH[nO + j] /= t; + } + } + } + } + } + } + + // Back transformation to get eigenvectors of original matrix + for (var j = order - 1; j >= 0; j--) + { + var jO = j*order; + for (var i = 0; i < order; i++) + { + z = 0.0; + for (var k = 0; k <= j; k++) + { + z = z + (a[k*order + i]*matrixH[jO + k]); + } + + a[jO + 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. + 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)))); + } } } diff --git a/src/Numerics/Providers/LinearAlgebra/Managed/ManagedLinearAlgebraProvider.Single.cs b/src/Numerics/Providers/LinearAlgebra/Managed/ManagedLinearAlgebraProvider.Single.cs index 9bb295f0..2258a484 100644 --- a/src/Numerics/Providers/LinearAlgebra/Managed/ManagedLinearAlgebraProvider.Single.cs +++ b/src/Numerics/Providers/LinearAlgebra/Managed/ManagedLinearAlgebraProvider.Single.cs @@ -3,7 +3,7 @@ // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // -// Copyright (c) 2009-2018 Math.NET +// Copyright (c) 2009-2020 Math.NET // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation @@ -28,10 +28,10 @@ // using System; -using MathNet.Numerics.LinearAlgebra.Factorization; using MathNet.Numerics.Properties; using MathNet.Numerics.Threading; using Complex = System.Numerics.Complex; +using QRMethod = MathNet.Numerics.LinearAlgebra.Factorization.QRMethod; namespace MathNet.Numerics.Providers.LinearAlgebra.Managed { @@ -2554,15 +2554,15 @@ namespace MathNet.Numerics.Providers.LinearAlgebra.Managed d[i] = matrixEv[i*order + om1]; } - Numerics.LinearAlgebra.Single.Factorization.DenseEvd.SymmetricTridiagonalize(matrixEv, d, e, order); - Numerics.LinearAlgebra.Single.Factorization.DenseEvd.SymmetricDiagonalize(matrixEv, d, e, order); + SymmetricTridiagonalize(matrixEv, d, e, order); + SymmetricDiagonalize(matrixEv, d, e, order); } else { var matrixH = new float[matrix.Length]; Buffer.BlockCopy(matrix, 0, matrixH, 0, matrix.Length*Constants.SizeOfFloat); - Numerics.LinearAlgebra.Single.Factorization.DenseEvd.NonsymmetricReduceToHessenberg(matrixEv, matrixH, order); - Numerics.LinearAlgebra.Single.Factorization.DenseEvd.NonsymmetricReduceHessenberToRealSchur(matrixEv, matrixH, d, e, order); + NonsymmetricReduceToHessenberg(matrixEv, matrixH, order); + NonsymmetricReduceHessenberToRealSchur(matrixEv, matrixH, d, e, order); } for (var i = 0; i < order; i++) @@ -2582,5 +2582,1015 @@ namespace MathNet.Numerics.Providers.LinearAlgebra.Managed } } } + + /// + /// 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. + internal 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. + /// + internal 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.SinglePrecision; + 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 NonConvergenceException(); + } + } 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. + internal static void NonsymmetricReduceToHessenberg(float[] a, float[] matrixH, int order) + { + var ort = new float[order]; + var high = order - 1; + for (var m = 1; m <= high - 1; m++) + { + var mm1 = m - 1; + var mm1O = mm1*order; + // Scale column. + var scale = 0.0f; + for (var i = m; i <= high; i++) + { + scale += Math.Abs(matrixH[mm1O + i]); + } + + if (scale != 0.0f) + { + // Compute Householder transformation. + var h = 0.0f; + for (var i = high; i >= m; i--) + { + ort[i] = matrixH[mm1O + i]/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 jO = j*order; + var f = 0.0f; + for (var i = order - 1; i >= m; i--) + { + f += ort[i]*matrixH[jO + i]; + } + + f = f/h; + + for (var i = m; i <= high; i++) + { + matrixH[jO + i] -= f*ort[i]; + } + } + + for (var i = 0; i <= high; i++) + { + var f = 0.0f; + for (var j = high; j >= m; j--) + { + f += ort[j]*matrixH[j*order + i]; + } + f = f/h; + + for (var j = m; j <= high; j++) + { + matrixH[j*order + i] -= f*ort[j]; + } + } + + ort[m] = scale*ort[m]; + matrixH[mm1O + m] = 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 = high - 1; m >= 1; m--) + { + var mm1 = m - 1; + var mm1O = mm1*order; + var mm1Om = mm1O + m; + if (matrixH[mm1Om] != 0.0) + { + for (var i = m + 1; i <= high; i++) + { + ort[i] = matrixH[mm1O + i]; + } + + for (var j = m; j <= high; j++) + { + var g = 0.0f; + var jO = j*order; + for (var i = m; i <= high; i++) + { + g += ort[i]*a[jO + i]; + } + + // Double division avoids possible underflow + g = (g/ort[m])/matrixH[mm1Om]; + + for (var i = m; i <= high; i++) + { + a[jO + 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. + /// + internal static void NonsymmetricReduceHessenberToRealSchur(float[] a, float[] matrixH, float[] d, float[] e, int order) + { + // Initialize + var n = order - 1; + var eps = (float) Precision.SinglePrecision; + var exshift = 0.0f; + float p = 0, q = 0, r = 0, s = 0, z = 0; + float 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[j*order + i]); + } + } + + // 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 lm1 = l - 1; + var lm1O = lm1*order; + s = Math.Abs(matrixH[lm1O + lm1]) + Math.Abs(matrixH[l*order + l]); + + if (s == 0.0) + { + s = norm; + } + + if (Math.Abs(matrixH[lm1O + l]) < eps*s) + { + break; + } + + l--; + } + + // Check for convergence + // One root found + if (l == n) + { + var index = n*order + n; + matrixH[index] += exshift; + d[n] = matrixH[index]; + e[n] = 0.0f; + n--; + iter = 0; + + // Two roots found + } + else if (l == n - 1) + { + var nO = n*order; + var nm1 = n - 1; + var nm1O = nm1*order; + var nOn = nO + n; + + w = matrixH[nm1O + n]*matrixH[nO + nm1]; + p = (matrixH[nm1O + nm1] - matrixH[nOn])/2.0f; + q = (p*p) + w; + z = (float) Math.Sqrt(Math.Abs(q)); + + matrixH[nOn] += exshift; + matrixH[nm1O + nm1] += exshift; + x = matrixH[nOn]; + + // Real pair + if (q >= 0) + { + if (p >= 0) + { + z = p + z; + } + else + { + z = p - z; + } + + d[nm1] = x + z; + + d[n] = d[nm1]; + if (z != 0.0) + { + d[n] = x - (w/z); + } + + e[n - 1] = 0.0f; + e[n] = 0.0f; + x = matrixH[nm1O + n]; + 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++) + { + var jO = j*order; + var jOn = jO + n; + z = matrixH[jO + nm1]; + matrixH[jO + nm1] = (q*z) + (p*matrixH[jOn]); + matrixH[jOn] = (q*matrixH[jOn]) - (p*z); + } + + // Column modification + for (var i = 0; i <= n; i++) + { + var nOi = nO + i; + z = matrixH[nm1O + i]; + matrixH[nm1O + i] = (q*z) + (p*matrixH[nOi]); + matrixH[nOi] = (q*matrixH[nOi]) - (p*z); + } + + // Accumulate transformations + for (var i = 0; i < order; i++) + { + var nOi = nO + i; + z = a[nm1O + i]; + a[nm1O + i] = (q*z) + (p*a[nOi]); + a[nOi] = (q*a[nOi]) - (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 + { + var nO = n*order; + var nm1 = n - 1; + var nm1O = nm1*order; + var nOn = nO + n; + + // Form shift + x = matrixH[nOn]; + y = 0.0f; + w = 0.0f; + if (l < n) + { + y = matrixH[nm1O + nm1]; + w = matrixH[nm1O + n]*matrixH[nO + nm1]; + } + + // Wilkinson's original ad hoc shift + if (iter == 10) + { + exshift += x; + for (var i = 0; i <= n; i++) + { + matrixH[i*order + i] -= x; + } + + s = Math.Abs(matrixH[nm1O + n]) + Math.Abs(matrixH[(n - 2)*order + nm1]); + 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*order + i] -= s; + } + + exshift += s; + x = y = w = 0.964f; + } + } + + iter = iter + 1; + if (iter >= 30*order) + { + throw new NonConvergenceException(); + } + + // Look for two consecutive small sub-diagonal elements + var m = n - 2; + while (m >= l) + { + var mp1 = m + 1; + var mm1 = m - 1; + var mO = m*order; + var mp1O = mp1*order; + var mm1O = mm1*order; + + z = matrixH[mO + m]; + r = x - z; + s = y - z; + p = (((r*s) - w)/matrixH[mO + mp1]) + matrixH[mp1O + m]; + q = matrixH[mp1O + mp1] - z - r - s; + r = matrixH[mp1O + (m + 2)]; + 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[mm1O + m])*(Math.Abs(q) + Math.Abs(r)) < eps*(Math.Abs(p)*(Math.Abs(matrixH[mm1O + mm1]) + Math.Abs(z) + Math.Abs(matrixH[mp1O + mp1])))) + { + break; + } + + m--; + } + + var mp2 = m + 2; + for (var i = mp2; i <= n; i++) + { + matrixH[(i - 2)*order + i] = 0.0f; + if (i > mp2) + { + matrixH[(i - 3)*order + i] = 0.0f; + } + } + + // Double QR step involving rows l:n and columns m:n + for (var k = m; k <= n - 1; k++) + { + var notlast = k != n - 1; + var kO = k*order; + var km1 = k - 1; + var kp1 = k + 1; + var kp2 = k + 2; + var kp1O = kp1*order; + var kp2O = kp2*order; + var km1O = km1*order; + if (k != m) + { + p = matrixH[km1O + k]; + q = matrixH[km1O + kp1]; + r = notlast ? matrixH[km1O + kp2] : 0.0f; + x = Math.Abs(p) + Math.Abs(q) + Math.Abs(r); + if (x == 0.0f) + { + continue; + } + + p = p/x; + q = q/x; + r = r/x; + } + + s = (float) Math.Sqrt((p*p) + (q*q) + (r*r)); + + if (p < 0) + { + s = -s; + } + + if (s != 0.0f) + { + if (k != m) + { + matrixH[km1O + k] = (-s)*x; + } + else if (l != m) + { + matrixH[km1O + k] = -matrixH[km1O + k]; + } + + 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++) + { + var jO = j*order; + var jOk = jO + k; + var jOkp1 = jO + kp1; + var jOkp2 = jO + kp2; + p = matrixH[jOk] + (q*matrixH[jOkp1]); + if (notlast) + { + p = p + (r*matrixH[jOkp2]); + matrixH[jOkp2] -= (p*z); + } + + matrixH[jOk] -= (p*x); + matrixH[jOkp1] -= (p*y); + } + + // Column modification + for (var i = 0; i <= Math.Min(n, k + 3); i++) + { + p = (x*matrixH[kO + i]) + (y*matrixH[kp1O + i]); + + if (notlast) + { + p = p + (z*matrixH[kp2O + i]); + matrixH[kp2O + i] -= (p*r); + } + + matrixH[kO + i] -= p; + matrixH[kp1O + i] -= (p*q); + } + + // Accumulate transformations + for (var i = 0; i < order; i++) + { + p = (x*a[kO + i]) + (y*a[kp1O + i]); + + if (notlast) + { + p = p + (z*a[kp2O + i]); + a[kp2O + i] -= p*r; + } + + a[kO + i] -= p; + a[kp1O + 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--) + { + var nO = n*order; + var nm1 = n - 1; + var nm1O = nm1*order; + + p = d[n]; + q = e[n]; + + + // Real vector + float t; + if (q == 0.0f) + { + var l = n; + matrixH[nO + n] = 1.0f; + for (var i = n - 1; i >= 0; i--) + { + var ip1 = i + 1; + var iO = i*order; + var ip1O = ip1*order; + + w = matrixH[iO + i] - p; + r = 0.0f; + for (var j = l; j <= n; j++) + { + r = r + (matrixH[j*order + i]*matrixH[nO + j]); + } + + if (e[i] < 0.0) + { + z = w; + s = r; + } + else + { + l = i; + if (e[i] == 0.0f) + { + if (w != 0.0f) + { + matrixH[nO + i] = (-r)/w; + } + else + { + matrixH[nO + i] = (-r)/(eps*norm); + } + + // Solve real equations + } + else + { + x = matrixH[ip1O + i]; + y = matrixH[iO + ip1]; + q = ((d[i] - p)*(d[i] - p)) + (e[i]*e[i]); + t = ((x*s) - (z*r))/q; + matrixH[nO + i] = t; + if (Math.Abs(x) > Math.Abs(z)) + { + matrixH[nO + ip1] = (-r - (w*t))/x; + } + else + { + matrixH[nO + ip1] = (-s - (y*t))/z; + } + } + + // Overflow control + t = Math.Abs(matrixH[nO + i]); + if ((eps*t)*t > 1) + { + for (var j = i; j <= n; j++) + { + matrixH[nO + j] /= t; + } + } + } + } + + // Complex vector + } + else if (q < 0) + { + var l = n - 1; + + // Last vector component imaginary so matrix is triangular + if (Math.Abs(matrixH[nm1O + n]) > Math.Abs(matrixH[nO + nm1])) + { + matrixH[nm1O + nm1] = q/matrixH[nm1O + n]; + matrixH[nO + nm1] = (-(matrixH[nO + n] - p))/matrixH[nm1O + n]; + } + else + { + var res = Cdiv(0.0f, -matrixH[nO + nm1], matrixH[nm1O + nm1] - p, q); + matrixH[nm1O + nm1] = res.Real; + matrixH[nO + nm1] = res.Imaginary; + } + + matrixH[nm1O + n] = 0.0f; + matrixH[nO + n] = 1.0f; + for (var i = n - 2; i >= 0; i--) + { + var ip1 = i + 1; + var iO = i*order; + var ip1O = ip1*order; + var ra = 0.0f; + var sa = 0.0f; + for (var j = l; j <= n; j++) + { + var jO = j*order; + var jOi = jO + i; + ra = ra + (matrixH[jOi]*matrixH[nm1O + j]); + sa = sa + (matrixH[jOi]*matrixH[nO + j]); + } + + w = matrixH[iO + 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[nm1O + i] = res.Real; + matrixH[nO + i] = res.Imaginary; + } + else + { + // Solve complex equations + x = matrixH[ip1O + i]; + y = matrixH[iO + ip1]; + + var vr = ((d[i] - p)*(d[i] - p)) + (e[i]*e[i]) - (q*q); + var 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[nm1O + i] = res.Real; + matrixH[nO + i] = res.Imaginary; + if (Math.Abs(x) > (Math.Abs(z) + Math.Abs(q))) + { + matrixH[nm1O + ip1] = (-ra - (w*matrixH[nm1O + i]) + (q*matrixH[nO + i]))/x; + matrixH[nO + ip1] = (-sa - (w*matrixH[nO + i]) - (q*matrixH[nm1O + i]))/x; + } + else + { + res = Cdiv(-r - (y*matrixH[nm1O + i]), -s - (y*matrixH[nO + i]), z, q); + matrixH[nm1O + ip1] = res.Real; + matrixH[nO + ip1] = res.Imaginary; + } + } + + // Overflow control + t = Math.Max(Math.Abs(matrixH[nm1O + i]), Math.Abs(matrixH[nO + i])); + if ((eps*t)*t > 1) + { + for (var j = i; j <= n; j++) + { + matrixH[nm1O + j] /= t; + matrixH[nO + j] /= t; + } + } + } + } + } + } + + // Back transformation to get eigenvectors of original matrix + for (var j = order - 1; j >= 0; j--) + { + var jO = j*order; + for (var i = 0; i < order; i++) + { + z = 0.0f; + for (var k = 0; k <= j; k++) + { + z = z + (a[k*order + i]*matrixH[jO + k]); + } + + a[jO + 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. + 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)))); + } } } diff --git a/src/Numerics/Providers/LinearAlgebra/Managed/ManagedLinearAlgebraProvider.cs b/src/Numerics/Providers/LinearAlgebra/Managed/ManagedLinearAlgebraProvider.cs index 93e5e7df..8c89a6b6 100644 --- a/src/Numerics/Providers/LinearAlgebra/Managed/ManagedLinearAlgebraProvider.cs +++ b/src/Numerics/Providers/LinearAlgebra/Managed/ManagedLinearAlgebraProvider.cs @@ -3,7 +3,7 @@ // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // -// Copyright (c) 2009-2013 Math.NET +// Copyright (c) 2009-2020 Math.NET // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation @@ -68,7 +68,7 @@ namespace MathNet.Numerics.Providers.LinearAlgebra.Managed /// /// Assumes that and have already been transposed. /// - protected static void GetRow(Transpose transpose, int rowindx, int numRows, int numCols, T[] matrix, T[] row) + static void GetRow(Transpose transpose, int rowindx, int numRows, int numCols, T[] matrix, T[] row) { if (transpose == Transpose.DontTranspose) { @@ -86,7 +86,7 @@ namespace MathNet.Numerics.Providers.LinearAlgebra.Managed /// /// Assumes that and have already been transposed. /// - protected static void GetColumn(Transpose transpose, int colindx, int numRows, int numCols, T[] matrix, T[] column) + static void GetColumn(Transpose transpose, int colindx, int numRows, int numCols, T[] matrix, T[] column) { if (transpose == Transpose.DontTranspose) { diff --git a/src/Numerics/Providers/LinearAlgebra/ManagedReference/ManagedReferenceLinearAlgebraProvider.Complex.cs b/src/Numerics/Providers/LinearAlgebra/ManagedReference/ManagedReferenceLinearAlgebraProvider.Complex.cs index d01e94f1..d5380a6a 100644 --- a/src/Numerics/Providers/LinearAlgebra/ManagedReference/ManagedReferenceLinearAlgebraProvider.Complex.cs +++ b/src/Numerics/Providers/LinearAlgebra/ManagedReference/ManagedReferenceLinearAlgebraProvider.Complex.cs @@ -3,7 +3,7 @@ // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // -// Copyright (c) 2009-2015 Math.NET +// Copyright (c) 2009-2020 Math.NET // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation @@ -28,11 +28,10 @@ // using System; -using MathNet.Numerics.LinearAlgebra.Complex.Factorization; -using MathNet.Numerics.LinearAlgebra.Factorization; using MathNet.Numerics.Properties; using MathNet.Numerics.Threading; using Complex = System.Numerics.Complex; +using QRMethod = MathNet.Numerics.LinearAlgebra.Factorization.QRMethod; namespace MathNet.Numerics.Providers.LinearAlgebra.ManagedReference { @@ -2743,9 +2742,9 @@ namespace MathNet.Numerics.Providers.LinearAlgebra.ManagedReference var d = new double[order]; var e = new double[order]; - DenseEvd.SymmetricTridiagonalize(matrixCopy, d, e, tau, order); - DenseEvd.SymmetricDiagonalize(matrixEv, d, e, order); - DenseEvd.SymmetricUntridiagonalize(matrixEv, matrixCopy, tau, order); + Managed.ManagedLinearAlgebraProvider.SymmetricTridiagonalize(matrixCopy, d, e, tau, order); + Managed.ManagedLinearAlgebraProvider.SymmetricDiagonalize(matrixEv, d, e, order); + Managed.ManagedLinearAlgebraProvider.SymmetricUntridiagonalize(matrixEv, matrixCopy, tau, order); for (var i = 0; i < order; i++) { @@ -2754,8 +2753,8 @@ namespace MathNet.Numerics.Providers.LinearAlgebra.ManagedReference } else { - DenseEvd.NonsymmetricReduceToHessenberg(matrixEv, matrixCopy, order); - DenseEvd.NonsymmetricReduceHessenberToRealSchur(vectorEv, matrixEv, matrixCopy, order); + Managed.ManagedLinearAlgebraProvider.NonsymmetricReduceToHessenberg(matrixEv, matrixCopy, order); + Managed.ManagedLinearAlgebraProvider.NonsymmetricReduceHessenberToRealSchur(vectorEv, matrixEv, matrixCopy, order); } for (var i = 0; i < order; i++) diff --git a/src/Numerics/Providers/LinearAlgebra/ManagedReference/ManagedReferenceLinearAlgebraProvider.Complex32.cs b/src/Numerics/Providers/LinearAlgebra/ManagedReference/ManagedReferenceLinearAlgebraProvider.Complex32.cs index 1a4575fa..ca19ab35 100644 --- a/src/Numerics/Providers/LinearAlgebra/ManagedReference/ManagedReferenceLinearAlgebraProvider.Complex32.cs +++ b/src/Numerics/Providers/LinearAlgebra/ManagedReference/ManagedReferenceLinearAlgebraProvider.Complex32.cs @@ -3,7 +3,7 @@ // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // -// Copyright (c) 2009-2015 Math.NET +// Copyright (c) 2009-2020 Math.NET // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation @@ -28,12 +28,10 @@ // using System; -using MathNet.Numerics.LinearAlgebra.Complex32; -using MathNet.Numerics.LinearAlgebra.Complex32.Factorization; -using MathNet.Numerics.LinearAlgebra.Factorization; using MathNet.Numerics.Properties; using MathNet.Numerics.Threading; using Complex = System.Numerics.Complex; +using QRMethod = MathNet.Numerics.LinearAlgebra.Factorization.QRMethod; namespace MathNet.Numerics.Providers.LinearAlgebra.ManagedReference { @@ -2743,9 +2741,9 @@ namespace MathNet.Numerics.Providers.LinearAlgebra.ManagedReference var d = new float[order]; var e = new float[order]; - DenseEvd.SymmetricTridiagonalize(matrixCopy, d, e, tau, order); - DenseEvd.SymmetricDiagonalize(matrixEv, d, e, order); - DenseEvd.SymmetricUntridiagonalize(matrixEv, matrixCopy, tau, order); + Managed.ManagedLinearAlgebraProvider.SymmetricTridiagonalize(matrixCopy, d, e, tau, order); + Managed.ManagedLinearAlgebraProvider.SymmetricDiagonalize(matrixEv, d, e, order); + Managed.ManagedLinearAlgebraProvider.SymmetricUntridiagonalize(matrixEv, matrixCopy, tau, order); for (var i = 0; i < order; i++) { @@ -2755,10 +2753,11 @@ namespace MathNet.Numerics.Providers.LinearAlgebra.ManagedReference } else { - var v = new DenseVector(order); + var v = new Complex32[order]; + + Managed.ManagedLinearAlgebraProvider.NonsymmetricReduceToHessenberg(matrixEv, matrixCopy, order); + Managed.ManagedLinearAlgebraProvider.NonsymmetricReduceHessenberToRealSchur(v, matrixEv, matrixCopy, order); - DenseEvd.NonsymmetricReduceToHessenberg(matrixEv, matrixCopy, order); - DenseEvd.NonsymmetricReduceHessenberToRealSchur(v.Values, matrixEv, matrixCopy, order); for (var i = 0; i < order; i++) { vectorEv[i] = new Complex(v[i].Real, v[i].Imaginary); diff --git a/src/Numerics/Providers/LinearAlgebra/ManagedReference/ManagedReferenceLinearAlgebraProvider.Double.cs b/src/Numerics/Providers/LinearAlgebra/ManagedReference/ManagedReferenceLinearAlgebraProvider.Double.cs index 3b12c727..a7969245 100644 --- a/src/Numerics/Providers/LinearAlgebra/ManagedReference/ManagedReferenceLinearAlgebraProvider.Double.cs +++ b/src/Numerics/Providers/LinearAlgebra/ManagedReference/ManagedReferenceLinearAlgebraProvider.Double.cs @@ -3,7 +3,7 @@ // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // -// Copyright (c) 2009-2015 Math.NET +// Copyright (c) 2009-2020 Math.NET // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation @@ -28,10 +28,10 @@ // using System; -using MathNet.Numerics.LinearAlgebra.Factorization; using MathNet.Numerics.Properties; using MathNet.Numerics.Threading; using Complex = System.Numerics.Complex; +using QRMethod = MathNet.Numerics.LinearAlgebra.Factorization.QRMethod; namespace MathNet.Numerics.Providers.LinearAlgebra.ManagedReference { @@ -2692,15 +2692,15 @@ namespace MathNet.Numerics.Providers.LinearAlgebra.ManagedReference d[i] = matrixEv[i*order + om1]; } - Numerics.LinearAlgebra.Double.Factorization.DenseEvd.SymmetricTridiagonalize(matrixEv, d, e, order); - Numerics.LinearAlgebra.Double.Factorization.DenseEvd.SymmetricDiagonalize(matrixEv, d, e, order); + Managed.ManagedLinearAlgebraProvider.SymmetricTridiagonalize(matrixEv, d, e, order); + Managed.ManagedLinearAlgebraProvider.SymmetricDiagonalize(matrixEv, d, e, order); } else { var matrixH = new double[matrix.Length]; Buffer.BlockCopy(matrix, 0, matrixH, 0, matrix.Length*Constants.SizeOfDouble); - Numerics.LinearAlgebra.Double.Factorization.DenseEvd.NonsymmetricReduceToHessenberg(matrixEv, matrixH, order); - Numerics.LinearAlgebra.Double.Factorization.DenseEvd.NonsymmetricReduceHessenberToRealSchur(matrixEv, matrixH, d, e, order); + Managed.ManagedLinearAlgebraProvider.NonsymmetricReduceToHessenberg(matrixEv, matrixH, order); + Managed.ManagedLinearAlgebraProvider.NonsymmetricReduceHessenberToRealSchur(matrixEv, matrixH, d, e, order); } for (var i = 0; i < order; i++) diff --git a/src/Numerics/Providers/LinearAlgebra/ManagedReference/ManagedReferenceLinearAlgebraProvider.Single.cs b/src/Numerics/Providers/LinearAlgebra/ManagedReference/ManagedReferenceLinearAlgebraProvider.Single.cs index f0dda826..c21e7520 100644 --- a/src/Numerics/Providers/LinearAlgebra/ManagedReference/ManagedReferenceLinearAlgebraProvider.Single.cs +++ b/src/Numerics/Providers/LinearAlgebra/ManagedReference/ManagedReferenceLinearAlgebraProvider.Single.cs @@ -3,7 +3,7 @@ // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // -// Copyright (c) 2009-2015 Math.NET +// Copyright (c) 2009-2020 Math.NET // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation @@ -28,10 +28,10 @@ // using System; -using MathNet.Numerics.LinearAlgebra.Factorization; using MathNet.Numerics.Properties; using MathNet.Numerics.Threading; using Complex = System.Numerics.Complex; +using QRMethod = MathNet.Numerics.LinearAlgebra.Factorization.QRMethod; namespace MathNet.Numerics.Providers.LinearAlgebra.ManagedReference { @@ -2698,15 +2698,15 @@ namespace MathNet.Numerics.Providers.LinearAlgebra.ManagedReference d[i] = matrixEv[i*order + om1]; } - Numerics.LinearAlgebra.Single.Factorization.DenseEvd.SymmetricTridiagonalize(matrixEv, d, e, order); - Numerics.LinearAlgebra.Single.Factorization.DenseEvd.SymmetricDiagonalize(matrixEv, d, e, order); + Managed.ManagedLinearAlgebraProvider.SymmetricTridiagonalize(matrixEv, d, e, order); + Managed.ManagedLinearAlgebraProvider.SymmetricDiagonalize(matrixEv, d, e, order); } else { var matrixH = new float[matrix.Length]; Buffer.BlockCopy(matrix, 0, matrixH, 0, matrix.Length*Constants.SizeOfFloat); - Numerics.LinearAlgebra.Single.Factorization.DenseEvd.NonsymmetricReduceToHessenberg(matrixEv, matrixH, order); - Numerics.LinearAlgebra.Single.Factorization.DenseEvd.NonsymmetricReduceHessenberToRealSchur(matrixEv, matrixH, d, e, order); + Managed.ManagedLinearAlgebraProvider.NonsymmetricReduceToHessenberg(matrixEv, matrixH, order); + Managed.ManagedLinearAlgebraProvider.NonsymmetricReduceHessenberToRealSchur(matrixEv, matrixH, d, e, order); } for (var i = 0; i < order; i++) diff --git a/src/Numerics/Providers/LinearAlgebra/ManagedReference/ManagedReferenceLinearAlgebraProvider.cs b/src/Numerics/Providers/LinearAlgebra/ManagedReference/ManagedReferenceLinearAlgebraProvider.cs index 2cb29b40..3fa70a14 100644 --- a/src/Numerics/Providers/LinearAlgebra/ManagedReference/ManagedReferenceLinearAlgebraProvider.cs +++ b/src/Numerics/Providers/LinearAlgebra/ManagedReference/ManagedReferenceLinearAlgebraProvider.cs @@ -3,7 +3,7 @@ // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // -// Copyright (c) 2009-2013 Math.NET +// Copyright (c) 2009-2020 Math.NET // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation @@ -27,8 +27,6 @@ // OTHER DEALINGS IN THE SOFTWARE. // -using System; - namespace MathNet.Numerics.Providers.LinearAlgebra.ManagedReference { /// @@ -64,41 +62,5 @@ namespace MathNet.Numerics.Providers.LinearAlgebra.ManagedReference { return "Managed"; } - - /// - /// Assumes that and have already been transposed. - /// - protected static void GetRow(Transpose transpose, int rowindx, int numRows, int numCols, T[] matrix, T[] row) - { - if (transpose == Transpose.DontTranspose) - { - for (int i = 0; i < numCols; i++) - { - row[i] = matrix[(i * numRows) + rowindx]; - } - } - else - { - Array.Copy(matrix, rowindx * numCols, row, 0, numCols); - } - } - - /// - /// Assumes that and have already been transposed. - /// - protected static void GetColumn(Transpose transpose, int colindx, int numRows, int numCols, T[] matrix, T[] column) - { - if (transpose == Transpose.DontTranspose) - { - Array.Copy(matrix, colindx * numRows, column, 0, numRows); - } - else - { - for (int i = 0; i < numRows; i++) - { - column[i] = matrix[(i * numCols) + colindx]; - } - } - } } }