/// <summary> /// Solves a system of linear equations, <b>AX = B</b>, with A SVD factorized. /// </summary> /// <param name="input">The right hand side <see cref="Matrix{T}"/>, <b>B</b>.</param> /// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>X</b>.</param> public override void Solve(Matrix <Numerics.Complex32> input, Matrix <Numerics.Complex32> result) { // The solution X should have the same number of columns as B if (input.ColumnCount != result.ColumnCount) { throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension); } // The dimension compatibility conditions for X = A\B require the two matrices A and B to have the same number of rows if (EigenValues.Count != input.RowCount) { throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension); } // The solution X row dimension is equal to the column dimension of A if (EigenValues.Count != result.RowCount) { throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension); } if (IsSymmetric) { var order = EigenValues.Count; var tmp = new Numerics.Complex32[order]; for (var k = 0; k < order; k++) { for (var j = 0; j < order; j++) { Numerics.Complex32 value = 0.0f; if (j < order) { for (var i = 0; i < order; i++) { value += ((DenseMatrix)EigenVectors).Values[(j * order) + i].Conjugate() * input.At(i, k); } value /= (float)EigenValues[j].Real; } tmp[j] = value; } for (var j = 0; j < order; j++) { Numerics.Complex32 value = 0.0f; for (var i = 0; i < order; i++) { value += ((DenseMatrix)EigenVectors).Values[(i * order) + j] * tmp[i]; } result.At(j, k, value); } } } else { throw new ArgumentException(Resources.ArgumentMatrixSymmetric); } }
/// <summary> /// Solves a system of linear equations, <b>Ax = b</b>, with A EVD factorized. /// </summary> /// <param name="input">The right hand side vector, <b>b</b>.</param> /// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>x</b>.</param> public override void Solve(Vector <Numerics.Complex32> input, Vector <Numerics.Complex32> result) { // Ax=b where A is an m x m matrix // Check that b is a column vector with m entries if (EigenValues.Count != input.Count) { throw new ArgumentException(Resources.ArgumentVectorsSameLength); } // Check that x is a column vector with n entries if (EigenValues.Count != result.Count) { throw new ArgumentException(Resources.ArgumentMatrixDimensions); } if (IsSymmetric) { // Symmetric case -> x = V * inv(λ) * VH * b; var order = EigenValues.Count; var tmp = new Numerics.Complex32[order]; Numerics.Complex32 value; for (var j = 0; j < order; j++) { value = 0; if (j < order) { for (var i = 0; i < order; i++) { value += ((DenseMatrix)EigenVectors).Values[(j * order) + i].Conjugate() * input[i]; } value /= (float)EigenValues[j].Real; } tmp[j] = value; } for (var j = 0; j < order; j++) { value = 0; for (var i = 0; i < order; i++) { value += ((DenseMatrix)EigenVectors).Values[(i * order) + j] * tmp[i]; } result[j] = value; } } else { throw new ArgumentException(Resources.ArgumentMatrixSymmetric); } }
/// <summary> /// Reduces a complex hermitian matrix to a real symmetric tridiagonal matrix using unitary similarity transformations. /// </summary> /// <param name="matrixA">Source matrix to reduce</param> /// <param name="d">Output: Arrays for internal storage of real parts of eigenvalues</param> /// <param name="e">Output: Arrays for internal storage of imaginary parts of eigenvalues</param> /// <param name="tau">Output: Arrays that contains further information about the transformations.</param> /// <param name="order">Order of initial matrix</param> /// <remarks>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.</remarks> internal static void SymmetricTridiagonalize(Numerics.Complex32[] matrixA, float[] d, float[] e, Numerics.Complex32[] tau, int order) { float hh; tau[order - 1] = Numerics.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] = Numerics.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; } Numerics.Complex32 g = (float) Math.Sqrt(h); e[i] = scale*g.Real; Numerics.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 = Numerics.Complex32.Zero; for (var j = 0; j < i; j++) { var tmp = Numerics.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 Numerics.Complex32(hh, scale*(float) Math.Sqrt(h)); } hh = d[0]; d[0] = matrixA[0].Real; matrixA[0] = hh; e[0] = 0.0f; }
/// <summary> /// Nonsymmetric reduction to Hessenberg form. /// </summary> /// <param name="dataEv">Data array of matrix V (eigenvectors)</param> /// <param name="matrixH">Array for internal storage of nonsymmetric Hessenberg form.</param> /// <param name="order">Order of initial matrix</param> /// <remarks>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.</remarks> internal static void NonsymmetricReduceToHessenberg(Numerics.Complex32[] dataEv, Numerics.Complex32[] matrixH, int order) { var ort = new Numerics.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 = Numerics.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 = Numerics.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 ? Numerics.Complex32.One : Numerics.Complex32.Zero; } } for (var m = order - 2; m >= 1; m--) { var mm1O = (m - 1)*order; var mm1Om = mm1O + m; if (matrixH[mm1Om] != Numerics.Complex32.Zero && ort[m] != Numerics.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 = Numerics.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; } } } }
/// <summary> /// Nonsymmetric reduction from Hessenberg to real Schur form. /// </summary> /// <param name="vectorV">Data array of the eigenvectors</param> /// <param name="dataEv">Data array of matrix V (eigenvectors)</param> /// <param name="matrixH">Array for internal storage of nonsymmetric Hessenberg form.</param> /// <param name="order">Order of initial matrix</param> /// <remarks>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.</remarks> internal static void NonsymmetricReduceHessenberToRealSchur(Numerics.Complex32[] vectorV, Numerics.Complex32[] dataEv, Numerics.Complex32[] matrixH, int order) { // Initialize var n = order - 1; var eps = (float) Precision.SingleMachinePrecision; float norm; Numerics.Complex32 x, y, z, exshift = Numerics.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 Numerics.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 Numerics.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 Numerics.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 = Numerics.Complex32.Zero; for (var k = 0; k <= j; k++) { z += dataEv[(k*order) + i]*matrixH[jO + k]; } dataEv[jO + i] = z; } } }
/// <summary> /// Solves a system of linear equations, <b>Ax = b</b>, with A EVD factorized. /// </summary> /// <param name="input">The right hand side vector, <b>b</b>.</param> /// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>x</b>.</param> public override void Solve(Vector<Numerics.Complex32> input, Vector<Numerics.Complex32> result) { // Ax=b where A is an m x m matrix // Check that b is a column vector with m entries if (EigenValues.Count != input.Count) { throw new ArgumentException(Resources.ArgumentVectorsSameLength); } // Check that x is a column vector with n entries if (EigenValues.Count != result.Count) { throw new ArgumentException(Resources.ArgumentMatrixDimensions); } if (IsSymmetric) { // Symmetric case -> x = V * inv(λ) * VH * b; var order = EigenValues.Count; var tmp = new Numerics.Complex32[order]; Numerics.Complex32 value; for (var j = 0; j < order; j++) { value = 0; if (j < order) { for (var i = 0; i < order; i++) { value += ((DenseMatrix) EigenVectors).Values[(j*order) + i].Conjugate()*input[i]; } value /= (float) EigenValues[j].Real; } tmp[j] = value; } for (var j = 0; j < order; j++) { value = 0; for (var i = 0; i < order; i++) { value += ((DenseMatrix) EigenVectors).Values[(i*order) + j]*tmp[i]; } result[j] = value; } } else { throw new ArgumentException(Resources.ArgumentMatrixSymmetric); } }
/// <summary> /// Solves a system of linear equations, <b>AX = B</b>, with A SVD factorized. /// </summary> /// <param name="input">The right hand side <see cref="Matrix{T}"/>, <b>B</b>.</param> /// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>X</b>.</param> public override void Solve(Matrix<Numerics.Complex32> input, Matrix<Numerics.Complex32> result) { // The solution X should have the same number of columns as B if (input.ColumnCount != result.ColumnCount) { throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension); } // The dimension compatibility conditions for X = A\B require the two matrices A and B to have the same number of rows if (EigenValues.Count != input.RowCount) { throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension); } // The solution X row dimension is equal to the column dimension of A if (EigenValues.Count != result.RowCount) { throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension); } if (IsSymmetric) { var order = EigenValues.Count; var tmp = new Numerics.Complex32[order]; for (var k = 0; k < order; k++) { for (var j = 0; j < order; j++) { Numerics.Complex32 value = 0.0f; if (j < order) { for (var i = 0; i < order; i++) { value += ((DenseMatrix) EigenVectors).Values[(j*order) + i].Conjugate()*input.At(i, k); } value /= (float) EigenValues[j].Real; } tmp[j] = value; } for (var j = 0; j < order; j++) { Numerics.Complex32 value = 0.0f; for (var i = 0; i < order; i++) { value += ((DenseMatrix) EigenVectors).Values[(i*order) + j]*tmp[i]; } result.At(j, k, value); } } } else { throw new ArgumentException(Resources.ArgumentMatrixSymmetric); } }
/// <summary> /// Nonsymmetric reduction from Hessenberg to real Schur form. /// </summary> /// <param name="vectorV">Data array of the eigenvectors</param> /// <param name="dataEv">Data array of matrix V (eigenvectors)</param> /// <param name="matrixH">Array for internal storage of nonsymmetric Hessenberg form.</param> /// <param name="order">Order of initial matrix</param> /// <remarks>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.</remarks> internal static void NonsymmetricReduceHessenberToRealSchur(Numerics.Complex32[] vectorV, Numerics.Complex32[] dataEv, Numerics.Complex32[] matrixH, int order) { // Initialize var n = order - 1; var eps = (float)Precision.SingleMachinePrecision; float norm; Numerics.Complex32 x, y, z, exshift = Numerics.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 Numerics.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 Numerics.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 Numerics.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 = Numerics.Complex32.Zero; for (var k = 0; k <= j; k++) { z += dataEv[(k * order) + i] * matrixH[jO + k]; } dataEv[jO + i] = z; } } }
/// <summary> /// Nonsymmetric reduction to Hessenberg form. /// </summary> /// <param name="dataEv">Data array of matrix V (eigenvectors)</param> /// <param name="matrixH">Array for internal storage of nonsymmetric Hessenberg form.</param> /// <param name="order">Order of initial matrix</param> /// <remarks>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.</remarks> internal static void NonsymmetricReduceToHessenberg(Numerics.Complex32[] dataEv, Numerics.Complex32[] matrixH, int order) { var ort = new Numerics.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 = Numerics.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 = Numerics.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 ? Numerics.Complex32.One : Numerics.Complex32.Zero; } } for (var m = order - 2; m >= 1; m--) { var mm1O = (m - 1) * order; var mm1Om = mm1O + m; if (matrixH[mm1Om] != Numerics.Complex32.Zero && ort[m] != Numerics.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 = Numerics.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; } } } }
/// <summary> /// Reduces a complex hermitian matrix to a real symmetric tridiagonal matrix using unitary similarity transformations. /// </summary> /// <param name="matrixA">Source matrix to reduce</param> /// <param name="d">Output: Arrays for internal storage of real parts of eigenvalues</param> /// <param name="e">Output: Arrays for internal storage of imaginary parts of eigenvalues</param> /// <param name="tau">Output: Arrays that contains further information about the transformations.</param> /// <param name="order">Order of initial matrix</param> /// <remarks>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.</remarks> internal static void SymmetricTridiagonalize(Numerics.Complex32[] matrixA, float[] d, float[] e, Numerics.Complex32[] tau, int order) { float hh; tau[order - 1] = Numerics.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] = Numerics.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; } Numerics.Complex32 g = (float)Math.Sqrt(h); e[i] = scale * g.Real; Numerics.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 = Numerics.Complex32.Zero; for (var j = 0; j < i; j++) { var tmp = Numerics.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 Numerics.Complex32(hh, scale * (float)Math.Sqrt(h)); } hh = d[0]; d[0] = matrixA[0].Real; matrixA[0] = hh; e[0] = 0.0f; }