/// <summary> /// Performs the operation: <paramref name="result"/> = generalized_inverse(A) * <paramref name="vector"/>. /// The resulting vector overwrites <paramref name="result"/>. /// </summary> /// <param name="vector">The vector that will be multiplied. Its <see cref="IIndexable1D.Length"/> must be equal to /// <see cref="IIndexable2D.NumRows"/> of the original matrix A. /// </param> /// <param name="result"> /// Output vector that will be overwritten with the solution of the linear system. Its <see cref="IIndexable1D.Length"/> /// must be equal to <see cref="IIndexable2D.NumColumns"/> of the original matrix A. /// </param> /// <exception cref="NonMatchingDimensionsException"> /// Thrown if <paramref name="vector"/> or <paramref name="result"/> violate the described constraints. /// </exception> public void MultiplyGeneralizedInverseMatrixTimesVector(Vector vector, Vector result) { Preconditions.CheckSystemSolutionDimensions(Order, vector.Length); Preconditions.CheckMultiplicationDimensions(Order, result.Length); // A^+ * b = [ Aii^-1 * bi; 0], where i are the independent rows/columns // TODO: Is this correct? CholeskySkyline.SubstituteForward(Order, values, diagOffsets, vector.RawData, result.RawData); CholeskySkyline.SubstituteBack(Order, values, diagOffsets, result.RawData); foreach (int row in DependentColumns) { result[row] = 0.0; } }
internal static (List <int> dependentColumns, List <double[]> nullSpaceBasis) FactorizeInternal(int order, double[] values, int[] diagOffsets, double pivotTolerance) { var dependentColumns = new List <int>(); var nullSpaceBasis = new List <double[]>(); // Process column j for (int j = 0; j < order; ++j) { int offsetAjj = diagOffsets[j]; // The number of non-zero entries in column j, above the diagonal and excluding it int heightColJ = diagOffsets[j + 1] - offsetAjj - 1; //TODO: reuse the diagOffset form previous iteration. // The row index above which col j has only zeros int topColJ = j - heightColJ; // Update each A[i,j] by traversing the column j from the top until the diagonal. // The top now is the min row with a non-zero entry instead of 0. for (int i = topColJ; i < j; ++i) { int offsetAii = diagOffsets[i]; // TODO: increment/decrement the offset from previous iteration int offsetAij = offsetAjj + j - i; // TODO: increment/decrement the offset from previous iteration // The number of non-zero entries in column i, above the diagonal and excluding it int heightColI = diagOffsets[i + 1] - offsetAii - 1; //TODO: reuse the diagOffset form previous iteration. // The row index above which col j has only zeros int topColI = i - heightColI; // The row index above which either col j or col i has only zeros: max(topColJ, topColI) int topCommon = (topColI > topColJ) ? topColI : topColJ; // Dot product of the parts of columns j, i (i < j) between: [the common top non-zero row, row i) // for (int k = max(topRowOfColJ, topRowOfColI; k < i; ++k) dotColsIJ += A[k,i] * A[k,j] int numDotEntries = i - topCommon; double dotColsIJ = 0.0; for (int t = 1; t <= numDotEntries; ++t) //TODO: BLAS dot { dotColsIJ += values[offsetAii + t] * values[offsetAij + t]; } // A[i,j] = (A[i,j] - dotIJ) / A[i,i] values[offsetAij] = (values[offsetAij] - dotColsIJ) / values[offsetAii]; } // Update the diagonal term // Dot product with itself of the part of column j between: [the top non-zero row, row j). // for (int k = topRowOfColJ; k < j; ++k) dotColsJJ += A[k,j]^2 double dotColsJJ = 0.0; double valueAkj; for (int t = 1; t <= heightColJ; ++t) //TODO: BLAS dot. //TODO: the managed version would be better off using the offsetAkj as a loop variable { valueAkj = values[offsetAjj + t]; dotColsJJ += valueAkj * valueAkj; } // if A[j,j] = sqrt(A[j,j]-dotColsJJ), but if the subroot is <= 0, then the matrix is not positive definite double subroot = values[offsetAjj] - dotColsJJ; if (subroot > pivotTolerance) { values[offsetAjj] = Math.Sqrt(subroot); // positive definite } else if (subroot < -pivotTolerance) // not positive semidefinite { throw new IndefiniteMatrixException($"The leading minor of order {j} (and therefore the matrix itself)" + " is negative, and the factorization could not be completed."); } else // positive semidefinite with rank deficiency { // Set the dependent column to identity and partially calculate a new basis vector of the null space. dependentColumns.Add(j); var basisVector = new double[order]; nullSpaceBasis.Add(basisVector); // The diagonal entries of the skyline column and the null space basis vector are set to 1. basisVector[j] = 1.0; values[offsetAjj] = 1.0; // Superdiagonal entries are set to 0 after copying them to the nullspace (and negating them). for (int t = 1; t <= heightColJ; ++t) //TODO: indexing could be written more efficiently { basisVector[j - t] = -values[offsetAjj + t]; values[offsetAjj + t] = 0; } // Subdiagonal entries are also set to 0, but they are stored in subsequent columns i of the skyline format. for (int i = j + 1; i < order; ++i) { int offsetAij = diagOffsets[i] + i - j; // TODO: reuse the indexing from the previous iteration if (offsetAij < diagOffsets[i + 1]) { values[offsetAij] = 0; //TODO: originally this was <= ? Which is the correct one? } } } } // The independent columns have been factorized successfully. Apply back substitution to finish the calculation // of each vector in the null space basis. foreach (var basisVector in nullSpaceBasis) { CholeskySkyline.SubstituteBack(order, values, diagOffsets, basisVector); } return(dependentColumns, nullSpaceBasis); }