/// <summary> /// Find a pivot in a matrix. /// </summary> /// <param name="markowitz">The Markowitz pivot strategy.</param> /// <param name="matrix">The matrix</param> /// <param name="eliminationStep">The current elimination step.</param> /// <returns> /// The pivot element, or null if no pivot was found. /// </returns> /// <exception cref="ArgumentNullException"> /// markowitz /// or /// matrix /// </exception> /// <exception cref="ArgumentException">Invalid elimination step</exception> public override MatrixElement <T> FindPivot(Markowitz <T> markowitz, SparseMatrix <T> matrix, int eliminationStep) { if (markowitz == null) { throw new ArgumentNullException(nameof(markowitz)); } if (matrix == null) { throw new ArgumentNullException(nameof(matrix)); } if (eliminationStep < 1) { throw new ArgumentException("Invalid elimination step"); } // No singletons left, so don't bother if (markowitz.Singletons == 0) { return(null); } // Find the first valid singleton we can use int singletons = 0, index; for (var i = matrix.Size + 1; i >= eliminationStep; i--) { // First check the current pivot, else // search from last to first as it tends to push the higher markowitz // products downwards. index = i > matrix.Size ? eliminationStep : i; // Not a singleton, let's skip this one... if (markowitz.Product(index) != 0) { continue; } // Keep track of how many singletons we have found singletons++; /* * NOTE: In the sparse library of Spice 3f5 first the diagonal is checked, * then the column is checked (if no success go to row) and finally the * row is checked for a valid singleton pivot. * The diagonal should actually not be checked, as the checking algorithm is the same * as for checking the column (FindBiggestInColExclude will not find anything in the column * for singletons). The original author did not find this. * Also, the original algorithm has a bug in there that renders the whole code invalid... * if (ChosenPivot != NULL) { break; } will throw away the pivot even if it was found! * (ref. Spice 3f5 Libraries/Sparse/spfactor.c, line 1286) */ // Find the singleton element MatrixElement <T> chosen; if (markowitz.ColumnCount(index) == 0) { // The last element in the column is the singleton element! chosen = matrix.GetLastInColumn(index); // Check if it is a valid pivot var magnitude = markowitz.Magnitude(chosen.Value); if (magnitude > markowitz.AbsolutePivotThreshold) { return(chosen); } } // Check if we can still use a row here if (markowitz.RowCount(index) == 0) { // The last element in the row is the singleton element chosen = matrix.GetLastInRow(index); // When the matrix has an empty row, and an RHS element, it is possible // that the singleton is not a singleton if (chosen == null || chosen.Column < eliminationStep) { // The last element is not valid, singleton failed! singletons--; continue; } // First find the biggest magnitude in the column, not counting the pivot candidate var element = chosen.Above; var largest = 0.0; while (element != null && element.Row >= eliminationStep) { largest = Math.Max(largest, markowitz.Magnitude(element.Value)); element = element.Above; } element = chosen.Below; while (element != null) { largest = Math.Max(largest, markowitz.Magnitude(element.Value)); element = element.Below; } // Check if the pivot is valid var magnitude = markowitz.Magnitude(chosen.Value); if (magnitude > markowitz.AbsolutePivotThreshold && magnitude > markowitz.RelativePivotThreshold * largest) { return(chosen); } } // Don't continue if no more singletons are available if (singletons >= markowitz.Singletons) { break; } } // All singletons were unacceptable... return(null); }
/// <summary> /// Search the entire matrix for a suitable pivot /// In order to preserve sparsity, Markowitz counts are used to find the largest valid /// pivot with the smallest number of elements. /// </summary> /// <param name="markowitz">Markowitz object</param> /// <param name="matrix">Matrix</param> /// <param name="eliminationStep">Step</param> /// <returns></returns> public override MatrixElement <T> FindPivot(Markowitz <T> markowitz, SparseMatrix <T> matrix, int eliminationStep) { if (markowitz == null) { throw new ArgumentNullException(nameof(markowitz)); } if (matrix == null) { throw new ArgumentNullException(nameof(matrix)); } if (eliminationStep < 1) { throw new ArgumentException("Invalid elimination step"); } MatrixElement <T> chosen = null; long minMarkowitzProduct = long.MaxValue; double largestMagnitude = 0.0, acceptedRatio = 0.0; MatrixElement <T> largestElement = null; int ties = 0; // Start search of matrix on column by column basis for (int i = eliminationStep; i <= matrix.Size; i++) { // Find the biggest magnitude in the column for checking valid pivots later double largest = 0.0; var element = matrix.GetLastInColumn(i); while (element != null && element.Row >= eliminationStep) { largest = Math.Max(largest, markowitz.Magnitude(element.Value)); element = element.Above; } if (largest.Equals(0.0)) { continue; } // Restart search for a pivot element = matrix.GetLastInColumn(i); while (element != null && element.Row >= eliminationStep) { // Find the magnitude and Markowitz product double magnitude = markowitz.Magnitude(element.Value); int product = markowitz.RowCount(element.Row) * markowitz.ColumnCount(element.Column); // In the case no valid pivot is available, at least return the largest element if (magnitude > largestMagnitude) { largestElement = element; largestMagnitude = magnitude; } // test to see if the element is acceptable as a pivot candidate if (product <= minMarkowitzProduct && magnitude > markowitz.RelativePivotThreshold * largest && magnitude > markowitz.AbsolutePivotThreshold) { // Test to see if the element has the lowest Markowitz product yet found, // or whether it is tied with an element found earlier if (product < minMarkowitzProduct) { // Notice strict inequality // This is a new smallest Markowitz product chosen = element; minMarkowitzProduct = product; acceptedRatio = largest / magnitude; ties = 0; } else { // This case handles Markowitz ties ties++; double ratio = largest / magnitude; if (ratio < acceptedRatio) { chosen = element; acceptedRatio = ratio; } if (ties >= minMarkowitzProduct * TiesMultiplier) { return(chosen); } } } element = element.Above; } } // If a valid pivot was found, return it if (chosen != null) { return(chosen); } // Singular matrix // If we can't find it while searching the entire matrix, then we definitely have a singular matrix... if (largestElement == null || largestElement.Value.Equals(0.0)) { throw new SingularException(eliminationStep); } return(largestElement); }
/// <inheritdoc/> public override Pivot <ISparseMatrixElement <T> > FindPivot(Markowitz <T> markowitz, ISparseMatrix <T> matrix, int eliminationStep, int max) { markowitz.ThrowIfNull(nameof(markowitz)); matrix.ThrowIfNull(nameof(matrix)); if (eliminationStep < 1 || eliminationStep > max) { return(Pivot <ISparseMatrixElement <T> > .Empty); } ISparseMatrixElement <T> chosen = null; var minMarkowitzProduct = long.MaxValue; double largestMagnitude = 0.0, acceptedRatio = 0.0; ISparseMatrixElement <T> largestElement = null; var ties = 0; // Start search of matrix on column by column basis for (var i = eliminationStep; i <= max; i++) { // Find an entry point to the interesting part of the column var lowest = matrix.GetLastInColumn(i); while (lowest != null && lowest.Row > max) { lowest = lowest.Above; } if (lowest == null || lowest.Row < eliminationStep) { continue; } // Find the biggest magnitude in the column for checking valid pivots later var largest = 0.0; var element = lowest; while (element != null && element.Row >= eliminationStep) { largest = Math.Max(largest, markowitz.Magnitude(element.Value)); element = element.Above; } if (largest.Equals(0.0)) { continue; } // Restart search for a pivot element = lowest; while (element != null && element.Row >= eliminationStep) { // Find the magnitude and Markowitz product var magnitude = markowitz.Magnitude(element.Value); var product = markowitz.RowCount(element.Row) * markowitz.ColumnCount(element.Column); // In the case no valid pivot is available, at least return the largest element if (magnitude > largestMagnitude) { largestElement = element; largestMagnitude = magnitude; } // test to see if the element is acceptable as a pivot candidate if (product <= minMarkowitzProduct && magnitude > markowitz.RelativePivotThreshold * largest && magnitude > markowitz.AbsolutePivotThreshold) { // Test to see if the element has the lowest Markowitz product yet found, // or whether it is tied with an element found earlier if (product < minMarkowitzProduct) { // Notice strict inequality // This is a new smallest Markowitz product chosen = element; minMarkowitzProduct = product; acceptedRatio = largest / magnitude; ties = 0; } else { // This case handles Markowitz ties ties++; var ratio = largest / magnitude; if (ratio < acceptedRatio) { chosen = element; acceptedRatio = ratio; } if (ties >= minMarkowitzProduct * _tiesMultiplier) { return(new Pivot <ISparseMatrixElement <T> >(chosen, PivotInfo.Suboptimal)); } } } element = element.Above; } } // If a valid pivot was found, return it if (chosen != null) { return(new Pivot <ISparseMatrixElement <T> >(chosen, PivotInfo.Suboptimal)); } // Else just return the largest element if (largestElement == null || largestElement.Value.Equals(default))