/// <summary> /// Multiplies a scalar to each element of the vector and stores the result in the result vector. /// </summary> /// <param name="scalar"> /// The scalar to multiply. /// </param> /// <param name="result"> /// The vector to store the result of the multiplication. /// </param> protected override void DoMultiply(Complex scalar, Vector <Complex> result) { var sparseResult = result as SparseVector; if (sparseResult == null) { result.Clear(); for (var index = 0; index < _storage.ValueCount; index++) { result.At(_storage.Indices[index], scalar * _storage.Values[index]); } } else { if (!ReferenceEquals(this, result)) { sparseResult._storage.ValueCount = _storage.ValueCount; sparseResult._storage.Indices = new int[_storage.ValueCount]; Buffer.BlockCopy(_storage.Indices, 0, sparseResult._storage.Indices, 0, _storage.ValueCount * Constants.SizeOfInt); sparseResult._storage.Values = new Complex[_storage.ValueCount]; Array.Copy(_storage.Values, sparseResult._storage.Values, _storage.ValueCount); } Control.LinearAlgebraProvider.ScaleArray(scalar, sparseResult._storage.Values, sparseResult._storage.Values); } }
/// <summary> /// Returns the largest absolute value from the unsorted data array. /// Returns NaN if data is empty or any entry is NaN. /// </summary> /// <param name="data">Sample array, no sorting is assumed.</param> public static Complex64 MaximumMagnitudePhase(Complex64[] data) { if (data.Length == 0) { return(new Complex64(double.NaN, double.NaN)); } double maxMagnitude = 0.0d; Complex64 max = Complex64.Zero; for (int i = 0; i < data.Length; i++) { double magnitude = data[i].Magnitude; if (double.IsNaN(magnitude)) { return(new Complex64(double.NaN, double.NaN)); } if (magnitude > maxMagnitude || magnitude == maxMagnitude && data[i].Phase > max.Phase) { maxMagnitude = magnitude; max = data[i]; } } return(max); }
/// <summary> /// Scale vector <paramref name="a"/> by <paramref name="z"/> starting from index <paramref name="start"/> /// </summary> /// <param name="a">Source vector</param> /// <param name="start">Row to scale from</param> /// <param name="z">Scale value</param> private static void CscalVector(Complex[] a, int start, Complex z) { for (var i = start; i < a.Length; i++) { a[i] = a[i] * z; } }
/// <summary> /// Multiplies each element of the matrix by a scalar and places results into the result matrix. /// </summary> /// <param name="scalar">The scalar to multiply the matrix with.</param> /// <param name="result">The matrix to store the result of the multiplication.</param> /// <exception cref="ArgumentNullException">If the result matrix is <see langword="null" />.</exception> /// <exception cref="ArgumentException">If the result matrix's dimensions are not the same as this matrix.</exception> protected override void DoMultiply(Complex scalar, Matrix <Complex> result) { if (scalar == 0.0) { result.Clear(); return; } if (scalar == 1.0) { CopyTo(result); return; } var diagResult = result as DiagonalMatrix; if (diagResult == null) { base.Multiply(scalar, result); } else { if (!ReferenceEquals(this, result)) { CopyTo(diagResult); } Control.LinearAlgebraProvider.ScaleArray(scalar, _data, diagResult._data); } }
/// <summary> /// Solves a system of linear equations, <b>Ax = b</b>, with A SVD 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 <Complex> input, Vector <Complex> result) { if (input == null) { throw new ArgumentNullException("input"); } if (result == null) { throw new ArgumentNullException("result"); } if (!ComputeVectors) { throw new InvalidOperationException(Resources.SingularVectorsNotComputed); } // Ax=b where A is an m x n matrix // Check that b is a column vector with m entries if (MatrixU.RowCount != input.Count) { throw new ArgumentException(Resources.ArgumentVectorsSameLength); } // Check that x is a column vector with n entries if (MatrixVT.ColumnCount != result.Count) { throw Matrix.DimensionsDontMatch <ArgumentException>(MatrixVT, result); } var mn = Math.Min(MatrixU.RowCount, MatrixVT.ColumnCount); var tmp = new Complex[MatrixVT.ColumnCount]; for (var j = 0; j < MatrixVT.ColumnCount; j++) { var value = Complex.Zero; if (j < mn) { for (var i = 0; i < MatrixU.RowCount; i++) { value += MatrixU.At(i, j).Conjugate() * input[i]; } value /= VectorS[j]; } tmp[j] = value; } for (var j = 0; j < MatrixVT.ColumnCount; j++) { var value = Complex.Zero; for (var i = 0; i < MatrixVT.ColumnCount; i++) { value += MatrixVT.At(i, j).Conjugate() * tmp[i]; } result[j] = value; } }
/// <summary> /// Returns the smallest absolute value from the unsorted data array. /// Returns NaN if data is empty or any entry is NaN. /// </summary> /// <param name="data">Sample array, no sorting is assumed.</param> public static Complex64 MinimumMagnitudePhase(Complex64[] data) { if (data.Length == 0) { return(new Complex64(double.NaN, double.NaN)); } double minMagnitude = double.PositiveInfinity; Complex64 min = new Complex64(double.PositiveInfinity, double.PositiveInfinity); for (int i = 0; i < data.Length; i++) { double magnitude = data[i].Magnitude; if (double.IsNaN(magnitude)) { return(new Complex64(double.NaN, double.NaN)); } if (magnitude < minMagnitude || magnitude == minMagnitude && data[i].Phase < min.Phase) { minMagnitude = magnitude; min = data[i]; } } return(min); }
/// <summary> /// Create a new diagonal matrix with the given number of rows and columns. /// All diagonal cells of the matrix will be initialized to the provided value, all non-diagonal ones to zero. /// Zero-length matrices are not supported. /// </summary> /// <exception cref="ArgumentException">If the row or column count is less than one.</exception> public DiagonalMatrix(int rows, int columns, Complex diagonalValue) : this(rows, columns) { for (var i = 0; i < _data.Length; i++) { _data[i] = diagonalValue; } }
/// <summary> /// Initializes a new instance of the <see cref="UserEvd"/> class. This object will compute the /// the eigenvalue decomposition when the constructor is called and cache it's decomposition. /// </summary> /// <param name="matrix">The matrix to factor.</param> /// <exception cref="ArgumentNullException">If <paramref name="matrix"/> is <c>null</c>.</exception> /// <exception cref="ArgumentException">If EVD algorithm failed to converge with matrix <paramref name="matrix"/>.</exception> public UserEvd(Matrix<Complex> matrix) { if (matrix == null) { throw new ArgumentNullException("matrix"); } if (matrix.RowCount != matrix.ColumnCount) { throw new ArgumentException(Resources.ArgumentMatrixSquare); } var order = matrix.RowCount; // Initialize matricies for eigenvalues and eigenvectors MatrixEv = DenseMatrix.Identity(order); MatrixD = matrix.CreateMatrix(order, order); VectorEv = new DenseVector(order); IsSymmetric = true; for (var i = 0; IsSymmetric && i < order; i++) { for (var j = 0; IsSymmetric && j < order; j++) { IsSymmetric &= matrix.At(i, j) == matrix.At(j, i).Conjugate(); } } if (IsSymmetric) { var matrixCopy = matrix.ToArray(); var tau = new Complex[order]; var d = new double[order]; var e = new double[order]; SymmetricTridiagonalize(matrixCopy, d, e, tau, order); SymmetricDiagonalize(d, e, order); SymmetricUntridiagonalize(matrixCopy, tau, order); for (var i = 0; i < order; i++) { VectorEv[i] = new Complex(d[i], e[i]); } } else { var matrixH = matrix.ToArray(); NonsymmetricReduceToHessenberg(matrixH, order); NonsymmetricReduceHessenberToRealSchur(matrixH, order); } MatrixD.SetDiagonal(VectorEv); }
/// <summary> /// Multiplies a scalar to each element of the vector and stores the result in the result vector. /// </summary> /// <param name="scalar">The scalar to multiply.</param> /// <param name="result">The vector to store the result of the multiplication.</param> /// <remarks></remarks> protected override void DoMultiply(Complex scalar, Vector <Complex> result) { var denseResult = result as DenseVector; if (denseResult == null) { base.DoMultiply(scalar, result); } else { Control.LinearAlgebraProvider.ScaleArray(scalar, _values, denseResult.Values); } }
public void FourierBluesteinIsReversible(FourierOptions options) { var dft = new DiscreteFourierTransform(); var samples = SignalGenerator.Random((u, v) => new Complex(u, v), GetUniform(1), 0x7FFF); var work = new Complex[samples.Length]; samples.CopyTo(work, 0); dft.BluesteinForward(work, options); Assert.IsFalse(work.ListAlmostEqual(samples, 6)); dft.BluesteinInverse(work, options); AssertHelpers.ListAlmostEqual(samples, work, 10); }
/// <summary> /// Initializes a new instance of the <see cref="UserCholesky"/> class. This object will compute the /// Cholesky factorization when the constructor is called and cache it's factorization. /// </summary> /// <param name="matrix">The matrix to factor.</param> /// <exception cref="ArgumentNullException">If <paramref name="matrix"/> is <c>null</c>.</exception> /// <exception cref="ArgumentException">If <paramref name="matrix"/> is not a square matrix.</exception> /// <exception cref="ArgumentException">If <paramref name="matrix"/> is not positive definite.</exception> public UserCholesky(Matrix <Complex> matrix) { if (matrix == null) { throw new ArgumentNullException("matrix"); } if (matrix.RowCount != matrix.ColumnCount) { throw new ArgumentException(Resources.ArgumentMatrixSquare); } // Create a new matrix for the Cholesky factor, then perform factorization (while overwriting). CholeskyFactor = matrix.Clone(); var tmpColumn = new Complex[CholeskyFactor.RowCount]; // Main loop - along the diagonal for (var ij = 0; ij < CholeskyFactor.RowCount; ij++) { // "Pivot" element var tmpVal = CholeskyFactor.At(ij, ij); if (tmpVal.Real > 0.0) { tmpVal = tmpVal.SquareRoot(); CholeskyFactor.At(ij, ij, tmpVal); tmpColumn[ij] = tmpVal; // Calculate multipliers and copy to local column // Current column, below the diagonal for (var i = ij + 1; i < CholeskyFactor.RowCount; i++) { CholeskyFactor.At(i, ij, CholeskyFactor.At(i, ij) / tmpVal); tmpColumn[i] = CholeskyFactor.At(i, ij); } // Remaining columns, below the diagonal DoCholeskyStep(CholeskyFactor, CholeskyFactor.RowCount, ij + 1, CholeskyFactor.RowCount, tmpColumn, Control.NumberOfParallelWorkerThreads); } else { throw new ArgumentException(Resources.ArgumentMatrixPositiveDefinite); } for (var i = ij + 1; i < CholeskyFactor.RowCount; i++) { CholeskyFactor.At(ij, i, Complex.Zero); } } }
public void FourierDefaultTransformSatisfiesParsevalsTheorem(int count) { var samples = SignalGenerator.Random((u, v) => new Complex(u, v), GetUniform(1), count); var timeSpaceEnergy = (from s in samples select s.MagnitudeSquared()).Mean(); var work = new Complex[samples.Length]; samples.CopyTo(work, 0); // Default -> Symmetric Scaling Transform.FourierForward(work); var frequencySpaceEnergy = (from s in work select s.MagnitudeSquared()).Mean(); Assert.AreEqual(timeSpaceEnergy, frequencySpaceEnergy, 1e-12); }
public static void AlmostEqual(Complex expected, Complex actual) { if (expected.IsNaN() && actual.IsNaN() || expected.IsInfinity() && expected.IsInfinity()) { return; } if (!expected.Real.AlmostEqual(actual.Real)) { Assert.Fail("Real components are not equal. Expected:{0}; Actual:{1}", expected.Real, actual.Real); } if (!expected.Imaginary.AlmostEqual(actual.Imaginary)) { Assert.Fail("Imaginary components are not equal. Expected:{0}; Actual:{1}", expected.Imaginary, actual.Imaginary); } }
/// <summary> /// Adds a scalar to each element of the vector and stores the result in the result vector. /// Warning, the new 'sparse vector' with a non-zero scalar added to it will be a 100% filled /// sparse vector and very inefficient. Would be better to work with a dense vector instead. /// </summary> /// <param name="scalar"> /// The scalar to add. /// </param> /// <param name="result"> /// The vector to store the result of the addition. /// </param> protected override void DoAdd(Complex scalar, Vector <Complex> result) { if (scalar == Complex.Zero) { if (!ReferenceEquals(this, result)) { CopyTo(result); } return; } if (ReferenceEquals(this, result)) { //populate a new vector with the scalar var vnonZeroValues = new Complex[Count]; var vnonZeroIndices = new int[Count]; for (int index = 0; index < Count; index++) { vnonZeroIndices[index] = index; vnonZeroValues[index] = scalar; } //populate the non zero values from this var indices = _storage.Indices; var values = _storage.Values; for (int j = 0; j < _storage.ValueCount; j++) { vnonZeroValues[indices[j]] = values[j] + scalar; } //assign this vectors arrary to the new arrays. _storage.Values = vnonZeroValues; _storage.Indices = vnonZeroIndices; _storage.ValueCount = Count; } else { for (var index = 0; index < Count; index++) { result.At(index, At(index) + scalar); } } }
/// <summary> /// Returns an array of starting vectors. /// </summary> /// <param name="maximumNumberOfStartingVectors">The maximum number of starting vectors that should be created.</param> /// <param name="numberOfVariables">The number of variables.</param> /// <returns> /// An array with starting vectors. The array will never be larger than the /// <paramref name="maximumNumberOfStartingVectors"/> but it may be smaller if /// the <paramref name="numberOfVariables"/> is smaller than /// the <paramref name="maximumNumberOfStartingVectors"/>. /// </returns> static IList <Vector> CreateStartingVectors(int maximumNumberOfStartingVectors, int numberOfVariables) { // Create no more starting vectors than the size of the problem - 1 // Get random values and then orthogonalize them with // modified Gramm - Schmidt var count = NumberOfStartingVectorsToCreate(maximumNumberOfStartingVectors, numberOfVariables); // Get a random set of samples based on the standard normal distribution with // mean = 0 and sd = 1 var distribution = new Normal(); Matrix matrix = new DenseMatrix(numberOfVariables, count); for (var i = 0; i < matrix.ColumnCount; i++) { var samples = new Complex[matrix.RowCount]; var samplesRe = distribution.Samples().Take(matrix.RowCount).ToArray(); var samplesIm = distribution.Samples().Take(matrix.RowCount).ToArray(); for (int j = 0; j < matrix.RowCount; j++) { samples[j] = new Complex(samplesRe[j], samplesIm[j]); } // Set the column matrix.SetColumn(i, samples); } // Compute the orthogonalization. var gs = matrix.GramSchmidt(); var orthogonalMatrix = gs.Q; // Now transfer this to vectors var result = new List <Vector>(); for (var i = 0; i < orthogonalMatrix.ColumnCount; i++) { result.Add((Vector)orthogonalMatrix.Column(i)); // Normalize the result vector result[i].Multiply(1 / result[i].L2Norm(), result[i]); } return(result); }
/// <summary> /// Subtracts a scalar from each element of the vector and stores the result in the result vector. /// </summary> /// <param name="scalar">The scalar to subtract.</param> /// <param name="result">The vector to store the result of the subtraction.</param> protected override void DoSubtract(Complex scalar, Vector <Complex> result) { var dense = result as DenseVector; if (dense == null) { base.DoSubtract(scalar, result); } else { CommonParallel.For(0, _values.Length, 4096, (a, b) => { for (int i = a; i < b; i++) { dense._values[i] = _values[i] - scalar; } }); } }
/// <summary> /// Multiplies this matrix with another matrix and places the results into the result matrix. /// </summary> /// <param name="other">The matrix to multiply with.</param> /// <param name="result">The result of the multiplication.</param> /// <exception cref="ArgumentNullException">If the other matrix is <see langword="null" />.</exception> /// <exception cref="ArgumentNullException">If the result matrix is <see langword="null" />.</exception> /// <exception cref="ArgumentException">If <strong>this.Columns != other.Rows</strong>.</exception> /// <exception cref="ArgumentException">If the result matrix's dimensions are not the this.Rows x other.Columns.</exception> public override void Multiply(Matrix <Complex> other, Matrix <Complex> result) { if (other == null) { throw new ArgumentNullException("other"); } if (result == null) { throw new ArgumentNullException("result"); } if (ColumnCount != other.RowCount) { throw DimensionsDontMatch <ArgumentException>(this, other); } if (result.RowCount != RowCount || result.ColumnCount != other.ColumnCount) { throw DimensionsDontMatch <ArgumentException>(this, result); } var m = other as DiagonalMatrix; var r = result as DiagonalMatrix; if (m == null || r == null) { base.Multiply(other, result); } else { var thisDataCopy = new Complex[r._data.Length]; var otherDataCopy = new Complex[r._data.Length]; Array.Copy(_data, thisDataCopy, (r._data.Length > _data.Length) ? _data.Length : r._data.Length); Array.Copy(m._data, otherDataCopy, (r._data.Length > m._data.Length) ? m._data.Length : r._data.Length); Control.LinearAlgebraProvider.PointWiseMultiplyArrays(thisDataCopy, otherDataCopy, r._data); } }
/// <summary> /// Subtracts a scalar from each element of the vector and stores the result in the result vector. /// </summary> /// <param name="scalar"> /// The scalar to subtract. /// </param> /// <param name="result"> /// The vector to store the result of the subtraction. /// </param> protected override void DoSubtract(Complex scalar, Vector <Complex> result) { DoAdd(-scalar, result); }
public SparseVector(int length, Complex value) : this(SparseVectorStorage <Complex> .OfInit(length, i => value)) { }
/// <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<Complex> input, Matrix<Complex> result) { // Check for proper arguments. if (input == null) { throw new ArgumentNullException("input"); } if (result == null) { throw new ArgumentNullException("result"); } // The solution X should have the same number of columns as B if (input.ColumnCount != result.ColumnCount) { throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension); } // The dimension compatibility conditions for X = A\B require the two matrices A and B to have the same number of rows if (VectorEv.Count != input.RowCount) { throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension); } // The solution X row dimension is equal to the column dimension of A if (VectorEv.Count != result.RowCount) { throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension); } if (IsSymmetric) { var order = VectorEv.Count; var tmp = new Complex[order]; for (var k = 0; k < order; k++) { for (var j = 0; j < order; j++) { Complex value = 0.0; if (j < order) { for (var i = 0; i < order; i++) { value += MatrixEv.At(i, j).Conjugate() * input.At(i, k); } value /= VectorEv[j].Real; } tmp[j] = value; } for (var j = 0; j < order; j++) { Complex value = 0.0; for (var i = 0; i < order; i++) { value += MatrixEv.At(j, i) * 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="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> private void NonsymmetricReduceHessenberToRealSchur(Complex[,] matrixH, int order) { // Initialize var n = order - 1; var eps = Precision.DoubleMachinePrecision; 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 tst1 = Math.Abs(matrixH[l - 1, l - 1].Real) + Math.Abs(matrixH[l - 1, l - 1].Imaginary) + Math.Abs(matrixH[l, l].Real) + Math.Abs(matrixH[l, l].Imaginary); if (Math.Abs(matrixH[l, l - 1].Real) < eps * tst1) { break; } l--; } // Check for convergence // One root found if (l == n) { matrixH[n, n] += exshift; VectorEv[n] = matrixH[n, n]; n--; iter = 0; } else { // Form shift Complex s; if (iter != 10 && iter != 20) { s = matrixH[n, n]; x = matrixH[n - 1, n] * matrixH[n, n - 1].Real; if (x.Real != 0.0 || x.Imaginary != 0.0) { y = (matrixH[n - 1, n - 1] - s) / 2.0; z = ((y * y) + x).SquareRoot(); if ((y.Real * z.Real) + (y.Imaginary * z.Imaginary) < 0.0) { z *= -1.0; } x /= y + z; s = s - x; } } else { // Form exceptional shift s = Math.Abs(matrixH[n, n - 1].Real) + Math.Abs(matrixH[n - 1, n - 2].Real); } for (var i = 0; i <= n; i++) { matrixH[i, i] -= s; } exshift += s; iter++; // Reduce to triangle (rows) for (var i = l + 1; i <= n; i++) { s = matrixH[i, i - 1].Real; norm = SpecialFunctions.Hypotenuse(matrixH[i - 1, i - 1].Magnitude, s.Real); x = matrixH[i - 1, i - 1] / norm; VectorEv[i - 1] = x; matrixH[i - 1, i - 1] = norm; matrixH[i, i - 1] = new Complex(0.0, s.Real / norm); for (var j = i; j < order; j++) { y = matrixH[i - 1, j]; z = matrixH[i, j]; matrixH[i - 1, j] = (x.Conjugate() * y) + (matrixH[i, i - 1].Imaginary * z); matrixH[i, j] = (x * z) - (matrixH[i, i - 1].Imaginary * y); } } s = matrixH[n, n]; if (s.Imaginary != 0.0) { s /= matrixH[n, n].Magnitude; matrixH[n, n] = matrixH[n, n].Magnitude; for (var j = n + 1; j < order; j++) { matrixH[n, j] *= s.Conjugate(); } } // Inverse operation (columns). for (var j = l + 1; j <= n; j++) { x = VectorEv[j - 1]; for (var i = 0; i <= j; i++) { z = matrixH[i, j]; if (i != j) { y = matrixH[i, j - 1]; matrixH[i, j - 1] = (x * y) + (matrixH[j, j - 1].Imaginary * z); } else { y = matrixH[i, j - 1].Real; matrixH[i, j - 1] = new Complex((x.Real * y.Real) - (x.Imaginary * y.Imaginary) + (matrixH[j, j - 1].Imaginary * z.Real), matrixH[i, j - 1].Imaginary); } matrixH[i, j] = (x.Conjugate() * z) - (matrixH[j, j - 1].Imaginary * y); } for (var i = 0; i < order; i++) { y = MatrixEv.At(i, j - 1); z = MatrixEv.At(i, j); MatrixEv.At(i, j - 1, (x * y) + (matrixH[j, j - 1].Imaginary * z)); MatrixEv.At(i, j, (x.Conjugate() * z) - (matrixH[j, j - 1].Imaginary * y)); } } if (s.Imaginary != 0.0) { for (var i = 0; i <= n; i++) { matrixH[i, n] *= s; } for (var i = 0; i < order; i++) { MatrixEv.At(i, n, MatrixEv.At(i, n) * s); } } } } // All roots found. // Backsubstitute to find vectors of upper triangular form norm = 0.0; for (var i = 0; i < order; i++) { for (var j = i; j < order; j++) { norm = Math.Max(norm, Math.Abs(matrixH[i, j].Real) + Math.Abs(matrixH[i, j].Imaginary)); } } if (order == 1) { return; } if (norm == 0.0) { return; } for (n = order - 1; n > 0; n--) { x = VectorEv[n]; matrixH[n, n] = 1.0; for (var i = n - 1; i >= 0; i--) { z = 0.0; for (var j = i + 1; j <= n; j++) { z += matrixH[i, j] * matrixH[j, n]; } y = x - VectorEv[i]; if (y.Real == 0.0 && y.Imaginary == 0.0) { y = eps * norm; } matrixH[i, n] = z / y; // Overflow control var tr = Math.Abs(matrixH[i, n].Real) + Math.Abs(matrixH[i, n].Imaginary); if ((eps * tr) * tr > 1) { for (var j = i; j <= n; j++) { matrixH[j, n] = matrixH[j, n] / tr; } } } } // Back transformation to get eigenvectors of original matrix for (var j = order - 1; j > 0; j--) { for (var i = 0; i < order; i++) { z = Complex.Zero; for (var k = 0; k <= j; k++) { z += MatrixEv.At(i, k) * matrixH[k, j]; } MatrixEv.At(i, j, z); } } }
/// <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> private static void SymmetricTridiagonalize(Complex[,] matrixA, double[] d, double[] e, Complex[] tau, int order) { double hh; tau[order - 1] = Complex.One; for (var i = 0; i < order; i++) { d[i] = matrixA[i, i].Real; } // Householder reduction to tridiagonal form. for (var i = order - 1; i > 0; i--) { // Scale to avoid under/overflow. var scale = 0.0; var h = 0.0; for (var k = 0; k < i; k++) { scale = scale + Math.Abs(matrixA[i, k].Real) + Math.Abs(matrixA[i, k].Imaginary); } if (scale == 0.0) { tau[i - 1] = Complex.One; e[i] = 0.0; } else { for (var k = 0; k < i; k++) { matrixA[i, k] /= scale; h += matrixA[i, k].MagnitudeSquared(); } Complex g = Math.Sqrt(h); e[i] = scale * g.Real; Complex temp; var f = matrixA[i, i - 1]; if (f.Magnitude != 0) { temp = -(matrixA[i, i - 1].Conjugate() * tau[i].Conjugate()) / f.Magnitude; h += f.Magnitude * g.Real; g = 1.0 + (g / f.Magnitude); matrixA[i, i - 1] *= g; } else { temp = -tau[i].Conjugate(); matrixA[i, i - 1] = g; } if ((f.Magnitude == 0) || (i != 1)) { f = Complex.Zero; for (var j = 0; j < i; j++) { var tmp = Complex.Zero; // Form element of A*U. for (var k = 0; k <= j; k++) { tmp += matrixA[j, k] * matrixA[i, k].Conjugate(); } for (var k = j + 1; k <= i - 1; k++) { tmp += matrixA[k, j].Conjugate() * matrixA[i, k].Conjugate(); } // Form element of P tau[j] = tmp / h; f += (tmp / h) * matrixA[i, j]; } hh = f.Real / (h + h); // Form the reduced A. for (var j = 0; j < i; j++) { f = matrixA[i, j].Conjugate(); g = tau[j] - (hh * f); tau[j] = g.Conjugate(); for (var k = 0; k <= j; k++) { matrixA[j, k] -= (f * tau[k]) + (g * matrixA[i, k]); } } } for (var k = 0; k < i; k++) { matrixA[i, k] *= scale; } tau[i - 1] = temp.Conjugate(); } hh = d[i]; d[i] = matrixA[i, i].Real; matrixA[i, i] = new Complex(hh, scale * Math.Sqrt(h)); } hh = d[0]; d[0] = matrixA[0, 0].Real; matrixA[0, 0] = hh; e[0] = 0.0; }
/// <summary> /// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the /// solution vector and x is the unknown vector. /// </summary> /// <param name="matrix">The coefficient <see cref="Matrix"/>, <c>A</c>.</param> /// <param name="input">The solution <see cref="Vector"/>, <c>b</c>.</param> /// <param name="result">The result <see cref="Vector"/>, <c>x</c>.</param> public void Solve(Matrix matrix, Vector input, Vector result) { // If we were stopped before, we are no longer // We're doing this at the start of the method to ensure // that we can use these fields immediately. _hasBeenStopped = false; // Parameters checks if (matrix == null) { throw new ArgumentNullException("matrix"); } if (matrix.RowCount != matrix.ColumnCount) { throw new ArgumentException(Resources.ArgumentMatrixSquare, "matrix"); } if (input == null) { throw new ArgumentNullException("input"); } if (result == null) { throw new ArgumentNullException("result"); } if (result.Count != input.Count) { throw new ArgumentException(Resources.ArgumentVectorsSameLength); } if (input.Count != matrix.RowCount) { throw Matrix.DimensionsDontMatch <ArgumentException>(input, result); } // Initialize the solver fields // Set the convergence monitor if (_iterator == null) { _iterator = Iterator.CreateDefault(); } if (_preconditioner == null) { _preconditioner = new UnitPreconditioner(); } _preconditioner.Initialize(matrix); // Compute r_0 = b - Ax_0 for some initial guess x_0 // In this case we take x_0 = vector // This is basically a SAXPY so it could be made a lot faster Vector residuals = new DenseVector(matrix.RowCount); CalculateTrueResidual(matrix, residuals, result, input); // Choose r~ (for example, r~ = r_0) var tempResiduals = residuals.Clone(); // create seven temporary vectors needed to hold temporary // coefficients. All vectors are mangled in each iteration. // These are defined here to prevent stressing the garbage collector Vector vecP = new DenseVector(residuals.Count); Vector vecPdash = new DenseVector(residuals.Count); Vector nu = new DenseVector(residuals.Count); Vector vecS = new DenseVector(residuals.Count); Vector vecSdash = new DenseVector(residuals.Count); Vector temp = new DenseVector(residuals.Count); Vector temp2 = new DenseVector(residuals.Count); // create some temporary double variables that are needed // to hold values in between iterations Complex currentRho = 0; Complex alpha = 0; Complex omega = 0; var iterationNumber = 0; while (ShouldContinue(iterationNumber, result, input, residuals)) { // rho_(i-1) = r~^T r_(i-1) // dotproduct r~ and r_(i-1) var oldRho = currentRho; currentRho = tempResiduals.ConjugateDotProduct(residuals); // if (rho_(i-1) == 0) // METHOD FAILS // If rho is only 1 ULP from zero then we fail. if (currentRho.Real.AlmostEqual(0, 1) && currentRho.Imaginary.AlmostEqual(0, 1)) { // Rho-type breakdown throw new Exception("Iterative solver experience a numerical break down"); } if (iterationNumber != 0) { // beta_(i-1) = (rho_(i-1)/rho_(i-2))(alpha_(i-1)/omega(i-1)) var beta = (currentRho / oldRho) * (alpha / omega); // p_i = r_(i-1) + beta_(i-1)(p_(i-1) - omega_(i-1) * nu_(i-1)) nu.Multiply(-omega, temp); vecP.Add(temp, temp2); temp2.CopyTo(vecP); vecP.Multiply(beta, vecP); vecP.Add(residuals, temp2); temp2.CopyTo(vecP); } else { // p_i = r_(i-1) residuals.CopyTo(vecP); } // SOLVE Mp~ = p_i // M = preconditioner _preconditioner.Approximate(vecP, vecPdash); // nu_i = Ap~ matrix.Multiply(vecPdash, nu); // alpha_i = rho_(i-1)/ (r~^T nu_i) = rho / dotproduct(r~ and nu_i) alpha = currentRho * 1 / tempResiduals.ConjugateDotProduct(nu); // s = r_(i-1) - alpha_i nu_i nu.Multiply(-alpha, temp); residuals.Add(temp, vecS); // Check if we're converged. If so then stop. Otherwise continue; // Calculate the temporary result. // Be careful not to change any of the temp vectors, except for // temp. Others will be used in the calculation later on. // x_i = x_(i-1) + alpha_i * p^_i + s^_i vecPdash.Multiply(alpha, temp); temp.Add(vecSdash, temp2); temp2.CopyTo(temp); temp.Add(result, temp2); temp2.CopyTo(temp); // Check convergence and stop if we are converged. if (!ShouldContinue(iterationNumber, temp, input, vecS)) { temp.CopyTo(result); // Calculate the true residual CalculateTrueResidual(matrix, residuals, result, input); // Now recheck the convergence if (!ShouldContinue(iterationNumber, result, input, residuals)) { // We're all good now. return; } // Continue the calculation iterationNumber++; continue; } // SOLVE Ms~ = s _preconditioner.Approximate(vecS, vecSdash); // temp = As~ matrix.Multiply(vecSdash, temp); // omega_i = temp^T s / temp^T temp omega = temp.ConjugateDotProduct(vecS) / temp.ConjugateDotProduct(temp); // x_i = x_(i-1) + alpha_i p^ + omega_i s^ temp.Multiply(-omega, residuals); residuals.Add(vecS, temp2); temp2.CopyTo(residuals); vecSdash.Multiply(omega, temp); result.Add(temp, temp2); temp2.CopyTo(result); vecPdash.Multiply(alpha, temp); result.Add(temp, temp2); temp2.CopyTo(result); // for continuation it is necessary that omega_i != 0.0 // If omega is only 1 ULP from zero then we fail. if (omega.Real.AlmostEqual(0, 1) && omega.Imaginary.AlmostEqual(0, 1)) { // Omega-type breakdown throw new Exception("Iterative solver experience a numerical break down"); } if (!ShouldContinue(iterationNumber, result, input, residuals)) { // Recalculate the residuals and go round again. This is done to ensure that // we have the proper residuals. // The residual calculation based on omega_i * s can be off by a factor 10. So here // we calculate the real residual (which can be expensive) but we only do it if we're // sufficiently close to the finish. CalculateTrueResidual(matrix, residuals, result, input); } iterationNumber++; } }
/// <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 <Complex> input, Matrix <Complex> result) { // Check for proper arguments. if (input == null) { throw new ArgumentNullException("input"); } if (result == null) { throw new ArgumentNullException("result"); } if (!ComputeVectors) { throw new InvalidOperationException(Resources.SingularVectorsNotComputed); } // 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 (MatrixU.RowCount != input.RowCount) { throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension); } // The solution X row dimension is equal to the column dimension of A if (MatrixVT.ColumnCount != result.RowCount) { throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension); } var mn = Math.Min(MatrixU.RowCount, MatrixVT.ColumnCount); var bn = input.ColumnCount; var tmp = new Complex[MatrixVT.ColumnCount]; for (var k = 0; k < bn; k++) { for (var j = 0; j < MatrixVT.ColumnCount; j++) { var value = Complex.Zero; if (j < mn) { for (var i = 0; i < MatrixU.RowCount; i++) { value += MatrixU.At(i, j).Conjugate() * input.At(i, k); } value /= VectorS[j]; } tmp[j] = value; } for (var j = 0; j < MatrixVT.ColumnCount; j++) { var value = Complex.Zero; for (var i = 0; i < MatrixVT.ColumnCount; i++) { value += MatrixVT.At(i, j).Conjugate() * tmp[i]; } result.At(j, k, value); } } }
public void CanDetermineIfZeroValueComplexNumber() { var complex = new Complex(0, 0); Assert.IsTrue(complex.IsZero(), "Zero complex number."); }
public static void AlmostEqualRelative(Complex expected, Complex actual, int decimalPlaces) { if (!expected.Real.AlmostEqualRelative(actual.Real, decimalPlaces)) { Assert.Fail("Real components are not equal within {0} places. Expected:{1}; Actual:{2}", decimalPlaces, expected.Real, actual.Real); } if (!expected.Imaginary.AlmostEqualRelative(actual.Imaginary, decimalPlaces)) { Assert.Fail("Imaginary components are not equal within {0} places. Expected:{1}; Actual:{2}", decimalPlaces, expected.Imaginary, actual.Imaginary); } }
/// <summary> /// Scale column <paramref name="column"/> by <paramref name="z"/> starting from row <paramref name="rowStart"/> /// </summary> /// <param name="a">Source matrix</param> /// <param name="rowCount">The number of rows in <paramref name="a"/> </param> /// <param name="column">Column to scale</param> /// <param name="rowStart">Row to scale from</param> /// <param name="z">Scale value</param> private static void CscalColumn(Matrix <Complex> a, int rowCount, int column, int rowStart, Complex z) { for (var i = rowStart; i < rowCount; i++) { a.At(i, column, a.At(i, column) * z); } }
/// <summary> /// Initializes a new instance of the <see cref="UserSvd"/> class. This object will compute the /// the singular value decomposition when the constructor is called and cache it's decomposition. /// </summary> /// <param name="matrix">The matrix to factor.</param> /// <param name="computeVectors">Compute the singular U and VT vectors or not.</param> /// <exception cref="ArgumentNullException">If <paramref name="matrix"/> is <c>null</c>.</exception> /// <exception cref="NonConvergenceException"></exception> public UserSvd(Matrix <Complex> matrix, bool computeVectors) { if (matrix == null) { throw new ArgumentNullException("matrix"); } ComputeVectors = computeVectors; var nm = Math.Min(matrix.RowCount + 1, matrix.ColumnCount); var matrixCopy = matrix.Clone(); VectorS = matrixCopy.CreateVector(nm); MatrixU = matrixCopy.CreateMatrix(matrixCopy.RowCount, matrixCopy.RowCount); MatrixVT = matrixCopy.CreateMatrix(matrixCopy.ColumnCount, matrixCopy.ColumnCount); const int Maxiter = 1000; var e = new Complex[matrixCopy.ColumnCount]; var work = new Complex[matrixCopy.RowCount]; int i, j; int l, lp1; var cs = 0.0; var sn = 0.0; Complex t; var ncu = matrixCopy.RowCount; // Reduce matrixCopy to bidiagonal form, storing the diagonal elements // In s and the super-diagonal elements in e. var nct = Math.Min(matrixCopy.RowCount - 1, matrixCopy.ColumnCount); var nrt = Math.Max(0, Math.Min(matrixCopy.ColumnCount - 2, matrixCopy.RowCount)); var lu = Math.Max(nct, nrt); for (l = 0; l < lu; l++) { lp1 = l + 1; if (l < nct) { // Compute the transformation for the l-th column and place the l-th diagonal in VectorS[l]. VectorS[l] = Cnrm2Column(matrixCopy, matrixCopy.RowCount, l, l); if (VectorS[l].Magnitude != 0.0) { if (matrixCopy.At(l, l).Magnitude != 0.0) { VectorS[l] = Csign(VectorS[l], matrixCopy.At(l, l)); } CscalColumn(matrixCopy, matrixCopy.RowCount, l, l, 1.0 / VectorS[l]); matrixCopy.At(l, l, (Complex.One + matrixCopy.At(l, l))); } VectorS[l] = -VectorS[l]; } for (j = lp1; j < matrixCopy.ColumnCount; j++) { if (l < nct) { if (VectorS[l].Magnitude != 0.0) { // Apply the transformation. t = -Cdotc(matrixCopy, matrixCopy.RowCount, l, j, l) / matrixCopy.At(l, l); if (t != Complex.Zero) { for (var ii = l; ii < matrixCopy.RowCount; ii++) { matrixCopy.At(ii, j, matrixCopy.At(ii, j) + (t * matrixCopy.At(ii, l))); } } } } // Place the l-th row of matrixCopy into e for the // Subsequent calculation of the row transformation. e[j] = matrixCopy.At(l, j).Conjugate(); } if (ComputeVectors && l < nct) { // Place the transformation in u for subsequent back multiplication. for (i = l; i < matrixCopy.RowCount; i++) { MatrixU.At(i, l, matrixCopy.At(i, l)); } } if (l >= nrt) { continue; } // Compute the l-th row transformation and place the l-th super-diagonal in e(l). var enorm = Cnrm2Vector(e, lp1); e[l] = enorm; if (e[l].Magnitude != 0.0) { if (e[lp1].Magnitude != 0.0) { e[l] = Csign(e[l], e[lp1]); } CscalVector(e, lp1, 1.0 / e[l]); e[lp1] = Complex.One + e[lp1]; } e[l] = -e[l].Conjugate(); if (lp1 < matrixCopy.RowCount && e[l].Magnitude != 0.0) { // Apply the transformation. for (i = lp1; i < matrixCopy.RowCount; i++) { work[i] = Complex.Zero; } for (j = lp1; j < matrixCopy.ColumnCount; j++) { if (e[j] != Complex.Zero) { for (var ii = lp1; ii < matrixCopy.RowCount; ii++) { work[ii] += e[j] * matrixCopy.At(ii, j); } } } for (j = lp1; j < matrixCopy.ColumnCount; j++) { var ww = (-e[j] / e[lp1]).Conjugate(); if (ww != Complex.Zero) { for (var ii = lp1; ii < matrixCopy.RowCount; ii++) { matrixCopy.At(ii, j, matrixCopy.At(ii, j) + (ww * work[ii])); } } } } if (ComputeVectors) { // Place the transformation in v for subsequent back multiplication. for (i = lp1; i < matrixCopy.ColumnCount; i++) { MatrixVT.At(i, l, e[i]); } } } // Set up the final bidiagonal matrixCopy or order m. var m = Math.Min(matrixCopy.ColumnCount, matrixCopy.RowCount + 1); var nctp1 = nct + 1; var nrtp1 = nrt + 1; if (nct < matrixCopy.ColumnCount) { VectorS[nctp1 - 1] = matrixCopy.At((nctp1 - 1), (nctp1 - 1)); } if (matrixCopy.RowCount < m) { VectorS[m - 1] = Complex.Zero; } if (nrtp1 < m) { e[nrtp1 - 1] = matrixCopy.At((nrtp1 - 1), (m - 1)); } e[m - 1] = Complex.Zero; // If required, generate u. if (ComputeVectors) { for (j = nctp1 - 1; j < ncu; j++) { for (i = 0; i < matrixCopy.RowCount; i++) { MatrixU.At(i, j, Complex.Zero); } MatrixU.At(j, j, Complex.One); } for (l = nct - 1; l >= 0; l--) { if (VectorS[l].Magnitude != 0.0) { for (j = l + 1; j < ncu; j++) { t = -Cdotc(MatrixU, matrixCopy.RowCount, l, j, l) / MatrixU.At(l, l); if (t != Complex.Zero) { for (var ii = l; ii < matrixCopy.RowCount; ii++) { MatrixU.At(ii, j, MatrixU.At(ii, j) + (t * MatrixU.At(ii, l))); } } } CscalColumn(MatrixU, matrixCopy.RowCount, l, l, -1.0); MatrixU.At(l, l, Complex.One + MatrixU.At(l, l)); for (i = 0; i < l; i++) { MatrixU.At(i, l, Complex.Zero); } } else { for (i = 0; i < matrixCopy.RowCount; i++) { MatrixU.At(i, l, Complex.Zero); } MatrixU.At(l, l, Complex.One); } } } // If it is required, generate v. if (ComputeVectors) { for (l = matrixCopy.ColumnCount - 1; l >= 0; l--) { lp1 = l + 1; if (l < nrt) { if (e[l].Magnitude != 0.0) { for (j = lp1; j < matrixCopy.ColumnCount; j++) { t = -Cdotc(MatrixVT, matrixCopy.ColumnCount, l, j, lp1) / MatrixVT.At(lp1, l); if (t != Complex.Zero) { for (var ii = l; ii < matrixCopy.ColumnCount; ii++) { MatrixVT.At(ii, j, MatrixVT.At(ii, j) + (t * MatrixVT.At(ii, l))); } } } } } for (i = 0; i < matrixCopy.ColumnCount; i++) { MatrixVT.At(i, l, Complex.Zero); } MatrixVT.At(l, l, Complex.One); } } // Transform s and e so that they are real . for (i = 0; i < m; i++) { Complex r; if (VectorS[i].Magnitude != 0.0) { t = VectorS[i].Magnitude; r = VectorS[i] / t; VectorS[i] = t; if (i < m - 1) { e[i] = e[i] / r; } if (ComputeVectors) { CscalColumn(MatrixU, matrixCopy.RowCount, i, 0, r); } } // Exit if (i == m - 1) { break; } if (e[i].Magnitude != 0.0) { t = e[i].Magnitude; r = t / e[i]; e[i] = t; VectorS[i + 1] = VectorS[i + 1] * r; if (ComputeVectors) { CscalColumn(MatrixVT, matrixCopy.ColumnCount, i + 1, 0, r); } } } // Main iteration loop for the singular values. var mn = m; var iter = 0; while (m > 0) { // Quit if all the singular values have been found. If too many iterations have been performed, // throw exception that Convergence Failed if (iter >= Maxiter) { throw new NonConvergenceException(); } // This section of the program inspects for negligible elements in the s and e arrays. On // completion the variables kase and l are set as follows. // Kase = 1 if VectorS[m] and e[l-1] are negligible and l < m // Kase = 2 if VectorS[l] is negligible and l < m // Kase = 3 if e[l-1] is negligible, l < m, and VectorS[l, ..., VectorS[m] are not negligible (qr step). // Лase = 4 if e[m-1] is negligible (convergence). double ztest; double test; for (l = m - 2; l >= 0; l--) { test = VectorS[l].Magnitude + VectorS[l + 1].Magnitude; ztest = test + e[l].Magnitude; if (ztest.AlmostEqualInDecimalPlaces(test, 15)) { e[l] = Complex.Zero; break; } } int kase; if (l == m - 2) { kase = 4; } else { int ls; for (ls = m - 1; ls > l; ls--) { test = 0.0; if (ls != m - 1) { test = test + e[ls].Magnitude; } if (ls != l + 1) { test = test + e[ls - 1].Magnitude; } ztest = test + VectorS[ls].Magnitude; if (ztest.AlmostEqualInDecimalPlaces(test, 15)) { VectorS[ls] = Complex.Zero; break; } } if (ls == l) { kase = 3; } else if (ls == m - 1) { kase = 1; } else { kase = 2; l = ls; } } l = l + 1; // Perform the task indicated by kase. int k; double f; switch (kase) { // Deflate negligible VectorS[m]. case 1: f = e[m - 2].Real; e[m - 2] = Complex.Zero; double t1; for (var kk = l; kk < m - 1; kk++) { k = m - 2 - kk + l; t1 = VectorS[k].Real; Srotg(ref t1, ref f, ref cs, ref sn); VectorS[k] = t1; if (k != l) { f = -sn * e[k - 1].Real; e[k - 1] = cs * e[k - 1]; } if (ComputeVectors) { Csrot(MatrixVT, matrixCopy.ColumnCount, k, m - 1, cs, sn); } } break; // Split at negligible VectorS[l]. case 2: f = e[l - 1].Real; e[l - 1] = Complex.Zero; for (k = l; k < m; k++) { t1 = VectorS[k].Real; Srotg(ref t1, ref f, ref cs, ref sn); VectorS[k] = t1; f = -sn * e[k].Real; e[k] = cs * e[k]; if (ComputeVectors) { Csrot(MatrixU, matrixCopy.RowCount, k, l - 1, cs, sn); } } break; // Perform one qr step. case 3: // Calculate the shift. var scale = 0.0; scale = Math.Max(scale, VectorS[m - 1].Magnitude); scale = Math.Max(scale, VectorS[m - 2].Magnitude); scale = Math.Max(scale, e[m - 2].Magnitude); scale = Math.Max(scale, VectorS[l].Magnitude); scale = Math.Max(scale, e[l].Magnitude); var sm = VectorS[m - 1].Real / scale; var smm1 = VectorS[m - 2].Real / scale; var emm1 = e[m - 2].Real / scale; var sl = VectorS[l].Real / scale; var el = e[l].Real / scale; var b = (((smm1 + sm) * (smm1 - sm)) + (emm1 * emm1)) / 2.0; var c = (sm * emm1) * (sm * emm1); var shift = 0.0; if (b != 0.0 || c != 0.0) { shift = Math.Sqrt((b * b) + c); if (b < 0.0) { shift = -shift; } shift = c / (b + shift); } f = ((sl + sm) * (sl - sm)) + shift; var g = sl * el; // Chase zeros. for (k = l; k < m - 1; k++) { Srotg(ref f, ref g, ref cs, ref sn); if (k != l) { e[k - 1] = f; } f = (cs * VectorS[k].Real) + (sn * e[k].Real); e[k] = (cs * e[k]) - (sn * VectorS[k]); g = sn * VectorS[k + 1].Real; VectorS[k + 1] = cs * VectorS[k + 1]; if (ComputeVectors) { Csrot(MatrixVT, matrixCopy.ColumnCount, k, k + 1, cs, sn); } Srotg(ref f, ref g, ref cs, ref sn); VectorS[k] = f; f = (cs * e[k].Real) + (sn * VectorS[k + 1].Real); VectorS[k + 1] = (-sn * e[k]) + (cs * VectorS[k + 1]); g = sn * e[k + 1].Real; e[k + 1] = cs * e[k + 1]; if (ComputeVectors && k < matrixCopy.RowCount) { Csrot(MatrixU, matrixCopy.RowCount, k, k + 1, cs, sn); } } e[m - 2] = f; iter = iter + 1; break; // Convergence. case 4: // Make the singular value positive if (VectorS[l].Real < 0.0) { VectorS[l] = -VectorS[l]; if (ComputeVectors) { CscalColumn(MatrixVT, matrixCopy.ColumnCount, l, 0, -1.0); } } // Order the singular value. while (l != mn - 1) { if (VectorS[l].Real >= VectorS[l + 1].Real) { break; } t = VectorS[l]; VectorS[l] = VectorS[l + 1]; VectorS[l + 1] = t; if (ComputeVectors && l < matrixCopy.ColumnCount) { Swap(MatrixVT, matrixCopy.ColumnCount, l, l + 1); } if (ComputeVectors && l < matrixCopy.RowCount) { Swap(MatrixU, matrixCopy.RowCount, l, l + 1); } l = l + 1; } iter = 0; m = m - 1; break; } } if (ComputeVectors) { MatrixVT = MatrixVT.ConjugateTranspose(); } // Adjust the size of s if rows < columns. We are using ported copy of linpack's svd code and it uses // a singular vector of length mRows+1 when mRows < mColumns. The last element is not used and needs to be removed. // we should port lapack's svd routine to remove this problem. if (matrixCopy.RowCount < matrixCopy.ColumnCount) { nm--; var tmp = matrixCopy.CreateVector(nm); for (i = 0; i < nm; i++) { tmp[i] = VectorS[i]; } VectorS = tmp; } }
/// <summary> /// Initializes a new instance of the <see cref="UserLU"/> class. This object will compute the /// LU factorization when the constructor is called and cache it's factorization. /// </summary> /// <param name="matrix">The matrix to factor.</param> /// <exception cref="ArgumentNullException">If <paramref name="matrix"/> is <c>null</c>.</exception> /// <exception cref="ArgumentException">If <paramref name="matrix"/> is not a square matrix.</exception> public UserLU(Matrix <Complex> matrix) { if (matrix == null) { throw new ArgumentNullException("matrix"); } if (matrix.RowCount != matrix.ColumnCount) { throw new ArgumentException(Resources.ArgumentMatrixSquare); } // Create an array for the pivot indices. var order = matrix.RowCount; Factors = matrix.Clone(); Pivots = new int[order]; // Initialize the pivot matrix to the identity permutation. for (var i = 0; i < order; i++) { Pivots[i] = i; } var vectorLUcolj = new Complex[order]; for (var j = 0; j < order; j++) { // Make a copy of the j-th column to localize references. for (var i = 0; i < order; i++) { vectorLUcolj[i] = Factors.At(i, j); } // Apply previous transformations. for (var i = 0; i < order; i++) { var kmax = Math.Min(i, j); var s = Complex.Zero; for (var k = 0; k < kmax; k++) { s += Factors.At(i, k) * vectorLUcolj[k]; } vectorLUcolj[i] -= s; Factors.At(i, j, vectorLUcolj[i]); } // Find pivot and exchange if necessary. var p = j; for (var i = j + 1; i < order; i++) { if (vectorLUcolj[i].Magnitude > vectorLUcolj[p].Magnitude) { p = i; } } if (p != j) { for (var k = 0; k < order; k++) { var temp = Factors.At(p, k); Factors.At(p, k, Factors.At(j, k)); Factors.At(j, k, temp); } Pivots[j] = p; } // Compute multipliers. if (j < order & Factors.At(j, j) != 0.0) { for (var i = j + 1; i < order; i++) { Factors.At(i, j, (Factors.At(i, j) / Factors.At(j, j))); } } } }
public void CanDetermineIfRealNumber() { var complex = new Complex(-1, 0); Assert.IsTrue(complex.IsReal(), "Is a real number."); }
public void CanComputeNaturalLogarithm(double real, double imag, double expectedReal, double expectedImag) { var value = new Complex(real, imag); var expected = new Complex(expectedReal, expectedImag); AssertHelpers.AlmostEqualRelative(expected, value.Ln(), 14); }
public void CanComputeExponential(double real, double imag, double expectedReal, double expectedImag) { var value = new Complex(real, imag); var expected = new Complex(expectedReal, expectedImag); AssertHelpers.AlmostEqualRelative(expected, value.Exp(), 15); }
public void CanComputeRoot() { var a = new Complex(0.0, -1.19209289550780998537e-7); var b = new Complex(0.0, 0.5); AssertHelpers.AlmostEqualRelative(new Complex(0.038550761943650161, 0.019526430428319544), a.Root(b), 13); a = new Complex(0.0, 0.5); b = new Complex(0.0, -0.5); AssertHelpers.AlmostEqualRelative(new Complex(0.007927894711475968, -0.042480480425152213), a.Root(b), 13); a = new Complex(0.0, -0.5); b = new Complex(0.0, 1.0); AssertHelpers.AlmostEqualRelative(new Complex(0.15990905692806806, 0.13282699942462053), a.Root(b), 13); a = new Complex(0.0, 2.0); b = new Complex(0.0, -2.0); AssertHelpers.AlmostEqualRelative(new Complex(0.42882900629436788, 0.15487175246424678), a.Root(b), 13); //a = new Complex(1.19209289550780998537e-7, 1.19209289550780998537e-7); //b = new Complex(1.19209289550780998537e-7, 1.19209289550780998537e-7); //AssertHelpers.AlmostEqual(new Complex(0.0, 0.0), a.Root(b), 15); a = new Complex(0.0, -8.388608e6); b = new Complex(1.19209289550780998537e-7, 0.0); AssertHelpers.AlmostEqualRelative(new Complex(double.PositiveInfinity, double.NegativeInfinity), a.Root(b), 14); }
public void CanComputePower() { var a = new Complex(1.19209289550780998537e-7, 1.19209289550780998537e-7); var b = new Complex(1.19209289550780998537e-7, 1.19209289550780998537e-7); AssertHelpers.AlmostEqualRelative( new Complex(9.99998047207974718744e-1, -1.76553541154378695012e-6), a.Power(b), 14); a = new Complex(0.0, 1.19209289550780998537e-7); b = new Complex(0.0, -1.19209289550780998537e-7); AssertHelpers.AlmostEqualRelative(new Complex(1.00000018725172576491, 1.90048076369011843105e-6), a.Power(b), 14); a = new Complex(0.0, -1.19209289550780998537e-7); b = new Complex(0.0, 0.5); AssertHelpers.AlmostEqualRelative(new Complex(-2.56488189382693049636e-1, -2.17823120666116144959), a.Power(b), 14); a = new Complex(0.0, 0.5); b = new Complex(0.0, -0.5); AssertHelpers.AlmostEqualRelative(new Complex(2.06287223508090495171, 7.45007062179724087859e-1), a.Power(b), 14); a = new Complex(0.0, -0.5); b = new Complex(0.0, 1.0); AssertHelpers.AlmostEqualRelative(new Complex(3.70040633557002510874, -3.07370876701949232239), a.Power(b), 14); a = new Complex(0.0, 2.0); b = new Complex(0.0, -2.0); AssertHelpers.AlmostEqualRelative(new Complex(4.24532146387429353891, -2.27479427903521192648e1), a.Power(b), 14); a = new Complex(0.0, -8.388608e6); b = new Complex(1.19209289550780998537e-7, 0.0); AssertHelpers.AlmostEqualRelative(new Complex(1.00000190048219620166, -1.87253870018168043834e-7), a.Power(b), 14); a = new Complex(0.0, 0.0); b = new Complex(0.0, 0.0); AssertHelpers.AlmostEqualRelative(new Complex(1.0, 0.0), a.Power(b), 14); a = new Complex(0.0, 0.0); b = new Complex(1.0, 0.0); AssertHelpers.AlmostEqualRelative(new Complex(0.0, 0.0), a.Power(b), 14); a = new Complex(0.0, 0.0); b = new Complex(-1.0, 0.0); AssertHelpers.AlmostEqualRelative(new Complex(double.PositiveInfinity, 0.0), a.Power(b), 14); a = new Complex(0.0, 0.0); b = new Complex(-1.0, 1.0); AssertHelpers.AlmostEqualRelative(new Complex(double.PositiveInfinity, double.PositiveInfinity), a.Power(b), 14); a = new Complex(0.0, 0.0); b = new Complex(0.0, 1.0); Assert.That(a.Power(b).IsNaN()); }
/// <summary> /// Initializes a new instance of the <see cref="UserEvd"/> class. This object will compute the /// the eigenvalue decomposition when the constructor is called and cache it's decomposition. /// </summary> /// <param name="matrix">The matrix to factor.</param> /// <exception cref="ArgumentNullException">If <paramref name="matrix"/> is <c>null</c>.</exception> /// <exception cref="ArgumentException">If EVD algorithm failed to converge with matrix <paramref name="matrix"/>.</exception> public UserEvd(Matrix <double> matrix) { if (matrix == null) { throw new ArgumentNullException("matrix"); } if (matrix.RowCount != matrix.ColumnCount) { throw new ArgumentException(Resources.ArgumentMatrixSquare); } var order = matrix.RowCount; // Initialize matricies for eigenvalues and eigenvectors MatrixEv = matrix.CreateMatrix(order, order); MatrixD = matrix.CreateMatrix(order, order); VectorEv = new LinearAlgebra.Complex.DenseVector(order); IsSymmetric = true; for (var i = 0; IsSymmetric && i < order; i++) { for (var j = 0; IsSymmetric && j < order; j++) { IsSymmetric &= matrix.At(i, j) == matrix.At(j, i); } } var d = new double[order]; var e = new double[order]; if (IsSymmetric) { matrix.CopyTo(MatrixEv); d = MatrixEv.Row(order - 1).ToArray(); SymmetricTridiagonalize(d, e, order); SymmetricDiagonalize(d, e, order); } else { var matrixH = matrix.ToArray(); NonsymmetricReduceToHessenberg(matrixH, order); NonsymmetricReduceHessenberToRealSchur(matrixH, d, e, order); } for (var i = 0; i < order; i++) { MatrixD.At(i, i, d[i]); if (e[i] > 0) { MatrixD.At(i, i + 1, e[i]); } else if (e[i] < 0) { MatrixD.At(i, i - 1, e[i]); } } for (var i = 0; i < order; i++) { VectorEv[i] = new Complex(d[i], e[i]); } }
/// <summary> /// Creates a matrix from a 2D array. /// </summary> /// <param name="data">The 2D array to create this matrix from.</param> /// <returns>A matrix with the given values.</returns> protected abstract Matrix<Complex> CreateMatrix(Complex[,] data);
/// <summary> /// Calculates absolute value of <paramref name="z1"/> multiplied on signum function of <paramref name="z2"/> /// </summary> /// <param name="z1">Complex value z1</param> /// <param name="z2">Complex value z2</param> /// <returns>Result multiplication of signum function and absolute value</returns> private static Complex Csign(Complex z1, Complex z2) { return(z1.Magnitude * (z2 / z2.Magnitude)); }
/// <summary> /// Nonsymmetric reduction to Hessenberg form. /// </summary> /// <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> private void NonsymmetricReduceToHessenberg(Complex[,] matrixH, int order) { var ort = new Complex[order]; for (var m = 1; m < order - 1; m++) { // Scale column. var scale = 0.0; for (var i = m; i < order; i++) { scale += Math.Abs(matrixH[i, m - 1].Real) + Math.Abs(matrixH[i, m - 1].Imaginary); } if (scale != 0.0) { // Compute Householder transformation. var h = 0.0; for (var i = order - 1; i >= m; i--) { ort[i] = matrixH[i, m - 1] / scale; h += ort[i].MagnitudeSquared(); } var g = Math.Sqrt(h); if (ort[m].Magnitude != 0) { h = h + (ort[m].Magnitude * g); g /= ort[m].Magnitude; ort[m] = (1.0 + g) * ort[m]; } else { ort[m] = g; matrixH[m, m - 1] = scale; } // Apply Householder similarity transformation // H = (I-u*u'/h)*H*(I-u*u')/h) for (var j = m; j < order; j++) { var f = Complex.Zero; for (var i = order - 1; i >= m; i--) { f += ort[i].Conjugate() * matrixH[i, j]; } f = f / h; for (var i = m; i < order; i++) { matrixH[i, j] -= f * ort[i]; } } for (var i = 0; i < order; i++) { var f = Complex.Zero; for (var j = order - 1; j >= m; j--) { f += ort[j] * matrixH[i, j]; } f = f / h; for (var j = m; j < order; j++) { matrixH[i, j] -= f * ort[j].Conjugate(); } } ort[m] = scale * ort[m]; matrixH[m, m - 1] *= -g; } } // Accumulate transformations (Algol's ortran). for (var i = 0; i < order; i++) { for (var j = 0; j < order; j++) { MatrixEv.At(i, j, i == j ? Complex.One : Complex.Zero); } } for (var m = order - 2; m >= 1; m--) { if (matrixH[m, m - 1] != Complex.Zero && ort[m] != Complex.Zero) { var norm = (matrixH[m, m - 1].Real * ort[m].Real) + (matrixH[m, m - 1].Imaginary * ort[m].Imaginary); for (var i = m + 1; i < order; i++) { ort[i] = matrixH[i, m - 1]; } for (var j = m; j < order; j++) { var g = Complex.Zero; for (var i = m; i < order; i++) { g += ort[i].Conjugate() * MatrixEv.At(i, j); } // Double division avoids possible underflow g /= norm; for (var i = m; i < order; i++) { MatrixEv.At(i, j, MatrixEv.At(i, j) + g * ort[i]); } } } } // Create real subdiagonal elements. for (var i = 1; i < order; i++) { if (matrixH[i, i - 1].Imaginary != 0.0) { var y = matrixH[i, i - 1] / matrixH[i, i - 1].Magnitude; matrixH[i, i - 1] = matrixH[i, i - 1].Magnitude; for (var j = i; j < order; j++) { matrixH[i, j] *= y.Conjugate(); } for (var j = 0; j <= Math.Min(i + 1, order - 1); j++) { matrixH[j, i] *= y; } for (var j = 0; j < order; j++) { MatrixEv.At(j, i, MatrixEv.At(j, i) * y); } } } }
public void CanDetermineIfImaginaryUnit() { var complex = new Complex(0, 1); Assert.IsTrue(complex.IsImaginaryOne(), "Imaginary unit"); }
public void FourierDefaultTransformIsReversible() { var samples = SignalGenerator.Random((u, v) => new Complex(u, v), GetUniform(1), 0x7FFF); var work = new Complex[samples.Length]; samples.CopyTo(work, 0); Transform.FourierForward(work); Assert.IsFalse(work.ListAlmostEqual(samples, 6)); Transform.FourierInverse(work); AssertHelpers.ListAlmostEqual(samples, work, 10); Transform.FourierInverse(work, FourierOptions.Default); Assert.IsFalse(work.ListAlmostEqual(samples, 6)); Transform.FourierForward(work, FourierOptions.Default); AssertHelpers.ListAlmostEqual(samples, work, 10); }
public void CanDetermineIfInfinity() { var complex = new Complex(double.PositiveInfinity, 1); Assert.IsTrue(complex.IsInfinity(), "Real part is infinity."); complex = new Complex(1, double.NegativeInfinity); Assert.IsTrue(complex.IsInfinity(), "Imaginary part is infinity."); complex = new Complex(double.NegativeInfinity, double.PositiveInfinity); Assert.IsTrue(complex.IsInfinity(), "Both parts are infinity."); }
/// <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<Complex> input, Vector<Complex> result) { if (input == null) { throw new ArgumentNullException("input"); } if (result == null) { throw new ArgumentNullException("result"); } // Ax=b where A is an m x m matrix // Check that b is a column vector with m entries if (VectorEv.Count != input.Count) { throw new ArgumentException(Resources.ArgumentVectorsSameLength); } // Check that x is a column vector with n entries if (VectorEv.Count != result.Count) { throw Matrix.DimensionsDontMatch<ArgumentException>(VectorEv, result); } if (IsSymmetric) { // Symmetric case -> x = V * inv(λ) * VH * b; var order = VectorEv.Count; var tmp = new Complex[order]; Complex value; for (var j = 0; j < order; j++) { value = 0; if (j < order) { for (var i = 0; i < order; i++) { value += MatrixEv.At(i, j).Conjugate() * input[i]; } value /= VectorEv[j].Real; } tmp[j] = value; } for (var j = 0; j < order; j++) { value = 0; for (int i = 0; i < order; i++) { value += MatrixEv.At(j, i) * tmp[i]; } result[j] = value; } } else { throw new ArgumentException(Resources.ArgumentMatrixSymmetric); } }
public void CanDetermineIfNaN() { var complex = new Complex(double.NaN, 1); Assert.IsTrue(complex.IsNaN(), "Real part is NaN."); complex = new Complex(1, double.NaN); Assert.IsTrue(complex.IsNaN(), "Imaginary part is NaN."); complex = new Complex(double.NaN, double.NaN); Assert.IsTrue(complex.IsNaN(), "Both parts are NaN."); }
public void CanDetermineIfOneValueComplexNumber() { var complex = new Complex(1, 0); Assert.IsTrue(complex.IsOne(), "Complex number with a value of one."); }
public void CanDetermineIfRealNonNegativeNumber() { var complex = new Complex(1, 0); Assert.IsTrue(complex.IsReal(), "Is a real non-negative number."); }
public void CanComputeSquare() { var complex = new Complex(1.19209289550780998537e-7, 1.19209289550780998537e-7); AssertHelpers.AlmostEqualRelative(new Complex(0, 2.8421709430403888e-14), complex.Square(), 15); complex = new Complex(0.0, 1.19209289550780998537e-7); AssertHelpers.AlmostEqualRelative(new Complex(-1.4210854715201944e-14, 0.0), complex.Square(), 15); complex = new Complex(0.0, -1.19209289550780998537e-7); AssertHelpers.AlmostEqualRelative(new Complex(-1.4210854715201944e-14, 0.0), complex.Square(), 15); complex = new Complex(0.0, 0.5); AssertHelpers.AlmostEqualRelative(new Complex(-0.25, 0.0), complex.Square(), 15); complex = new Complex(0.0, -0.5); AssertHelpers.AlmostEqualRelative(new Complex(-0.25, 0.0), complex.Square(), 15); complex = new Complex(0.0, -8.388608e6); AssertHelpers.AlmostEqualRelative(new Complex(-70368744177664.0, 0.0), complex.Square(), 15); }
/// <summary> /// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the /// solution vector and x is the unknown vector. /// </summary> /// <param name="matrix">The coefficient matrix, <c>A</c>.</param> /// <param name="input">The solution vector, <c>b</c></param> /// <param name="result">The result vector, <c>x</c></param> public void Solve(Matrix matrix, Vector input, Vector result) { // If we were stopped before, we are no longer // We're doing this at the start of the method to ensure // that we can use these fields immediately. _hasBeenStopped = false; // Error checks if (matrix == null) { throw new ArgumentNullException("matrix"); } if (matrix.RowCount != matrix.ColumnCount) { throw new ArgumentException(Resources.ArgumentMatrixSquare, "matrix"); } if (input == null) { throw new ArgumentNullException("input"); } if (result == null) { throw new ArgumentNullException("result"); } if (input.Count != matrix.RowCount || result.Count != input.Count) { throw Matrix.DimensionsDontMatch <ArgumentException>(matrix, input, result); } // Initialize the solver fields // Set the convergence monitor if (_iterator == null) { _iterator = Iterator.CreateDefault(); } if (_preconditioner == null) { _preconditioner = new UnitPreconditioner(); } _preconditioner.Initialize(matrix); // Choose an initial guess x_0 // Take x_0 = 0 Vector xtemp = new DenseVector(input.Count); // Choose k vectors q_1, q_2, ..., q_k // Build a new set if: // a) the stored set doesn't exist (i.e. == null) // b) Is of an incorrect length (i.e. too long) // c) The vectors are of an incorrect length (i.e. too long or too short) var useOld = false; if (_startingVectors != null) { // We don't accept collections with zero starting vectors so ... if (_startingVectors.Count <= NumberOfStartingVectorsToCreate(_numberOfStartingVectors, input.Count)) { // Only check the first vector for sizing. If that matches we assume the // other vectors match too. If they don't the process will crash if (_startingVectors[0].Count == input.Count) { useOld = true; } } } _startingVectors = useOld ? _startingVectors : CreateStartingVectors(_numberOfStartingVectors, input.Count); // Store the number of starting vectors. Not really necessary but easier to type :) var k = _startingVectors.Count; // r_0 = b - Ax_0 // This is basically a SAXPY so it could be made a lot faster Vector residuals = new DenseVector(matrix.RowCount); CalculateTrueResidual(matrix, residuals, xtemp, input); // Define the temporary values var c = new Complex[k]; // Define the temporary vectors Vector gtemp = new DenseVector(residuals.Count); Vector u = new DenseVector(residuals.Count); Vector utemp = new DenseVector(residuals.Count); Vector temp = new DenseVector(residuals.Count); Vector temp1 = new DenseVector(residuals.Count); Vector temp2 = new DenseVector(residuals.Count); Vector zd = new DenseVector(residuals.Count); Vector zg = new DenseVector(residuals.Count); Vector zw = new DenseVector(residuals.Count); var d = CreateVectorArray(_startingVectors.Count, residuals.Count); // g_0 = r_0 var g = CreateVectorArray(_startingVectors.Count, residuals.Count); residuals.CopyTo(g[k - 1]); var w = CreateVectorArray(_startingVectors.Count, residuals.Count); // FOR (j = 0, 1, 2 ....) var iterationNumber = 0; while (ShouldContinue(iterationNumber, xtemp, input, residuals)) { // SOLVE M g~_((j-1)k+k) = g_((j-1)k+k) _preconditioner.Approximate(g[k - 1], gtemp); // w_((j-1)k+k) = A g~_((j-1)k+k) matrix.Multiply(gtemp, w[k - 1]); // c_((j-1)k+k) = q^T_1 w_((j-1)k+k) c[k - 1] = _startingVectors[0].ConjugateDotProduct(w[k - 1]); if (c[k - 1].Real.AlmostEqual(0, 1) && c[k - 1].Imaginary.AlmostEqual(0, 1)) { throw new Exception("Iterative solver experience a numerical break down"); } // alpha_(jk+1) = q^T_1 r_((j-1)k+k) / c_((j-1)k+k) var alpha = _startingVectors[0].ConjugateDotProduct(residuals) / c[k - 1]; // u_(jk+1) = r_((j-1)k+k) - alpha_(jk+1) w_((j-1)k+k) w[k - 1].Multiply(-alpha, temp); residuals.Add(temp, u); // SOLVE M u~_(jk+1) = u_(jk+1) _preconditioner.Approximate(u, temp1); temp1.CopyTo(utemp); // rho_(j+1) = -u^t_(jk+1) A u~_(jk+1) / ||A u~_(jk+1)||^2 matrix.Multiply(temp1, temp); var rho = temp.ConjugateDotProduct(temp); // If rho is zero then temp is a zero vector and we're probably // about to have zero residuals (i.e. an exact solution). // So set rho to 1.0 because in the next step it will turn to zero. if (rho.Real.AlmostEqual(0, 1) && rho.Imaginary.AlmostEqual(0, 1)) { rho = 1.0; } rho = -u.ConjugateDotProduct(temp) / rho; // r_(jk+1) = rho_(j+1) A u~_(jk+1) + u_(jk+1) u.CopyTo(residuals); // Reuse temp temp.Multiply(rho, temp); residuals.Add(temp, temp2); temp2.CopyTo(residuals); // x_(jk+1) = x_((j-1)k_k) - rho_(j+1) u~_(jk+1) + alpha_(jk+1) g~_((j-1)k+k) utemp.Multiply(-rho, temp); xtemp.Add(temp, temp2); temp2.CopyTo(xtemp); gtemp.Multiply(alpha, gtemp); xtemp.Add(gtemp, temp2); temp2.CopyTo(xtemp); // Check convergence and stop if we are converged. if (!ShouldContinue(iterationNumber, xtemp, input, residuals)) { // Calculate the true residual CalculateTrueResidual(matrix, residuals, xtemp, input); // Now recheck the convergence if (!ShouldContinue(iterationNumber, xtemp, input, residuals)) { // We're all good now. // Exit from the while loop. break; } } // FOR (i = 1,2, ...., k) for (var i = 0; i < k; i++) { // z_d = u_(jk+1) u.CopyTo(zd); // z_g = r_(jk+i) residuals.CopyTo(zg); // z_w = 0 zw.Clear(); // FOR (s = i, ...., k-1) AND j >= 1 Complex beta; if (iterationNumber >= 1) { for (var s = i; s < k - 1; s++) { // beta^(jk+i)_((j-1)k+s) = -q^t_(s+1) z_d / c_((j-1)k+s) beta = -_startingVectors[s + 1].ConjugateDotProduct(zd) / c[s]; // z_d = z_d + beta^(jk+i)_((j-1)k+s) d_((j-1)k+s) d[s].Multiply(beta, temp); zd.Add(temp, temp2); temp2.CopyTo(zd); // z_g = z_g + beta^(jk+i)_((j-1)k+s) g_((j-1)k+s) g[s].Multiply(beta, temp); zg.Add(temp, temp2); temp2.CopyTo(zg); // z_w = z_w + beta^(jk+i)_((j-1)k+s) w_((j-1)k+s) w[s].Multiply(beta, temp); zw.Add(temp, temp2); temp2.CopyTo(zw); } } beta = rho * c[k - 1]; if (beta.Real.AlmostEqual(0, 1) && beta.Imaginary.AlmostEqual(0, 1)) { throw new Exception("Iterative solver experience a numerical break down"); } // beta^(jk+i)_((j-1)k+k) = -(q^T_1 (r_(jk+1) + rho_(j+1) z_w)) / (rho_(j+1) c_((j-1)k+k)) zw.Multiply(rho, temp2); residuals.Add(temp2, temp); beta = -_startingVectors[0].ConjugateDotProduct(temp) / beta; // z_g = z_g + beta^(jk+i)_((j-1)k+k) g_((j-1)k+k) g[k - 1].Multiply(beta, temp); zg.Add(temp, temp2); temp2.CopyTo(zg); // z_w = rho_(j+1) (z_w + beta^(jk+i)_((j-1)k+k) w_((j-1)k+k)) w[k - 1].Multiply(beta, temp); zw.Add(temp, temp2); temp2.CopyTo(zw); zw.Multiply(rho, zw); // z_d = r_(jk+i) + z_w residuals.Add(zw, zd); // FOR (s = 1, ... i - 1) for (var s = 0; s < i - 1; s++) { // beta^(jk+i)_(jk+s) = -q^T_s+1 z_d / c_(jk+s) beta = -_startingVectors[s + 1].ConjugateDotProduct(zd) / c[s]; // z_d = z_d + beta^(jk+i)_(jk+s) * d_(jk+s) d[s].Multiply(beta, temp); zd.Add(temp, temp2); temp2.CopyTo(zd); // z_g = z_g + beta^(jk+i)_(jk+s) * g_(jk+s) g[s].Multiply(beta, temp); zg.Add(temp, temp2); temp2.CopyTo(zg); } // d_(jk+i) = z_d - u_(jk+i) zd.Subtract(u, d[i]); // g_(jk+i) = z_g + z_w zg.Add(zw, g[i]); // IF (i < k - 1) if (i < k - 1) { // c_(jk+1) = q^T_i+1 d_(jk+i) c[i] = _startingVectors[i + 1].ConjugateDotProduct(d[i]); if (c[i].Real.AlmostEqual(0, 1) && c[i].Imaginary.AlmostEqual(0, 1)) { throw new Exception("Iterative solver experience a numerical break down"); } // alpha_(jk+i+1) = q^T_(i+1) u_(jk+i) / c_(jk+i) alpha = _startingVectors[i + 1].ConjugateDotProduct(u) / c[i]; // u_(jk+i+1) = u_(jk+i) - alpha_(jk+i+1) d_(jk+i) d[i].Multiply(-alpha, temp); u.Add(temp, temp2); temp2.CopyTo(u); // SOLVE M g~_(jk+i) = g_(jk+i) _preconditioner.Approximate(g[i], gtemp); // x_(jk+i+1) = x_(jk+i) + rho_(j+1) alpha_(jk+i+1) g~_(jk+i) gtemp.Multiply(rho * alpha, temp); xtemp.Add(temp, temp2); temp2.CopyTo(xtemp); // w_(jk+i) = A g~_(jk+i) matrix.Multiply(gtemp, w[i]); // r_(jk+i+1) = r_(jk+i) - rho_(j+1) alpha_(jk+i+1) w_(jk+i) w[i].Multiply(-rho * alpha, temp); residuals.Add(temp, temp2); temp2.CopyTo(residuals); // We can check the residuals here if they're close if (!ShouldContinue(iterationNumber, xtemp, input, residuals)) { // Recalculate the residuals and go round again. This is done to ensure that // we have the proper residuals. CalculateTrueResidual(matrix, residuals, xtemp, input); } } } // END ITERATION OVER i iterationNumber++; } // copy the temporary result to the real result vector xtemp.CopyTo(result); }
/// <summary> /// Solves the matrix equation Ax = b, where A is the coefficient matrix, b is the /// solution vector and x is the unknown vector. /// </summary> /// <param name="matrix">The coefficient matrix, <c>A</c>.</param> /// <param name="input">The solution vector, <c>b</c></param> /// <param name="result">The result vector, <c>x</c></param> public void Solve(Matrix matrix, Vector input, Vector result) { // If we were stopped before, we are no longer // We're doing this at the start of the method to ensure // that we can use these fields immediately. _hasBeenStopped = false; // Error checks if (matrix == null) { throw new ArgumentNullException("matrix"); } if (matrix.RowCount != matrix.ColumnCount) { throw new ArgumentException(Resources.ArgumentMatrixSquare, "matrix"); } if (input == null) { throw new ArgumentNullException("input"); } if (result == null) { throw new ArgumentNullException("result"); } if (input.Count != matrix.RowCount || result.Count != input.Count) { throw Matrix.DimensionsDontMatch <ArgumentException>(matrix, input, result); } // Initialize the solver fields // Set the convergence monitor if (_iterator == null) { _iterator = Iterator.CreateDefault(); } if (_preconditioner == null) { _preconditioner = new UnitPreconditioner(); } _preconditioner.Initialize(matrix); // x_0 is initial guess // Take x_0 = 0 Vector xtemp = new DenseVector(input.Count); // r_0 = b - Ax_0 // This is basically a SAXPY so it could be made a lot faster Vector residuals = new DenseVector(matrix.RowCount); CalculateTrueResidual(matrix, residuals, xtemp, input); // Define the temporary scalars Complex beta = 0; // Define the temporary vectors // rDash_0 = r_0 Vector rdash = DenseVector.OfVector(residuals); // t_-1 = 0 Vector t = new DenseVector(residuals.Count); Vector t0 = new DenseVector(residuals.Count); // w_-1 = 0 Vector w = new DenseVector(residuals.Count); // Define the remaining temporary vectors Vector c = new DenseVector(residuals.Count); Vector p = new DenseVector(residuals.Count); Vector s = new DenseVector(residuals.Count); Vector u = new DenseVector(residuals.Count); Vector y = new DenseVector(residuals.Count); Vector z = new DenseVector(residuals.Count); Vector temp = new DenseVector(residuals.Count); Vector temp2 = new DenseVector(residuals.Count); Vector temp3 = new DenseVector(residuals.Count); // for (k = 0, 1, .... ) var iterationNumber = 0; while (ShouldContinue(iterationNumber, xtemp, input, residuals)) { // p_k = r_k + beta_(k-1) * (p_(k-1) - u_(k-1)) p.Subtract(u, temp); temp.Multiply(beta, temp2); residuals.Add(temp2, p); // Solve M b_k = p_k _preconditioner.Approximate(p, temp); // s_k = A b_k matrix.Multiply(temp, s); // alpha_k = (r*_0 * r_k) / (r*_0 * s_k) var alpha = rdash.ConjugateDotProduct(residuals) / rdash.ConjugateDotProduct(s); // y_k = t_(k-1) - r_k - alpha_k * w_(k-1) + alpha_k s_k s.Subtract(w, temp); t.Subtract(residuals, y); temp.Multiply(alpha, temp2); y.Add(temp2, temp3); temp3.CopyTo(y); // Store the old value of t in t0 t.CopyTo(t0); // t_k = r_k - alpha_k s_k s.Multiply(-alpha, temp2); residuals.Add(temp2, t); // Solve M d_k = t_k _preconditioner.Approximate(t, temp); // c_k = A d_k matrix.Multiply(temp, c); var cdot = c.ConjugateDotProduct(c); // cDot can only be zero if c is a zero vector // We'll set cDot to 1 if it is zero to prevent NaN's // Note that the calculation should continue fine because // c.DotProduct(t) will be zero and so will c.DotProduct(y) if (cdot.Real.AlmostEqual(0, 1) && cdot.Imaginary.AlmostEqual(0, 1)) { cdot = 1.0; } // Even if we don't want to do any BiCGStab steps we'll still have // to do at least one at the start to initialize the // system, but we'll only have to take special measures // if we don't do any so ... var ctdot = c.ConjugateDotProduct(t); Complex eta; Complex sigma; if (((_numberOfBiCgStabSteps == 0) && (iterationNumber == 0)) || ShouldRunBiCgStabSteps(iterationNumber)) { // sigma_k = (c_k * t_k) / (c_k * c_k) sigma = ctdot / cdot; // eta_k = 0 eta = 0; } else { var ydot = y.ConjugateDotProduct(y); // yDot can only be zero if y is a zero vector // We'll set yDot to 1 if it is zero to prevent NaN's // Note that the calculation should continue fine because // y.DotProduct(t) will be zero and so will c.DotProduct(y) if (ydot.Real.AlmostEqual(0, 1) && ydot.Imaginary.AlmostEqual(0, 1)) { ydot = 1.0; } var ytdot = y.ConjugateDotProduct(t); var cydot = c.ConjugateDotProduct(y); var denom = (cdot * ydot) - (cydot * cydot); // sigma_k = ((y_k * y_k)(c_k * t_k) - (y_k * t_k)(c_k * y_k)) / ((c_k * c_k)(y_k * y_k) - (y_k * c_k)(c_k * y_k)) sigma = ((ydot * ctdot) - (ytdot * cydot)) / denom; // eta_k = ((c_k * c_k)(y_k * t_k) - (y_k * c_k)(c_k * t_k)) / ((c_k * c_k)(y_k * y_k) - (y_k * c_k)(c_k * y_k)) eta = ((cdot * ytdot) - (cydot * ctdot)) / denom; } // u_k = sigma_k s_k + eta_k (t_(k-1) - r_k + beta_(k-1) u_(k-1)) u.Multiply(beta, temp2); t0.Add(temp2, temp); temp.Subtract(residuals, temp3); temp3.CopyTo(temp); temp.Multiply(eta, temp); s.Multiply(sigma, temp2); temp.Add(temp2, u); // z_k = sigma_k r_k +_ eta_k z_(k-1) - alpha_k u_k z.Multiply(eta, z); u.Multiply(-alpha, temp2); z.Add(temp2, temp3); temp3.CopyTo(z); residuals.Multiply(sigma, temp2); z.Add(temp2, temp3); temp3.CopyTo(z); // x_(k+1) = x_k + alpha_k p_k + z_k p.Multiply(alpha, temp2); xtemp.Add(temp2, temp3); temp3.CopyTo(xtemp); xtemp.Add(z, temp3); temp3.CopyTo(xtemp); // r_(k+1) = t_k - eta_k y_k - sigma_k c_k // Copy the old residuals to a temp vector because we'll // need those in the next step residuals.CopyTo(t0); y.Multiply(-eta, temp2); t.Add(temp2, residuals); c.Multiply(-sigma, temp2); residuals.Add(temp2, temp3); temp3.CopyTo(residuals); // beta_k = alpha_k / sigma_k * (r*_0 * r_(k+1)) / (r*_0 * r_k) // But first we check if there is a possible NaN. If so just reset beta to zero. beta = (!sigma.Real.AlmostEqual(0, 1) || !sigma.Imaginary.AlmostEqual(0, 1)) ? alpha / sigma * rdash.ConjugateDotProduct(residuals) / rdash.ConjugateDotProduct(t0) : 0; // w_k = c_k + beta_k s_k s.Multiply(beta, temp2); c.Add(temp2, w); // Get the real value _preconditioner.Approximate(xtemp, result); // Now check for convergence if (!ShouldContinue(iterationNumber, result, input, residuals)) { // Recalculate the residuals and go round again. This is done to ensure that // we have the proper residuals. CalculateTrueResidual(matrix, residuals, result, input); } // Next iteration. iterationNumber++; } }
public void CanComputeSquareRoot() { var complex = new Complex(1.19209289550780998537e-7, 1.19209289550780998537e-7); AssertHelpers.AlmostEqualRelative( new Complex(0.00037933934912842666, 0.00015712750315077684), complex.SquareRoot(), 14); complex = new Complex(0.0, 1.19209289550780998537e-7); AssertHelpers.AlmostEqualRelative( new Complex(0.00024414062499999973, 0.00024414062499999976), complex.SquareRoot(), 14); complex = new Complex(0.0, -1.19209289550780998537e-7); AssertHelpers.AlmostEqualRelative( new Complex(0.00024414062499999973, -0.00024414062499999976), complex.SquareRoot(), 14); complex = new Complex(0.0, 0.5); AssertHelpers.AlmostEqualRelative(new Complex(0.5, 0.5), complex.SquareRoot(), 14); complex = new Complex(0.0, -0.5); AssertHelpers.AlmostEqualRelative(new Complex(0.5, -0.5), complex.SquareRoot(), 14); complex = new Complex(0.0, -8.388608e6); AssertHelpers.AlmostEqualRelative(new Complex(2048.0, -2048.0), complex.SquareRoot(), 14); complex = new Complex(8.388608e6, 1.19209289550780998537e-7); AssertHelpers.AlmostEqualRelative(new Complex(2896.3093757400989, 2.0579515874459933e-11), complex.SquareRoot(), 14); complex = new Complex(0.0, 0.0); AssertHelpers.AlmostEqualRelative(Complex.Zero, complex.SquareRoot(), 14); }