private void WriteToStream(ISparseMatrix matrix, StreamWriter writer) { SparseFormat sparseFormat = matrix.GetSparseFormat(); writer.Write(sparseFormat.RawValuesTitle + ": "); if (titlesOnOtherLines) { writer.WriteLine(); } WriteArray(sparseFormat.RawValuesArray, writer, false); foreach (var nameArrayPair in sparseFormat.RawIndexArrays) { if (lineBetweenArrays) { writer.WriteLine(); } writer.WriteLine(); // otherwise everything would be on the same line writer.Write(nameArrayPair.Key + ": "); if (titlesOnOtherLines) { writer.WriteLine(); } WriteArray(nameArrayPair.Value, writer, false); } }
/// <summary> /// Links matrix elements. /// </summary> /// <param name="matrix">The matrix.</param> /// <param name="row">The row variable.</param> /// <param name="column">The column variable.</param> private void LinkElement(ISparseMatrix <T> matrix, Bridge <int> row, Bridge <int> column) { var loc = Solver.ExternalToInternal(new MatrixLocation(row.Local, column.Local)); // Do we need to create an element? var local_elt = matrix.FindElement(loc); if (local_elt == null) { // Check if solving will result in an element var left = matrix.GetFirstInRow(loc.Row); if (left == null || left.Column > Solver.Size - Solver.Degeneracy) { return; } var top = matrix.GetFirstInColumn(loc.Column); if (top == null || top.Row > Solver.Size - Solver.Degeneracy) { return; } // Create the element because decomposition will cause these elements to be created local_elt = matrix.GetElement(loc); } if (local_elt == null) { return; } var parent_elt = Parent.Solver.GetElement(new MatrixLocation(row.Global, column.Global)); _elements.Add(new Bridge <Element <T> >(local_elt, parent_elt)); }
/// <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> /// Accumulates an identity matrix, but only for selected variabels /// </summary> /// <param name="M">matrix to be modified (input/output)</param> /// <param name="factor"></param> /// <param name="iVar"></param> static public void AccEyeSp(this ISparseMatrix M, UnsetteledCoordinateMapping map, int[] iVar, double factor = 1.0) { using (new FuncTrace()) { if (M.RowPartitioning.LocalLength != M.ColPartition.LocalLength) { throw new ArgumentException("supported only for quadratic matrices"); } if (map.LocalLength != M.RowPartitioning.LocalLength) { throw new ArgumentException(); } int J = map.GridDat.iLogicalCells.NoOfLocalUpdatedCells; for (int j = 0; j < J; j++) { foreach (int f in iVar) { int Np = map.BasisS[f].GetLength(j); for (int n = 0; n < Np; n++) { int idx = map.GlobalUniqueCoordinateIndex(f, j, n); M.SetDiagonalElement(idx, M.GetDiagonalElement(idx) + factor); } } } } }
/// <summary> /// Writes each internal array of the provided sparse matrix to a different file. All these filenames have a common /// prefix, extracted from <paramref name="pathBase"/>, and a suffix corresponding to the purpose of each array. /// </summary> /// <param name="matrix">The sparse matrix to write.</param> /// <param name="pathBase">An absolute path. This filename will not be used directly. Instead one file per internal array /// of <paramref name="matrix"/> will be used. Each file will be suffixed with the name of that array and then the /// extension specified in <paramref name="pathBase"/>.</param> /// <param name="writeArrayLengthFirst">If true, the first line of each file will contain the length of the corresponding /// array. If false, there will be only one line per file, which will contain the array.</param> public void WriteToMultipleFiles(ISparseMatrix matrix, string pathBase, bool writeArrayLengthFirst = true) // TODO: this should be a different writer { string path = Path.GetDirectoryName(pathBase); string nameOnly = Path.GetFileNameWithoutExtension(pathBase); string ext = Path.GetExtension(pathBase); SparseFormat sparseFormat = matrix.GetSparseFormat(); // Values array string suffix = "-" + sparseFormat.RawValuesTitle.ToLower(); string valuesPath = path + "\\" + nameOnly + suffix + ext; // Not too sure about the \\ using (var writer = new StreamWriter(valuesPath)) { #if DEBUG writer.AutoFlush = true; // To look at intermediate output at certain breakpoints #endif WriteArray(sparseFormat.RawValuesArray, writer, writeArrayLengthFirst); } // Indexing arrays foreach (var nameArrayPair in sparseFormat.RawIndexArrays) { suffix = "-" + nameArrayPair.Key.ToLower(); string indexerPath = path + "\\" + nameOnly + suffix + ext; // Not too sure about the \\ using (var writer = new StreamWriter(indexerPath)) { #if DEBUG writer.AutoFlush = true; // To look at intermediate output at certain breakpoints #endif WriteArray(nameArrayPair.Value, writer, writeArrayLengthFirst); } } }
private static void TestWriteOperation(ISparseMatrix matrix, string referenceFile, RawArraysWriter writer) { string tempFile = Guid.NewGuid().ToString() + ".txt"; writer.WriteToFile(matrix, tempFile); bool success = IOUtilities.AreFilesEquivalent(referenceFile, tempFile); File.Delete(tempFile); Assert.True(success); }
public static void GetI0(ref int _ref, out int i0, out int ierr) { ierr = 0; i0 = -1; try { ISparseMatrix M = (ISparseMatrix)Infrastructure.GetObject(_ref); i0 = (int)M.RowPartitioning.i0; } catch (Exception e) { ierr = Infrastructure.ErrorHandler(e); } }
public static void GetLocLen(ref int _ref, out int LocLen, out int ierr) { ierr = 0; LocLen = -1; try { ISparseMatrix M = (ISparseMatrix)Infrastructure.GetObject(_ref); LocLen = M.RowPartitioning.LocalLength; } catch (Exception e) { ierr = Infrastructure.ErrorHandler(e); } }
private void Count(ISparseMatrix <T> matrix, ISparseVector <T> rhs, int step, int max) { ISparseMatrixElement <T> element; // Get the first element in the vector var rhsElement = rhs.GetFirstInVector(); // Generate Markowitz row count for (var i = max; i >= step; i--) { // Set count to -1 initially to remove count due to pivot element var count = -1; element = matrix.GetFirstInRow(i); while (element != null && element.Column < step) { element = element.Right; } while (element != null) // We want to count the elements outside the limit as well { count++; element = element.Right; } // Include elements on the Rhs vector while (rhsElement != null && rhsElement.Index < step) { rhsElement = rhsElement.Below; } if (rhsElement != null && rhsElement.Index == i) { count++; } _markowitzRow[i] = Math.Min(count, _maxMarkowitzCount); } // Generate Markowitz column count for (var i = step; i <= max; i++) { // Set count to -1 initially to remove count due to pivot element var count = -1; element = matrix.GetFirstInColumn(i); while (element != null && element.Row < step) { element = element.Below; } while (element != null) { count++; element = element.Below; } _markowitzColumn[i] = Math.Min(count, _maxMarkowitzCount); } }
public static void GetRowPart(ref int MtxRef, out int PartRef, out int ierr) { ierr = 0; PartRef = -1; try { ISparseMatrix M = (ISparseMatrix)Infrastructure.GetObject(MtxRef); PartRef = Infrastructure.RegisterObject(M.RowPartitioning); } catch (Exception e) { ierr = Infrastructure.ErrorHandler(e); } }
private void WriteSparseMatrix(ISparseMatrix matrix, StreamWriter writer) { string numberFormat = NumericFormat.GetRealNumberFormat(); foreach (var(row, col, val) in matrix.EnumerateNonZeros()) { writer.Write($"{row + 1} {col + 1} "); writer.WriteLine(string.Format(numberFormat, val)); } writer.Write($"{matrix.NumRows} {matrix.NumColumns} 0.0"); }
/// <summary> /// Setup the pivot strategy. /// </summary> /// <param name="matrix">The matrix.</param> /// <param name="rhs">The right-hand side vector.</param> /// <param name="eliminationStep">The current elimination step.</param> /// <param name="max">The maximum row/column index.</param> /// <exception cref="ArgumentNullException"> /// Thrown if <paramref name="matrix"/> or <paramref name="rhs"/> is <c>null</c>. /// </exception> public void Setup(ISparseMatrix <T> matrix, ISparseVector <T> rhs, int eliminationStep, int max) { matrix.ThrowIfNull(nameof(matrix)); rhs.ThrowIfNull(nameof(rhs)); // Initialize Markowitz row, column and product vectors if necessary if (_markowitzRow == null || _markowitzRow.Length != matrix.Size + 1) { Initialize(matrix); } Count(matrix, rhs, eliminationStep, max); Products(eliminationStep, max); }
/// <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++; } } }
/// <summary> /// Adds <paramref name="factor"/> to all diagonal entries of <paramref name="M"/>. /// </summary> static public void AccEyeSp(this ISparseMatrix M, double factor = 1.0) { using (new FuncTrace()) { if (M.RowPartitioning.LocalLength != M.ColPartition.LocalLength) { throw new ArgumentException("supported only for quadratic matrices"); } var rm = M.RowPartitioning; int i0 = (int)rm.i0; int L = rm.LocalLength; for (int i = 0; i < L; i++) { M.SetDiagonalElement(i + i0, M.GetDiagonalElement(i + i0) + factor); } } }
/// <summary> /// Find a pivot in the matrix. /// </summary> /// <param name="matrix">The matrix.</param> /// <param name="eliminationStep">The current elimination step.</param> /// <param name="max">The maximum row/column index of any pivot.</param> /// <returns>The pivot information.</returns> /// <remarks> /// The pivot should be searched for in the submatrix towards the right and down of the /// current diagonal at row/column <paramref name="eliminationStep" />. This pivot element /// will be moved to the diagonal for this elimination step. /// </remarks> /// <exception cref="ArgumentNullException">Thrown if <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 Pivot <ISparseMatrixElement <T> > FindPivot(ISparseMatrix <T> matrix, int eliminationStep, int max) { matrix.ThrowIfNull(nameof(matrix)); eliminationStep.GreaterThan(nameof(eliminationStep), 0); max.GreaterThan(nameof(max), 0); // No pivot possible if we're already eliminating outside of our bounds if (eliminationStep > max) { return(Pivot <ISparseMatrixElement <T> > .Empty); } // Fix the search limit to allow our strategies to work foreach (var strategy in Strategies) { var chosen = strategy.FindPivot(this, matrix, eliminationStep, max); if (chosen.Info != PivotInfo.None) { return(chosen); } } return(Pivot <ISparseMatrixElement <T> > .Empty); }
/// <summary> /// Solves the linear system (diag(1/<paramref name="dt"/>) + /// <em>M</em> / 2) * x = /// <see cref="ImplicitTimeStepper.CurrentState"/> / /// <paramref name="dt"/> - /// <em>M</em> * /// <see cref="ImplicitTimeStepper.CurrentState"/> / 2 - /// 0.5*(<see cref="ImplicitTimeStepper.m_AffineOffset1"/> + /// <see cref="m_AffineOffset0"/> ) /// and writes the /// result do <see cref="ImplicitTimeStepper.CurrentState"/>. /// </summary> /// <param name="dt">The length of the timestep</param> protected override void PerformTimeStep(double dt) { using (var tr = new ilPSP.Tracing.FuncTrace()) { if (m_Theta <= 0 || m_Theta > 1) { throw new ApplicationException("m_Theta out of range; must be betwwen 0.0 (including) and 1.0 (including), but m_Theta = " + m_Theta); } int n = Mapping.LocalLength; int np = m_diagVecOneSec.Length; double[] diag = new double[n]; for (int i = 0; i < n; i++) { diag[i] = m_diagVecOneSec[i % np]; } BLAS.dscal(n, 1.0 / (m_Theta * dt), diag, 1); double[] rhs; if (m_AffineOffset0 != null) { rhs = m_AffineOffset0; BLAS.dscal(rhs.Length, -(1.0 - m_Theta) / m_Theta, rhs, 1); BLAS.daxpy(rhs.Length, -1.0, m_AffineOffset1, 1, rhs, 1); } else { rhs = (double[])m_AffineOffset1.Clone(); } if (m_Theta != 1.0) { // if m_Theta == 0.0, this has no effect and is a waste of comp. power ISparseMatrix eM = m_Solver.GetMatrix(); eM.SpMV <CoordinateVector, double[]>(-(1.0 - m_Theta) / m_Theta, CurrentState, 1.0, rhs); } for (int i = 0; i < n; i++) { rhs[i] += diag[i] * CurrentState[i]; // fraglich } tr.Info("Calling solver"); LastSolverResult = m_Solver.Solve <double[], CoordinateVector, double[]>(1.0, diag, CurrentState, rhs); tr.Info("no. of iterations: " + LastSolverResult.NoOfIterations); tr.Info("converged? : " + LastSolverResult.Converged); tr.Info("Pure solver runtime: " + LastSolverResult.RunTime.TotalSeconds + " sec."); /* * int n = Mapping.LocalLength; * int np = m_diagVecOneSec.Length; * * double[] diag = new double[n]; * for (int i = 0; i < n; i++) { * diag[i] = m_diagVecOneSec[i % np]; * } * BLAS.dscal(n, 1.0 / dt, diag, 1); * * double[] rhs = (double[])m_AffineOffset1.Clone(); * BLAS.dscal(n, -1.0, rhs, 1); * * for (int i = 0; i < n; i++) { * rhs[i] += diag[i] * DGCoordinates[i]; * } * * m_Context.IOMaster.tracer.Message(ht, "Calling solver"); * LastSolverResult = m_Solver.Solve<double[], CoordinateVector, double[]>(1.0, diag, DGCoordinates, rhs); */ } }
private static void TestWriteOperation(ISparseMatrix matrix, string referenceFile) => TestWriteOperation(matrix, referenceFile, new CoordinateTextFileWriter());
/// <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 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);
/// <summary> /// Initializes a new instance of <see cref="DokColMajor"/> with the specified matrix dimensions and the non-zero /// entries of the provided sparse matrix. /// </summary> /// <param name="matrix">A sparse matrix whose dimensions and non-zero entries will be used to intialize the new /// <see cref="DokColMajor"/>.</param> public static DokColMajor CreateFromSparseMatrix(ISparseMatrix matrix) => CreateFromSparsePattern(matrix.NumRows, matrix.NumColumns, matrix.EnumerateNonZeros());
private static void TestWriteOperation(ISparseMatrix matrix, string referenceFile) => TestWriteOperation(matrix, referenceFile, new RawArraysWriter(true, true));
/// <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++; } } }
/// <summary> /// Writes the internal arrays of the provided sparse matrix to Console. /// </summary> /// <param name="matrix">The sparse matrix to write.</param> public void WriteToConsole(ISparseMatrix matrix) { Utilities.WriteToConsole((writer) => WriteToStream(matrix, writer)); }
/// <summary> /// Writes the internal arrays of the provided sparse matrix to the file at <paramref name="path"/>. /// </summary> /// <param name="matrix">The sparse matrix to write.</param> /// <param name="path">The absolute path of the file, where <paramref name="matrix"/> will be written.</param> /// <param name="append">If true, <paramref name="matrix"/> will be written after the current contents of the file at /// <paramref name="path"/>. If false, it will overwrite them.</param> public void WriteToFile(ISparseMatrix matrix, string path, bool append = false) { Utilities.WriteToFile((writer) => WriteToStream(matrix, writer), path, append); }
/// <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; 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); }
/// <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))