/// <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); } var minMarkowitzProduct = int.MaxValue; ISparseMatrixElement <T> chosen = null; var ratioOfAccepted = 0.0; var ties = 0; /* Used for debugging alongside Spice 3f5 * for (var index = matrix.Size + 1; index > eliminationStep; index--) * { * var i = index > matrix.Size ? eliminationStep : index; */ for (var i = eliminationStep; i <= max; i++) { // Skip the diagonal if we already have a better one if (markowitz.Product(i) > minMarkowitzProduct) { continue; } // Get the diagonal var diagonal = matrix.FindDiagonalElement(i); if (diagonal == null) { continue; } // Get the magnitude var magnitude = markowitz.Magnitude(diagonal.Value); if (magnitude <= markowitz.AbsolutePivotThreshold) { continue; } // Check that the pivot is eligible var largest = 0.0; var element = diagonal.Below; while (element != null && element.Row <= max) { largest = Math.Max(largest, markowitz.Magnitude(element.Value)); element = element.Below; } element = diagonal.Above; while (element != null && element.Row >= eliminationStep) { largest = Math.Max(largest, markowitz.Magnitude(element.Value)); element = element.Above; } if (magnitude <= markowitz.RelativePivotThreshold * largest) { continue; } // Check markowitz numbers to find the optimal pivot if (markowitz.Product(i) < minMarkowitzProduct) { // Notice strict inequality, this is a new smallest product chosen = diagonal; minMarkowitzProduct = markowitz.Product(i); ratioOfAccepted = largest / magnitude; ties = 0; } else { // If we have enough elements with the same (minimum) number of ties, stop searching ties++; var ratio = largest / magnitude; if (ratio < ratioOfAccepted) { chosen = diagonal; ratioOfAccepted = ratio; } if (ties >= minMarkowitzProduct * _tiesMultiplier) { return(new Pivot <ISparseMatrixElement <T> >(chosen, PivotInfo.Suboptimal)); } } } // The chosen pivot has already been checked for validity return(chosen != null ? new Pivot <ISparseMatrixElement <T> >(chosen, PivotInfo.Suboptimal) : Pivot <ISparseMatrixElement <T> > .Empty); }
/// <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); } // No singletons left, so don't bother if (markowitz.Singletons == 0) { return(Pivot <ISparseMatrixElement <T> > .Empty); } // Find the first valid singleton we can use int singletons = 0, index; for (var i = max + 1; i >= eliminationStep; i--) { // First check the current pivot, else // search from last to first as this tends to push the higher markowitz // products downwards. index = i > max ? 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 ISparseMatrixElement <T> chosen; if (markowitz.ColumnCount(index) == 0) { // The last element in the column is the singleton element! chosen = matrix.GetLastInColumn(index); if (chosen.Row <= max && chosen.Column <= max) { // Check if it is a valid pivot var magnitude = markowitz.Magnitude(chosen.Value); if (magnitude > markowitz.AbsolutePivotThreshold) { return(new Pivot <ISparseMatrixElement <T> >(chosen, PivotInfo.Good)); } } } // 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 && element.Row <= max) { largest = Math.Max(largest, markowitz.Magnitude(element.Value)); element = element.Below; } // Check if the pivot is valid if (chosen.Row <= max && chosen.Column <= max) { var magnitude = markowitz.Magnitude(chosen.Value); if (magnitude > markowitz.AbsolutePivotThreshold && magnitude > markowitz.RelativePivotThreshold * largest) { return(new Pivot <ISparseMatrixElement <T> >(chosen, PivotInfo.Good)); } } } // Don't continue if no more singletons are available if (singletons >= markowitz.Singletons) { break; } } // All singletons were unacceptable... return(Pivot <ISparseMatrixElement <T> > .Empty); }
/// <summary> /// Find the pivot on the diagonal /// </summary> /// <param name="markowitz">Markowitz</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 (matrix == null) { throw new ArgumentNullException(nameof(matrix)); } if (markowitz == null) { throw new ArgumentNullException(nameof(markowitz)); } if (eliminationStep < 1) { throw new ArgumentException("Invalid elimination step"); } int minMarkowitzProduct = int.MaxValue; MatrixElement <T> chosen = null; double ratioOfAccepted = 0.0; int ties = 0; for (int i = eliminationStep; i <= matrix.Size; i++) { // Skip the diagonal if we already have a better one if (markowitz.Product(i) > minMarkowitzProduct) { continue; } // Get the diagonal var diagonal = matrix.GetDiagonalElement(i); if (diagonal == null) { continue; } // Get the magnitude double magnitude = markowitz.Magnitude(diagonal.Value); if (magnitude <= markowitz.AbsolutePivotThreshold) { continue; } // Check that the pivot is eligible double largest = 0.0; var element = diagonal.Below; while (element != null) { largest = Math.Max(largest, markowitz.Magnitude(element.Value)); element = element.Below; } element = diagonal.Above; while (element != null && element.Row >= eliminationStep) { largest = Math.Max(largest, markowitz.Magnitude(element.Value)); element = element.Above; } if (magnitude <= markowitz.RelativePivotThreshold * largest) { continue; } // Check markowitz numbers to find the optimal if (markowitz.Product(i) < minMarkowitzProduct) { // Notice strict inequality, this is a new smallest product chosen = diagonal; minMarkowitzProduct = markowitz.Product(i); ratioOfAccepted = largest / magnitude; ties = 0; } else { // If we have enough elements with the same (minimum) number of ties, stop searching ties++; double ratio = largest / magnitude; if (ratio < ratioOfAccepted) { chosen = diagonal; ratioOfAccepted = ratio; } if (ties >= minMarkowitzProduct * TiesMultiplier) { return(chosen); } } } // The chosen pivot has already been checked for validity return(chosen); }
/// <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"); } var minMarkowitzProduct = int.MaxValue; MatrixElement <T> chosen = null; for (var i = eliminationStep; i <= matrix.Size; i++) { // Skip diagonal elements with a Markowitz product worse than already found var product = markowitz.Product(i); if (product >= minMarkowitzProduct) { continue; } // Get the diagonal item var diagonal = matrix.GetDiagonalElement(i); if (diagonal == null) { continue; } // Get the magnitude var magnitude = markowitz.Magnitude(diagonal.Value); if (magnitude <= markowitz.AbsolutePivotThreshold) { continue; } if (product == 1) { // Find the off-diagonal elements var otherInRow = diagonal.Right ?? diagonal.Left; var otherInColumn = diagonal.Below ?? diagonal.Above; // Accept diagonal as pivot if diagonal is larger than off-diagonals and // the off-diagonals are placed symmetrically if (otherInRow != null && otherInColumn != null) { if (otherInRow.Column == otherInColumn.Row) { var largest = Math.Max( markowitz.Magnitude(otherInRow.Value), markowitz.Magnitude(otherInColumn.Value)); if (magnitude >= largest) { return(diagonal); } } } } minMarkowitzProduct = markowitz.Product(i); chosen = diagonal; } // No decision was made yet, so check again here if (chosen != null) { // Find the biggest element above and below the pivot var element = chosen.Below; var largest = 0.0; while (element != null) { largest = Math.Max(largest, markowitz.Magnitude(element.Value)); element = element.Below; } element = chosen.Above; while (element != null && element.Row >= eliminationStep) { largest = Math.Max(largest, markowitz.Magnitude(element.Value)); element = element.Above; } // If we can't have stability, then drop the pivot if (markowitz.Magnitude(chosen.Value) <= markowitz.RelativePivotThreshold * largest) { chosen = null; } } return(chosen); }
/// <summary> /// Initializes a new instance of the <see cref="SparseLUSolver{T}"/> class. /// </summary> /// <param name="magnitude">The magnitude function.</param> /// <exception cref="ArgumentNullException">Thrown if <paramref name="magnitude"/> is <c>null</c>.</exception> protected SparseLUSolver(Func <T, double> magnitude) : base(new SparseMatrix <T>(), new SparseVector <T>()) { Parameters = new Markowitz <T>(magnitude); Fillins = 0; }
/// <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))
/// <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> /// <param name="max">The maximum row/column index.</param> /// <returns> /// The pivot element, or null if no pivot was found. /// </returns> /// <exception cref="ArgumentNullException">Thrown if <paramref name="markowitz" /> or <paramref name="matrix" /> is <c>null</c>.</exception> /// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="eliminationStep" /> or <paramref name="max" /> not 1 or higher, /// or <paramref name="eliminationStep" /> is higher than <paramref name="max" />.</exception> public abstract Pivot <ISparseMatrixElement <T> > FindPivot(Markowitz <T> markowitz, ISparseMatrix <T> matrix, int eliminationStep, int max);