public void SolveMatrix() { DoubleMatrix b = new DoubleMatrix(3); b[0, 0] = 2; b[0, 1] = 2; b[0, 2] = 2; b[1, 0] = 13; b[1, 1] = 13; b[1, 2] = 13; b[2, 0] = 25; b[2, 1] = 25; b[2, 2] = 25; DoubleMatrix x = lu.Solve(b); Assert.AreEqual(x[0, 0], 2.965, TOLERENCE); Assert.AreEqual(x[0, 1], 2.965, TOLERENCE); Assert.AreEqual(x[0, 2], 2.965, TOLERENCE); Assert.AreEqual(x[1, 0], -0.479, TOLERENCE); Assert.AreEqual(x[1, 1], -0.479, TOLERENCE); Assert.AreEqual(x[1, 2], -0.479, TOLERENCE); Assert.AreEqual(x[2, 0], 1.227, TOLERENCE); Assert.AreEqual(x[2, 1], 1.227, TOLERENCE); Assert.AreEqual(x[2, 2], 1.227, TOLERENCE); b = new DoubleMatrix(3, 2); b[0, 0] = 2; b[0, 1] = 2; b[1, 0] = 13; b[1, 1] = 13; b[2, 0] = 25; b[2, 1] = 25; x = lu.Solve(b); Assert.AreEqual(x[0, 0], 2.965, TOLERENCE); Assert.AreEqual(x[0, 1], 2.965, TOLERENCE); Assert.AreEqual(x[1, 0], -0.479, TOLERENCE); Assert.AreEqual(x[1, 1], -0.479, TOLERENCE); Assert.AreEqual(x[2, 0], 1.227, TOLERENCE); Assert.AreEqual(x[2, 1], 1.227, TOLERENCE); b = new DoubleMatrix(3, 4); b[0, 0] = 2; b[0, 1] = 2; b[0, 2] = 2; b[0, 3] = 2; b[1, 0] = 13; b[1, 1] = 13; b[1, 2] = 13; b[1, 3] = 13; b[2, 0] = 25; b[2, 1] = 25; b[2, 2] = 25; b[2, 3] = 25; x = lu.Solve(b); Assert.AreEqual(x[0, 0], 2.965, TOLERENCE); Assert.AreEqual(x[0, 1], 2.965, TOLERENCE); Assert.AreEqual(x[0, 2], 2.965, TOLERENCE); Assert.AreEqual(x[0, 3], 2.965, TOLERENCE); Assert.AreEqual(x[1, 0], -0.479, TOLERENCE); Assert.AreEqual(x[1, 1], -0.479, TOLERENCE); Assert.AreEqual(x[1, 2], -0.479, TOLERENCE); Assert.AreEqual(x[1, 3], -0.479, TOLERENCE); Assert.AreEqual(x[2, 0], 1.227, TOLERENCE); Assert.AreEqual(x[2, 1], 1.227, TOLERENCE); Assert.AreEqual(x[2, 2], 1.227, TOLERENCE); Assert.AreEqual(x[2, 3], 1.227, TOLERENCE); }
public static void Test01e_2() { var X = new DoubleMatrix(new double[,] { { 73, 746, 743, 106 }, { 584, 531, 420, 579 }, { 255, 562, 234, 693 }, { 360, 474, 381, 484 }, { 301, 78, 68, 313 } }); var y = new DoubleMatrix(new double[,] { { 803 }, { 292 }, { 230 }, { 469 }, { 655 } }); var XtX = X.GetTranspose() * X; var Xty = X.GetTranspose() * y; var solver = new DoubleLUDecomp(XtX); var expected = solver.Solve(Xty); IMatrix x, w; FastNonnegativeLeastSquares.Execution(XtX, Xty, (i) => false, null, out x, out w); Assert.AreEqual(expected[0, 0], x[0, 0], 1e-4); Assert.AreEqual(expected[1, 0], x[1, 0], 1e-4); Assert.AreEqual(expected[2, 0], x[2, 0], 1e-4); Assert.AreEqual(expected[3, 0], x[3, 0], 1e-4); Assert.AreEqual(0, w[0, 0], 1e-8); Assert.AreEqual(0, w[1, 0], 1e-8); Assert.AreEqual(0, w[2, 0], 1e-8); Assert.AreEqual(0, w[3, 0], 1e-8); }
/// <summary> /// Calculate the matrix for the polyharmonic spline and solves the linear equation to calculate the coefficients. /// </summary> private void InternalCompute() { var N = _numberOfControlPoints; // Allocate the matrix and vector var mtx_l = new DoubleMatrix(N + 1 + _coordDim, N + 1 + _coordDim); // there is no need for this matrix if we don't need to calculate the bending energy _mtx_orig_k = new DoubleMatrix(N, N); // Fill K (p x p, upper left of L) // K is symmetrical so we really have to calculate only about half of the coefficients. double a = 0.0; for (int i = 0; i < N; ++i) { for (int j = i + 1; j < N; ++j) { double distance = DistanceBetweenControlPoints(i, j); mtx_l[i, j] = mtx_l[j, i] = _mtx_orig_k[i, j] = _mtx_orig_k[j, i] = _cachedTpsFunc(distance); a += distance * 2; // same for upper & lower triangle } } a /= ((double)N) * N; // Fill the rest of L with the values to do regularization and linear interpolation for (int i = 0; i < N; ++i) { // diagonal: regularization parameter (lambda * a^2) mtx_l[i, i] = _mtx_orig_k[i, i] = _regularizationParameter * (a * a); // P (N x (nCoordDim+1), upper right) mtx_l[i, N + 0] = 1; // for the intercept for (int d = 0; d < _coordDim; d++) mtx_l[i, N + 1 + d] = _coordinates[d][i]; // P transposed ((nCoordDim+1) x N, bottom left) mtx_l[N + 0, i] = 1; // for the intercept for (int d = 0; d < _coordDim; ++d) mtx_l[N + 1 + d, i] = _coordinates[d][i]; } // Zero ((nCoordDim+1) x (nCoordDim+1), lower right) for (int i = N; i < N + _coordDim + 1; ++i) for (int j = N; j < N + _coordDim + 1; ++j) mtx_l[i, j] = 0; // Solve the linear system var solver = new DoubleLUDecomp(mtx_l); if (solver.IsSingular) throw new ArgumentException("The provided points lead to a singular matrix"); // Fill the right hand vector V with the values to spline; the last nCoordDim+1 elements are zero _mtx_v = new DoubleVector(N + 1 + _coordDim); for (int i = 0; i < N; ++i) _mtx_v[i] = _values[i]; for (int d = 0; d <= _coordDim; ++d) // comparison '<=' is ok here because of additional intercept _mtx_v[N + d] = 0; _mtx_v = solver.Solve(_mtx_v); }
/// <summary> /// Calculate the matrix for the polyharmonic spline and solves the linear equation to calculate the coefficients. /// </summary> private void InternalCompute() { var N = _numberOfControlPoints; // Allocate the matrix and vector var mtx_l = new DoubleMatrix(N + 1 + _coordDim, N + 1 + _coordDim); // there is no need for this matrix if we don't need to calculate the bending energy _mtx_orig_k = new DoubleMatrix(N, N); // Fill K (p x p, upper left of L) // K is symmetrical so we really have to calculate only about half of the coefficients. double a = 0.0; for (int i = 0; i < N; ++i) { for (int j = i + 1; j < N; ++j) { double distance = DistanceBetweenControlPoints(i, j); mtx_l[i, j] = mtx_l[j, i] = _mtx_orig_k[i, j] = _mtx_orig_k[j, i] = _cachedTpsFunc(distance); a += distance * 2; // same for upper & lower triangle } } a /= ((double)N) * N; // Fill the rest of L with the values to do regularization and linear interpolation for (int i = 0; i < N; ++i) { // diagonal: regularization parameter (lambda * a^2) mtx_l[i, i] = _mtx_orig_k[i, i] = _regularizationParameter * (a * a); // P (N x (nCoordDim+1), upper right) mtx_l[i, N + 0] = 1; // for the intercept for (int d = 0; d < _coordDim; d++) { mtx_l[i, N + 1 + d] = _coordinates[d][i]; } // P transposed ((nCoordDim+1) x N, bottom left) mtx_l[N + 0, i] = 1; // for the intercept for (int d = 0; d < _coordDim; ++d) { mtx_l[N + 1 + d, i] = _coordinates[d][i]; } } // Zero ((nCoordDim+1) x (nCoordDim+1), lower right) for (int i = N; i < N + _coordDim + 1; ++i) { for (int j = N; j < N + _coordDim + 1; ++j) { mtx_l[i, j] = 0; } } // Solve the linear system var solver = new DoubleLUDecomp(mtx_l); if (solver.IsSingular) { throw new ArgumentException("The provided points lead to a singular matrix"); } // Fill the right hand vector V with the values to spline; the last nCoordDim+1 elements are zero _mtx_v = new DoubleVector(N + 1 + _coordDim); for (int i = 0; i < N; ++i) { _mtx_v[i] = _values[i]; } for (int d = 0; d <= _coordDim; ++d) // comparison '<=' is ok here because of additional intercept { _mtx_v[N + d] = 0; } _mtx_v = solver.Solve(_mtx_v); }
/// <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 XtX, IROMatrix Xty, Func<int, bool> isRestrictedToPositiveValues, double? tolerance, out IMatrix x, out IMatrix 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.Rows != XtX.Columns) throw new ArgumentException("Matrix should be a square matrix", nameof(XtX)); if (Xty.Columns != 1) throw new ArgumentException(nameof(Xty) + " should be a column vector (number of columns should be equal to 1)", nameof(Xty)); if (Xty.Rows != XtX.Columns) 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.Rows, XtX.Columns); // [m, n] = size(XtX); int n = XtX.Columns; // 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); } }