public static void Test02() { int NR = 100; int NC = 5; // erzeuge Basisfunktionen var X = new DoubleMatrix(NR, NC); for (int c = 0; c < 5; ++c) { double rt = (c + 1) * 4; for (int r = 0; r < NR; ++r) { X[r, c] = Math.Exp(-r / rt); } } var y = new DoubleMatrix(NR, 1); for (int r = 0; r < NR; ++r) { double sum = 0; for (int c = 0; c < 5; ++c) { double amp = 1 - 0.4 * Math.Abs(c - 2); sum += amp * X[r, c]; } y[r, 0] = sum; } var XtX = new DoubleMatrix(5, 5); MatrixMath.MultiplyFirstTransposed(X, X, XtX); var Xty = new DoubleMatrix(5, 1); MatrixMath.MultiplyFirstTransposed(X, y, Xty); FastNonnegativeLeastSquares.Execution(XtX, Xty, null, out var x, out var w); Assert.AreEqual(0.2, x[0, 0], 1e-6); Assert.AreEqual(0.6, x[1, 0], 1e-6); Assert.AreEqual(1.0, x[2, 0], 1e-6); Assert.AreEqual(0.6, x[3, 0], 1e-6); Assert.AreEqual(0.2, x[4, 0], 1e-6); }
/// <summary>Solves system of linear equations Ax = b using Gaussian elimination with partial pivoting.</summary> /// <param name="A">Elements of matrix 'A'. This array is modified during solution!</param> /// <param name="b">Right part 'b'. This array is also modified during solution!</param> /// <param name="x">Vector to store the result, i.e. the solution to the problem a x = b.</param> public void Solve(IROMatrix <double> A, IReadOnlyList <double> b, IVector <double> x) { if (_temp_A.RowCount != A.RowCount || _temp_A.ColumnCount != A.ColumnCount) { _temp_A = new MatrixWrapperStructForLeftSpineJaggedArray <double>(A.RowCount, A.ColumnCount); } if (b.Count != _temp_b?.Length) { _temp_b = new double[b.Count]; } if (b.Count != _temp_x?.Length) { _temp_x = new double[b.Count]; } MatrixMath.Copy(A, _temp_A); VectorMath.Copy(b, _temp_b); SolveDestructive(_temp_A, _temp_b, _temp_x); VectorMath.Copy(_temp_x, x); }
/// <summary> /// For a given set of spectra, predicts the y-values and stores them in the matrix <c>predictedY</c> /// </summary> /// <param name="mcalib">The calibration model of the analysis.</param> /// <param name="preprocessOptions">The information how to preprocess the spectra.</param> /// <param name="matrixX">The matrix of spectra to predict. Each spectrum is a row in the matrix.</param> /// <param name="numberOfFactors">The number of factors used for prediction.</param> /// <param name="predictedY">On return, this matrix holds the predicted y-values. Each row in this matrix corresponds to the same row (spectrum) in matrixX.</param> /// <param name="spectralResiduals">If you set this parameter to a appropriate matrix, the spectral residuals will be stored in this matrix. Set this parameter to null if you don't need the residuals.</param> public virtual void CalculatePredictedY( IMultivariateCalibrationModel mcalib, SpectralPreprocessingOptions preprocessOptions, IMatrix matrixX, int numberOfFactors, MatrixMath.BEMatrix predictedY, IMatrix spectralResiduals) { MultivariateRegression.PreprocessSpectraForPrediction(mcalib, preprocessOptions, matrixX); MultivariateRegression regress = this.CreateNewRegressionObject(); regress.SetCalibrationModel(mcalib); regress.PredictedYAndSpectralResidualsFromPreprocessed(matrixX, numberOfFactors, predictedY, spectralResiduals); MultivariateRegression.PostprocessY(mcalib.PreprocessingModel, predictedY); }
/** Least squares solution of A*X = B * @param B A Matrix with as many rows as A and any number of columns. * @return X that minimizes the two norm of Q*R*X-B. * @exception IllegalArgumentException Matrix row dimensions must agree. * @exception RuntimeException Matrix is rank deficient. */ public void Solve(IROMatrix <double> B, IMatrix <double> result) { if (B.RowCount != m) { throw new ArgumentException("Matrix row dimensions must agree."); } if (!IsFullRank()) { throw new Exception("Matrix is rank deficient."); } // Copy right hand side int nx = B.ColumnCount; double[][] X; if (_solveMatrixWorkspace != null && _solveMatrixWorkspace.RowCount == B.RowCount && _solveMatrixWorkspace.ColumnCount == B.ColumnCount) { X = _solveMatrixWorkspace.Array; MatrixMath.Copy(B, _solveMatrixWorkspace); } else { X = JaggedArrayMath.GetMatrixCopy(B); _solveMatrixWorkspace = new JaggedArrayMatrix(X, B.RowCount, B.ColumnCount); } // Compute Y = transpose(Q)*B for (int k = 0; k < n; k++) { for (int j = 0; j < nx; j++) { double s = 0.0; for (int i = k; i < m; i++) { s += QR[i][k] * X[i][j]; } s = -s / QR[k][k]; for (int i = k; i < m; i++) { X[i][j] += s * QR[i][k]; } } } // Solve R*X = Y; for (int k = n - 1; k >= 0; k--) { for (int j = 0; j < nx; j++) { X[k][j] /= Rdiag[k]; } for (int i = 0; i < k; i++) { for (int j = 0; j < nx; j++) { X[i][j] -= X[k][j] * QR[i][k]; } } } MatrixMath.Submatrix(_solveMatrixWorkspace, result, 0, 0); }
/// <summary> /// Factorize a nonnegative matrix A into two nonnegative matrices B and C so that A is nearly equal to B*C. /// Tikhonovs the nm f3. /// </summary> /// <param name="A">Matrix to factorize.</param> /// <param name="r">The number of factors.</param> /// <param name="B0">Original B matrix. Can be null.</param> /// <param name="C0">Original C matrix. Can be null.</param> /// <param name="oldalpha">The oldalpha.</param> /// <param name="oldbeta">The oldbeta.</param> /// <param name="gammaB">The gamma b.</param> /// <param name="gammaC">The gamma c.</param> /// <param name="maxiter">The maxiter.</param> /// <param name="tol">The tol.</param> public static void TikhonovNMF3( IMatrix <double> A, int r, IMatrix <double> B0, IMatrix <double> C0, IVector <double> oldalpha, IVector <double> oldbeta, IMatrix <double> gammaB, IMatrix <double> gammaC, int maxiter, double tol) { // The converged version of the algorithm // Use complementary slackness as stopping criterion // format long; // Check the input matrix if (null == A) { throw new ArgumentNullException(nameof(A)); } if (MatrixMath.Min(A) < 0) { throw new ArgumentException("Input matrix must not contain negative elements", nameof(A)); } int m = A.RowCount; int n = A.ColumnCount; // Check input arguments //if ˜exist(’r’) if (null == B0) { B0 = DoubleMatrix.Random(m, r); } if (null == C0) { C0 = DoubleMatrix.Random(r, n); } if (null == oldalpha) { oldalpha = new DoubleVector(n); } if (null == oldbeta) { oldbeta = new DoubleVector(m); } if (null == gammaB) { gammaB = new DoubleMatrix(m, 1); gammaB.SetMatrixElements(0.1); // small values lead to better convergence property } if (null == gammaC) { gammaC = new DoubleMatrix(n, 1); gammaC.SetMatrixElements(0.1); // small values lead to better convergence property } if (0 == maxiter) { maxiter = 1000; } if (double.IsNaN(tol) || tol <= 0) { tol = 1.0e-9; } var B = B0; B0 = null; var C = C0; C0 = null; var newalpha = oldalpha; var newbeta = oldbeta; var AtA = new DoubleMatrix(n, n); MatrixMath.MultiplyFirstTransposed(A, A, AtA); double trAtA = MatrixMath.Trace(AtA); var olderror = new DoubleVector(maxiter + 1); var BtA = new DoubleMatrix(r, n); MatrixMath.MultiplyFirstTransposed(B, A, BtA); var CtBtA = new DoubleMatrix(n, n); MatrixMath.MultiplyFirstTransposed(C, BtA, CtBtA); var BtB = new DoubleMatrix(r, r); MatrixMath.MultiplyFirstTransposed(B, B, BtB); var BtBC = new DoubleMatrix(r, n); MatrixMath.Multiply(BtB, C, BtBC); var CtBtBC = new DoubleMatrix(n, n); MatrixMath.MultiplyFirstTransposed(C, BtBC, CtBtBC); var BtDgNewbeta = new DoubleMatrix(r, m); MatrixMath.MultiplyFirstTransposed(B, DoubleMatrix.Diag(newbeta), BtDgNewbeta); var BtDgNewbetaB = new DoubleMatrix(r, r); // really rxr ? MatrixMath.Multiply(BtDgNewbeta, B, BtDgNewbetaB); var CDgNewalpha = new DoubleMatrix(r, n); MatrixMath.Multiply(C, DoubleMatrix.Diag(newalpha), CDgNewalpha); var CtCDgNewalpha = new DoubleMatrix(n, n); MatrixMath.MultiplyFirstTransposed(C, CDgNewalpha, CtCDgNewalpha); olderror[0] = 0.5 * trAtA - MatrixMath.Trace(CtBtA) + 0.5 * MatrixMath.Trace(CtBtBC) + 0.5 * MatrixMath.Trace(BtDgNewbetaB) + 0.5 * MatrixMath.Trace(CtCDgNewalpha); double sigma = 1.0e-9; double delta = sigma; for (int iteration = 1; iteration <= maxiter; ++iteration) { var CCt = new DoubleMatrix(r, r); MatrixMath.MultiplySecondTransposed(C, C, CCt); var gradB = new DoubleMatrix(m, r); var tempMR = new DoubleMatrix(m, r); //gradB = B*CCt - A*C’ +diag(newbeta)*B; MatrixMath.Multiply(B, CCt, gradB); MatrixMath.MultiplySecondTransposed(A, C, tempMR); MatrixMath.Add(gradB, tempMR, gradB); MatrixMath.Multiply(DoubleMatrix.Diag(newbeta), B, tempMR); MatrixMath.Add(gradB, tempMR, gradB); // Bm = max(B, (gradB < 0) * sigma); var sigMR = new DoubleMatrix(m, r); sigMR.SetMatrixElements((i, j) => gradB[i, j] < 0 ? sigma : 0); var Bm = new DoubleMatrix(m, r); Bm.SetMatrixElements((i, j) => Math.Max(B[i, j], sigMR[i, j])); } }
/// <summary> /// Execution of the fast nonnegative least squares algorithm. The algorithm finds a vector x with all elements xi>=0 which minimizes |X*x-y|. /// </summary> /// <param name="XtX">X transposed multiplied by X, thus a square matrix.</param> /// <param name="Xty">X transposed multiplied by Y, thus a matrix with one column and same number of rows as X.</param> /// <param name="isRestrictedToPositiveValues">Function that takes the parameter index as argument and returns true if the parameter at this index is restricted to positive values; otherwise the return value must be false.</param> /// <param name="tolerance">Used to decide if a solution element is less than or equal to zero. If this is null, a default tolerance of tolerance = MAX(SIZE(XtX)) * NORM(XtX,1) * EPS is used.</param> /// <param name="x">Output: solution vector (matrix with one column and number of rows according to dimension of X.</param> /// <param name="w">Output: Lagrange vector. Elements which take place in the fit are set to 0. Elements fixed to zero contain a negative number.</param> /// <remarks> /// <para> /// Literature: Rasmus Bro and Sijmen De Jong, 'A fast non-negativity-constrained least squares algorithm', Journal of Chemometrics, Vol. 11, 393-401 (1997) /// </para> /// <para> /// Algorithm modified by Dirk Lellinger 2015 to allow a mixture of restricted and unrestricted parameters. /// </para> /// </remarks> public static void Execution(IROMatrix <double> XtX, IROMatrix <double> Xty, Func <int, bool> isRestrictedToPositiveValues, double?tolerance, out IMatrix <double> x, out IMatrix <double> w) { if (null == XtX) { throw new ArgumentNullException(nameof(XtX)); } if (null == Xty) { throw new ArgumentNullException(nameof(Xty)); } if (null == isRestrictedToPositiveValues) { throw new ArgumentNullException(nameof(isRestrictedToPositiveValues)); } if (XtX.RowCount != XtX.ColumnCount) { throw new ArgumentException("Matrix should be a square matrix", nameof(XtX)); } if (Xty.ColumnCount != 1) { throw new ArgumentException(nameof(Xty) + " should be a column vector (number of columns should be equal to 1)", nameof(Xty)); } if (Xty.RowCount != XtX.ColumnCount) { throw new ArgumentException("Number of rows in " + nameof(Xty) + " should match number of columns in " + nameof(XtX), nameof(Xty)); } var matrixGenerator = new Func <int, int, DoubleMatrix>((rows, cols) => new DoubleMatrix(rows, cols)); // if nargin < 3 // tol = 10 * eps * norm(XtX, 1) * length(XtX); // end double tol = tolerance.HasValue ? tolerance.Value : 10 * DoubleConstants.DBL_EPSILON * MatrixMath.Norm(XtX, MatrixNorm.M1Norm) * Math.Max(XtX.RowCount, XtX.ColumnCount); // [m, n] = size(XtX); int n = XtX.ColumnCount; // P = zeros(1, n); // Z = 1:n; var P = new bool[n]; // POSITIVE SET: all indices which are currently not fixed are marked with TRUE (Negative set is simply this, but inverted) bool initializationOfSolutionRequired = false; for (int i = 0; i < n; ++i) { bool isNotRestricted = !isRestrictedToPositiveValues(i); P[i] = isNotRestricted; initializationOfSolutionRequired |= isNotRestricted; } // x = P'; x = matrixGenerator(n, 1); // w = Xty-XtX*x; w = matrixGenerator(n, 1); MatrixMath.Copy(Xty, w); var helper_n_1 = matrixGenerator(n, 1); MatrixMath.Multiply(XtX, x, helper_n_1); MatrixMath.Subtract(w, helper_n_1, w); // set up iteration criterion int iter = 0; int itmax = 30 * n; // outer loop to put variables into set to hold positive coefficients // while any(Z) & any(w(ZZ) > tol) while (initializationOfSolutionRequired || (P.Any(ele => false == ele) && w.Any((r, c, ele) => false == P[r] && ele > tol))) { if (initializationOfSolutionRequired) { initializationOfSolutionRequired = false; } else { // [wt, t] = max(w(ZZ)); // t = ZZ(t); int t = -1; // INDEX double wt = double.NegativeInfinity; for (int i = 0; i < n; ++i) { if (!P[i]) { if (w[i, 0] > wt) { wt = w[i, 0]; t = i; } } } // P(1, t) = t; // Z(t) = 0; P[t] = true; } // z(PP')=(Xty(PP)'/XtX(PP,PP)'); var subXty = Xty.SubMatrix(P, 0, matrixGenerator); // Xty(PP)' var subXtX = XtX.SubMatrix(P, P, matrixGenerator); var solver = new DoubleLUDecomp(subXtX); var subSolution = solver.Solve(subXty); var z = matrixGenerator(n, 1); for (int i = 0, ii = 0; i < n; ++i) { z[i, 0] = P[i] ? subSolution[ii++, 0] : 0; } // C. Inner loop (to remove elements from the positive set which no longer belong to) while (z.Any((r, c, ele) => true == P[r] && ele <= tol && isRestrictedToPositiveValues(r)) && iter < itmax) { ++iter; // QQ = find((z <= tol) & P'); //alpha = min(x(QQ)./ (x(QQ) - z(QQ))); double alpha = double.PositiveInfinity; for (int i = 0; i < n; ++i) { if ((z[i, 0] <= tol && true == P[i] && isRestrictedToPositiveValues(i))) { alpha = Math.Min(alpha, x[i, 0] / (x[i, 0] - z[i, 0])); } } // x = x + alpha * (z - x); for (int i = 0; i < n; ++i) { x[i, 0] += alpha * (z[i, 0] - x[i, 0]); } // ij = find(abs(x) < tol & P' ~= 0); // Z(ij) = ij'; // P(ij) = zeros(1, length(ij)); for (int i = 0; i < n; ++i) { if (Math.Abs(x[i, 0]) < tol && P[i] == true && isRestrictedToPositiveValues(i)) { P[i] = false; } } //PP = find(P); //ZZ = find(Z); //nzz = size(ZZ); //z(PP) = (Xty(PP)'/XtX(PP,PP)'); subXty = Xty.SubMatrix(P, 0, matrixGenerator); subXtX = XtX.SubMatrix(P, P, matrixGenerator); solver = new DoubleLUDecomp(subXtX); subSolution = solver.Solve(subXty); for (int i = 0, ii = 0; i < n; ++i) { z[i, 0] = P[i] ? subSolution[ii++, 0] : 0; } } // end inner loop MatrixMath.Copy(z, x); MatrixMath.Copy(Xty, w); MatrixMath.Multiply(XtX, x, helper_n_1); MatrixMath.Subtract(w, helper_n_1, w); } }
/// <summary> /// Represents the content of the matrix as a string. /// </summary> /// <returns></returns> public override string ToString() { return(MatrixMath.MatrixToString(null, this)); }
/* Modified algorithm which is better for M>>N */ int gsl_linalg_SV_decomp_mod(IROMatrix A, IROMatrix X, IMatrix V, IVector S, IVector work) { int i, j; int M = A.Rows; int N = A.Columns; if (M < N) { throw new ArgumentException("svd of MxN matrix, M<N, is not implemented"); } else if (V.Rows != N) { throw new ArgumentException("square matrix V must match second dimension of matrix A"); } else if (V.Rows != V.Columns) { throw new ArgumentException("matrix V must be square"); } else if (X.Rows != N) { throw new ArgumentException("square matrix X must match second dimension of matrix A"); } else if (X.Rows != X.Columns) { throw new ArgumentException("matrix X must be square"); } else if (S.Length != N) { throw new ArgumentException("length of vector S must match second dimension of matrix A"); } else if (work.Length != N) { throw new ArgumentException("length of workspace must match second dimension of matrix A"); } if (N == 1) { IROVector column = MatrixMath.ColumnToROVector(A, 0); // gsl_vector_view = gsl_matrix_column(A, 0); double norm = VectorMath.GetNorm(column); //gsl_blas_dnrm2(&column.vector); S[0] = norm; // gsl_vector_set(S, 0, norm); V[0, 0] = 1; //gsl_matrix_set(V, 0, 0, 1.0); if (norm != 0.0) { gsl_blas_dscal(1.0 / norm, &column.vector); } return(GSL_SUCCESS); } /* Convert A into an upper triangular matrix R */ for (i = 0; i < N; i++) { IVector c = MatrixMath.ColumnToVector(A, i); // gsl_vector_view c = gsl_matrix_column(A, i); gsl_vector_view v = gsl_vector_subvector(&c.vector, i, M - i); double tau_i = gsl_linalg_householder_transform(&v.vector); /* Apply the transformation to the remaining columns */ if (i + 1 < N) { gsl_matrix_view m = gsl_matrix_submatrix(A, i, i + 1, M - i, N - (i + 1)); gsl_linalg_householder_hm(tau_i, &v.vector, &m.matrix); } gsl_vector_set(S, i, tau_i); } /* Copy the upper triangular part of A into X */ for (i = 0; i < N; i++) { for (j = 0; j < i; j++) { gsl_matrix_set(X, i, j, 0.0); } { double Aii = gsl_matrix_get(A, i, i); gsl_matrix_set(X, i, i, Aii); } for (j = i + 1; j < N; j++) { double Aij = gsl_matrix_get(A, i, j); gsl_matrix_set(X, i, j, Aij); } } /* Convert A into an orthogonal matrix L */ for (j = N; j > 0 && j--;) { /* Householder column transformation to accumulate L */ double tj = gsl_vector_get(S, j); gsl_matrix_view m = gsl_matrix_submatrix(A, j, j, M - j, N - j); gsl_linalg_householder_hm1(tj, &m.matrix); } /* unpack R into X V S */ gsl_linalg_SV_decomp(X, V, S, work); /* Multiply L by X, to obtain U = L X, stored in U */ { gsl_vector_view sum = gsl_vector_subvector(work, 0, N); for (i = 0; i < M; i++) { gsl_vector_view L_i = gsl_matrix_row(A, i); gsl_vector_set_zero(&sum.vector); for (j = 0; j < N; j++) { double Lij = gsl_vector_get(&L_i.vector, j); gsl_vector_view X_j = gsl_matrix_row(X, j); gsl_blas_daxpy(Lij, &X_j.vector, &sum.vector); } gsl_vector_memcpy(&L_i.vector, &sum.vector); } } return(GSL_SUCCESS); }