Exemplo n.º 1
0
        /// <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);
        }
Exemplo n.º 3
0
        /// <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);
        }
Exemplo n.º 4
0
        /// <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);
        }
Exemplo n.º 5
0
        /// <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);
        }
Exemplo n.º 7
0
        /// <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);
        }
Exemplo n.º 9
0
 /// <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);
        }
Exemplo n.º 11
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);
            }

            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);
        }
Exemplo n.º 12
0
        /// <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++;
                }
            }
        }
Exemplo n.º 13
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))