/// <summary> /// Notifies the strategy that a fill-in has been created /// </summary> /// <param name="matrix">The matrix.</param> /// <param name="fillin">The fill-in.</param> /// <exception cref="ArgumentNullException"> /// Thrown if <paramref name="matrix"/> or <paramref name="fillin"/> is <c>null</c>. /// </exception> public void CreateFillin(ISparseMatrix <T> matrix, ISparseMatrixElement <T> fillin) { matrix.ThrowIfNull(nameof(matrix)); fillin.ThrowIfNull(nameof(fillin)); if (_markowitzProduct == null) { return; } // Update the markowitz row count int index = fillin.Row; _markowitzRow[index]++; _markowitzProduct[index] = Math.Min(_markowitzRow[index] * _markowitzColumn[index], _maxMarkowitzCount); if (_markowitzRow[index] == 1 && _markowitzColumn[index] != 0) { Singletons--; } // Update the markowitz column count index = fillin.Column; _markowitzColumn[index]++; _markowitzProduct[index] = Math.Min(_markowitzRow[index] * _markowitzColumn[index], _maxMarkowitzCount); if (_markowitzRow[index] != 0 && _markowitzColumn[index] == 1) { Singletons--; } }
/// <summary> /// Count the number of twins in a matrix that is typically constructed using Modified Nodal Analysis (MNA). /// </summary> /// <remarks> /// A twin is a matrix element that is equal to one, and also has a one on the transposed position. MNA formulation /// often leads to many twins, allowing us to save some time by searching for them beforehand. /// </remarks> /// <param name="matrix">The matrix.</param> /// <param name="column">The column index.</param> /// <param name="twin1">The first twin element.</param> /// <param name="twin2">The second twin element.</param> /// <param name="size">The size of the submatrix to search.</param> /// <returns>The number of twins found.</returns> private static int CountTwins <M>(M matrix, int column, ref ISparseMatrixElement <T> twin1, ref ISparseMatrixElement <T> twin2, int size) where M : ISparseMatrix <T> { var twins = 0; // Begin `CountTwins'. var cTwin1 = matrix.GetFirstInColumn(column); while (cTwin1 != null && cTwin1.Row <= size) { // if (Math.Abs(pTwin1.Element.Magnitude) == 1.0) if (Magnitude(cTwin1.Value).Equals(1.0)) { var row = cTwin1.Row; var cTwin2 = matrix.GetFirstInColumn(row); while (cTwin2 != null && cTwin2.Row != column) { cTwin2 = cTwin2.Below; } if (cTwin2 != null && Magnitude(cTwin2.Value).Equals(1.0)) { // Found symmetric twins. if (++twins >= 2) { return(twins); } twin1 = cTwin1; twin2 = cTwin2; } } cTwin1 = cTwin1.Below; } return(twins); }
/// <summary> /// This method will check whether or not a pivot element is valid or not. /// It checks for the submatrix right/below of the pivot. /// </summary> /// <param name="pivot">The pivot candidate.</param> /// <param name="max">The maximum index that a pivot can have.</param> /// <returns> /// True if the pivot can be used. /// </returns> /// <exception cref="ArgumentNullException">Thrown if <paramref name="pivot"/> is <c>null</c>.</exception> public bool IsValidPivot(ISparseMatrixElement <T> pivot, int max) { pivot.ThrowIfNull(nameof(pivot)); if (pivot.Row > max || pivot.Column > max) { return(false); } // Get the magnitude of the current pivot var magnitude = Magnitude(pivot.Value); // Search for the largest element below the pivot var element = pivot.Below; var largest = 0.0; while (element != null && element.Row <= max) { largest = Math.Max(largest, Magnitude(element.Value)); element = element.Below; } // Check the validity if (largest * RelativePivotThreshold < magnitude) { return(true); } return(false); }
/// <summary> /// Moves a chosen pivot to the diagonal. /// </summary> /// <param name="pivot">The pivot element.</param> /// <param name="step">The current step of factoring.</param> /// <exception cref="ArgumentNullException">Thrown if <paramref name="pivot"/> is <c>null</c>.</exception> protected void MovePivot(ISparseMatrixElement <T> pivot, int step) { pivot.ThrowIfNull(nameof(pivot)); Parameters.MovePivot(Matrix, Vector, pivot, step); // Move the pivot in the matrix SwapRows(pivot.Row, step); SwapColumns(pivot.Column, step); // Update the pivoting strategy Parameters.Update(Matrix, pivot, Size - PivotSearchReduction); }
/// <summary> /// Update the strategy after the pivot was moved. /// </summary> /// <param name="matrix">The matrix.</param> /// <param name="pivot">The pivot element.</param> /// <param name="limit">The maximum row/column for pivots.</param> /// <exception cref="ArgumentNullException"> /// Thrown if <paramref name="matrix"/> or <paramref name="pivot"/> is <c>null</c>. /// </exception> public void Update(ISparseMatrix <T> matrix, ISparseMatrixElement <T> pivot, int limit) { matrix.ThrowIfNull(nameof(matrix)); pivot.ThrowIfNull(nameof(pivot)); // If we haven't setup, just skip if (_markowitzProduct == null) { return; } // Go through all elements below the pivot. If they exist, then we can subtract 1 from the Markowitz row vector! for (var column = pivot.Below; column != null && column.Row <= limit; column = column.Below) { var row = column.Row; // Update the Markowitz product _markowitzProduct[row] -= _markowitzColumn[row]; --_markowitzRow[row]; // If we reached 0, then the row just turned to a singleton row if (_markowitzRow[row] == 0) { Singletons++; } } // go through all elements right of the pivot. For every element, we can subtract 1 from the Markowitz column vector! for (var row = pivot.Right; row != null && row.Column <= limit; row = row.Right) { var column = row.Column; // Update the Markowitz product _markowitzProduct[column] -= _markowitzRow[column]; --_markowitzColumn[column]; // If we reached 0, then the column just turned to a singleton column // This only adds a singleton if the row wasn't detected as a singleton row first if (_markowitzColumn[column] == 0 && _markowitzRow[column] != 0) { Singletons++; } } }
private double LargestOtherElementInColumn(Markowitz <T> markowitz, ISparseMatrixElement <T> chosen, int eliminationStep, int max) { // Find the biggest element above and below the pivot var element = chosen.Below; var largest = 0.0; while (element != null && element.Row <= max) { 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; } return(largest); }
/// <inheritdoc/> protected override void Eliminate(ISparseMatrixElement <double> pivot) { // Test for zero pivot if (pivot == null || pivot.Value.Equals(0.0)) { throw new ArgumentException(Properties.Resources.Algebra_InvalidPivot.FormatString(pivot.Row)); } pivot.Value = 1.0 / pivot.Value; var upper = pivot.Right; while (upper != null) { // Calculate upper triangular element // upper = upper / pivot upper.Value *= pivot.Value; var sub = upper.Below; var lower = pivot.Below; while (lower != null) { var row = lower.Row; // Find element in row that lines up with the current lower triangular element while (sub != null && sub.Row < row) { sub = sub.Below; } // Test to see if the desired element was not found, if not, create fill-in if (sub == null || sub.Row > row) { sub = CreateFillin(new MatrixLocation(row, upper.Column)); } // element -= upper * lower sub.Value -= upper.Value * lower.Value; sub = sub.Below; lower = lower.Below; } upper = upper.Right; } }
/// <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; var numberOfTies = -1; /* Used for debugging along Spice 3f5 * for (var index = matrix.Size + 1; index > eliminationStep; index--) * { * int i = index > matrix.Size ? eliminationStep : index; */ for (var i = eliminationStep; i <= max; 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.FindDiagonalElement(i); if (diagonal == null) { continue; } // Get the magnitude var magnitude = markowitz.Magnitude(diagonal.Value); if (magnitude <= markowitz.AbsolutePivotThreshold) { continue; } // Well, can't do much better than this can we? (Assuming all the singletons are taken) // Note that a singleton can still appear depending on the allowed tolerances! 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(new Pivot <ISparseMatrixElement <T> >(diagonal, PivotInfo.Good)); } } } } if (product < minMarkowitzProduct) { // We found a diagonal that beats all the previous ones! numberOfTies = 0; _tiedElements[0] = diagonal; minMarkowitzProduct = product; } else { if (numberOfTies < _tiedElements.Length - 1) { // Keep track of this diagonal too _tiedElements[++numberOfTies] = diagonal; // This is our heuristic for speeding up pivot searching if (numberOfTies >= minMarkowitzProduct * TiesMultiplier) { break; } } } } // Not even one eligible pivot on the diagonal... if (numberOfTies < 0) { return(Pivot <ISparseMatrixElement <T> > .Empty); } // Determine which of the tied elements is the best numerical choise ISparseMatrixElement <T> chosen = null; var maxRatio = 1.0 / markowitz.RelativePivotThreshold; for (var i = 0; i <= numberOfTies; i++) { var diag = _tiedElements[i]; var mag = markowitz.Magnitude(diag.Value); var largest = LargestOtherElementInColumn(markowitz, diag, eliminationStep, max); var ratio = largest / mag; if (ratio < maxRatio) { maxRatio = ratio; chosen = diag; } } // We don't actually know if the pivot is sub-optimal, but we take the worst case scenario. return(chosen != null ? new Pivot <ISparseMatrixElement <T> >(chosen, PivotInfo.Suboptimal) : Pivot <ISparseMatrixElement <T> > .Empty); }
/// <summary> /// Eliminates the matrix right and below the pivot. /// </summary> /// <param name="pivot">The pivot element.</param> /// <returns> /// <c>true</c> if the elimination was successful; otherwise <c>false</c>. /// </returns> /// <exception cref="AlgebraException">Thrown if the pivot is <c>null</c> or has a magnitude of zero.</exception> protected abstract void Eliminate(ISparseMatrixElement <T> pivot);
/// <summary> /// Preorders the modified nodal analysis. /// </summary> /// <param name="matrix">The matrix.</param> /// <param name="size">The submatrix size to be preordered.</param> public static void PreorderModifiedNodalAnalysis(ISparseMatrix <T> matrix, int size) { /* * MNA often has patterns that we can already use for pivoting * * For example, the following pattern is quite common (lone twins): * ? ... 1 * . \ . * 1 ... 0 * We can swap columns to pivot the 1's to the diagonal. This * makes searching for pivots faster. * * We also often have the pattern of double twins: * ? ... ? ... 1 * . \ . ... . * ? ... ? ... -1 * . ... . \ . * 1 ... -1 ... 0 * We can swap either of the columns to pivot the 1 or -1 * to the diagonal. Note that you can also treat this pattern * as 2 twins on the ?-diagonal elements. These should be taken * care of first. */ ISparseMatrixElement <T> twin1 = null, twin2 = null; var start = 1; bool anotherPassNeeded; do { bool swapped; anotherPassNeeded = swapped = false; // Search for zero diagonals with lone twins. for (var j = start; j <= size; j++) { if (matrix.FindDiagonalElement(j) == null) { var twins = CountTwins(matrix, j, ref twin1, ref twin2, size); if (twins == 1) { // Lone twins found, swap columns matrix.SwapColumns(twin2.Column, j); swapped = true; } else if (twins > 1 && !anotherPassNeeded) { anotherPassNeeded = true; start = j; } } } // All lone twins are gone, look for zero diagonals with multiple twins. if (anotherPassNeeded) { for (var j = start; !swapped && j <= size; j++) { if (matrix.FindDiagonalElement(j) == null) { CountTwins(matrix, j, ref twin1, ref twin2, size); matrix.SwapColumns(twin2.Column, j); swapped = true; } } } }while (anotherPassNeeded); }
/// <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); }
/// <summary> /// Move the pivot to the diagonal for this elimination step. /// </summary> /// <param name="matrix">The matrix.</param> /// <param name="rhs">The right-hand side vector.</param> /// <param name="pivot">The pivot element.</param> /// <param name="eliminationStep">The elimination step.</param> /// <remarks> /// This is done by swapping the rows and columns of the diagonal and that of the pivot. /// </remarks> /// <exception cref="ArgumentNullException"> /// Thrown if <paramref name="matrix"/>, <paramref name="rhs"/> or <paramref name="pivot"/> is <c>null</c>. /// </exception> public void MovePivot(ISparseMatrix <T> matrix, ISparseVector <T> rhs, ISparseMatrixElement <T> pivot, int eliminationStep) { matrix.ThrowIfNull(nameof(matrix)); rhs.ThrowIfNull(nameof(rhs)); pivot.ThrowIfNull(nameof(pivot)); // If we haven't setup, just skip if (_markowitzProduct == null) { return; } int oldProduct; var row = pivot.Row; var col = pivot.Column; // If the pivot is a singleton, then we just consumed it if (_markowitzProduct[row] == 0 || _markowitzProduct[col] == 0) { Singletons--; } // Exchange rows if (row != eliminationStep) { // Swap row Markowitz numbers var tmp = _markowitzRow[row]; _markowitzRow[row] = _markowitzRow[eliminationStep]; _markowitzRow[eliminationStep] = tmp; // Update the Markowitz product oldProduct = _markowitzProduct[row]; _markowitzProduct[row] = _markowitzRow[row] * _markowitzColumn[row]; if (oldProduct == 0) { if (_markowitzProduct[row] != 0) { Singletons--; } } else { if (_markowitzProduct[row] == 0) { Singletons++; } } } // Exchange columns if (col != eliminationStep) { // Swap column Markowitz numbers var tmp = _markowitzColumn[col]; _markowitzColumn[col] = _markowitzColumn[eliminationStep]; _markowitzColumn[eliminationStep] = tmp; // Update the Markowitz product oldProduct = _markowitzProduct[col]; _markowitzProduct[col] = _markowitzRow[col] * _markowitzColumn[col]; if (oldProduct == 0) { if (_markowitzProduct[col] != 0) { Singletons--; } } else { if (_markowitzProduct[col] == 0) { Singletons++; } } } // Also update the moved pivot oldProduct = _markowitzProduct[eliminationStep]; _markowitzProduct[eliminationStep] = _markowitzRow[eliminationStep] * _markowitzColumn[eliminationStep]; if (oldProduct == 0) { if (_markowitzProduct[eliminationStep] != 0) { Singletons--; } } else { if (_markowitzProduct[eliminationStep] == 0) { Singletons++; } } }
/// <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))