/// <summary> /// This function solves simultaneous equations in matrix form. /// The equations are represented by the matrix equation: /// /// aMatrix xVector = bVector /// /// The algorithm is from the book: /// "Mathematical Methods For Digital Computers" Volume 2 /// Edited by Anthony Ralston and Herbert S. Wilf, 1967, /// John Wiley and Sons, pages 65-93. /// "The solution of ill-conditioned linear equations" /// by J. H. Wilkinson. /// </summary> /// <param name="numberOfEquations">The number of equations</param> /// <param name="aMatrix">A square matrix</param> /// <param name="bVector">A vector that contains the right hand side of the matrix equations shown below.</param> /// <param name="xVector">A vector to contain the solution of the matrix equations.</param> /// <returns> /// This function returns an enumerated value of type Status_T /// that is the value LinearEquationSolverStatus.Singular if the /// coefficient matrix is singular to working accuracy. A value of /// LinearEquationSolverStatus.IllConditioned is returned if the /// coefficient matrix is singular to working accuracy, i.e. the /// floating point arithmetic does not have enough precision to /// guarantee convergence to an accurate solution. /// The value LinearEquationSolverStatus.Success is returned /// if the solution vector was calculated successfully. /// </returns> public static LinearEquationSolverStatus Solve(int numberOfEquations, Sparse2DMatrix <int, int, double> aMatrix, SparseArray <int, double> bVector, SparseArray <int, double> xVector) { //---------------------------------------------------------- // Matrix a_matrix is copied into working matrix aMatrixCopy. //---------------------------------------------------------- Sparse2DMatrix <int, int, double> aMatrixCopy = new Sparse2DMatrix <int, int, double>(aMatrix); //---------------------------------------------------------- // The maximum value rowMaximumVector[i], i = 0 to n - 1 // is stored //---------------------------------------------------------- SparseArray <int, double> rowMaximumVector = new SparseArray <int, double>(); int i; for (i = 0; i < numberOfEquations; i++) { double temp = 0.0; for (int j = 0; j < numberOfEquations; j++) { double test = Math.Abs(aMatrix[i, j]); if (test > temp) { temp = test; } } rowMaximumVector[i] = temp; //---------------------------------------------------------- // Test for singular matrix. //---------------------------------------------------------- if (Math.Abs(temp) <= 0.0) { return(LinearEquationSolverStatus.Singular); } } //---------------------------------------------------------- // The r'th column of "l", the r'th pivotal position r', and // the r'th row of "u" are determined. //---------------------------------------------------------- SparseArray <int, int> pivotRowArray = new SparseArray <int, int>(); for (int r = 0; r < numberOfEquations; r++) { double maximumValue = 0.0; int rowMaximumValueIndex = r; //---------------------------------------------------------- // The l[i][r], i = r to n - 1 are determined. // l[i][r] is a lower triangular matrix. It is calculated // using the variable temp and copied into matrix // "aMatrixCopy". The variable "maximumValue" contains // the largest Math.Abs(l[i][r] / pivotRowArray[i]) and // rowMaximumValueIndex stores the "i" which corresponds // to the value in variable maximumValue. //---------------------------------------------------------- double temp; for (i = r; i < numberOfEquations; i++) { temp = aMatrixCopy[i, r]; for (int j = 0; j < r; j++) { temp = temp - aMatrixCopy[i, j] * aMatrixCopy[j, r]; } aMatrixCopy[i, r] = temp; double test = Math.Abs(temp / rowMaximumVector[i]); if (test > maximumValue) { maximumValue = test; rowMaximumValueIndex = i; } } //---------------------------------------------------------- // Test for matrix which is singular to working precision. //---------------------------------------------------------- if (Math.Abs(maximumValue) <= 0.0) { return(LinearEquationSolverStatus.IllConditioned); } //---------------------------------------------------------- // "rowMaximumValueIndex" = r' and "pivotRowArray[r]" // is the pivotal row. //---------------------------------------------------------- rowMaximumVector[rowMaximumValueIndex] = rowMaximumVector[r]; pivotRowArray[r] = rowMaximumValueIndex; //---------------------------------------------------------- // Rows "r" and "pivotRowArray[r]" are exchanged. //---------------------------------------------------------- for (i = 0; i < numberOfEquations; i++) { temp = aMatrixCopy[r, i]; aMatrixCopy[r, i] = aMatrixCopy[rowMaximumValueIndex, i]; aMatrixCopy[rowMaximumValueIndex, i] = temp; } //---------------------------------------------------------- // The u[r][i], i = r + 1 to n - 1 are determined. // "u[r][i]" is an upper triangular matrix. It is copied // into aMatrixCopy. //---------------------------------------------------------- for (i = r + 1; i < numberOfEquations; i++) { temp = aMatrixCopy[r, i]; for (int j = 0; j < r; j++) { temp = temp - aMatrixCopy[r, j] * aMatrixCopy[j, i]; } aMatrixCopy[r, i] = temp / aMatrixCopy[r, r]; } } //---------------------------------------------------------- // The first solution vector is set equal to the null vector // and the first residuals are set equal to the equation // constant vector. //---------------------------------------------------------- SparseArray <int, double> residualVector = new SparseArray <int, double>(); for (i = 0; i < numberOfEquations; i++) { xVector[i] = 0.0; residualVector[i] = bVector[i]; } //---------------------------------------------------------- // The iteration counter is initialized. //---------------------------------------------------------- int iteration = 0; bool notConvergedFlag = true; do { //---------------------------------------------------------- // The forward substitution is performed and the solution // of l y = p r is calculated where p r is the current // residual after interchanges. //---------------------------------------------------------- for (i = 0; i < numberOfEquations; i++) { int pivotRowIndex = pivotRowArray[i]; double temp = residualVector[pivotRowIndex]; residualVector[pivotRowIndex] = residualVector[i]; for (int j = 0; j < i; j++) { temp = temp - aMatrixCopy[i, j] * residualVector[j]; } residualVector[i] = temp / aMatrixCopy[i, i]; } //---------------------------------------------------------- // The back substitution is performed and the solution of // u e = y is calculated. The current correction is stored // in variable residualVector. //---------------------------------------------------------- for (i = numberOfEquations - 1; i >= 0; i--) { double temp = residualVector[i]; for (int j = i + 1; j < numberOfEquations; j++) { temp = temp - aMatrixCopy[i, j] * residualVector[j]; } residualVector[i] = temp; } //---------------------------------------------------------- // The norm of the error in the residuals and the norm of // the present solution vector are calculated. //---------------------------------------------------------- double normOfX = 0.0; double normOfError = 0.0; for (i = 0; i < numberOfEquations; i++) { double test = Math.Abs(xVector[i]); if (test > normOfX) { normOfX = test; } test = Math.Abs(residualVector[i]); if (test > normOfError) { normOfError = test; } } //---------------------------------------------------------- // If iteration is zero then skip this section because // no correction exists on the first iteration. //---------------------------------------------------------- if (iteration != 0) { //------------------------------------------------------ // Test for matrix which is singular to working // precision. //------------------------------------------------------ if ((iteration == 1) && (normOfError / normOfX > 0.5)) { return(LinearEquationSolverStatus.IllConditioned); } //------------------------------------------------------ // Check to see if "normOfError" / "normOfX" is greater // than 2 ^ (1 - t), where "t" is the number of bits // in the mantissa of a double precision number. If // this is not true then the last correction is almost // negligible and "notConvergedFlag" is set to false. //------------------------------------------------------ notConvergedFlag = normOfError / normOfX >= SSmallFloatingPointValue; #if DEBUGCODE double normRatioForDebug = normOfError / normOfX; #endif } //---------------------------------------------------------- // The corrections (residuals) are added to the // solution vector. //---------------------------------------------------------- for (i = 0; i < numberOfEquations; i++) { xVector[i] = xVector[i] + residualVector[i]; } //---------------------------------------------------------- // The new residuals corresponding to the solution vector // are calculated. //---------------------------------------------------------- for (i = 0; i < numberOfEquations; i++) { double temp = bVector[i]; for (int j = 0; j < numberOfEquations; j++) { temp = temp - aMatrix[i, j] * xVector[j]; } residualVector[i] = temp; } //---------------------------------------------------------- // The iteration counter is incremented and the flag // "notConvergedFlag" is tested. If notConvergedFlag is false // then the solution vector has converged to sufficient // accuracy. //---------------------------------------------------------- iteration++; }while (notConvergedFlag); return(LinearEquationSolverStatus.Success); }
/// <summary> /// This method gets a term in the simultaneous equation. The term /// can be a number, a variable, or a number and a variable. A term /// cannot be split between lines input to this method. /// </summary> /// <param name="inputLine">The input line to be parsed</param> /// <param name="positionIndex">The current parse position in the input string.</param> /// <param name="aMatrix">The A matrix for the simultaneous equations. /// This is updated as each line of input is parsed. /// </param> /// <param name="bVector">The B vector for the simultaneous equations. /// This is updated as each line of input is parsed.</param> /// <param name="variableNameIndexMap">A map that stores the integer /// index for a variable using the variable name as a key.</param> /// <returns>This method returns the value 'true' if and only if a term is found. /// </returns> private bool GetTerm(string inputLine, ref int positionIndex, Sparse2DMatrix <int, int, double> aMatrix, SparseArray <int, double> bVector, SparseArray <string, int> variableNameIndexMap) { //------------------------------------------------------------------ // A term is one of the following three patterns. // // <Space> <Sign> Number <Space> // <Space> <Sign> Variable <Space> // <Space> <Sign> Number Variable <Space> // // Check for a plus or a minus sign at the start of a term. //------------------------------------------------------------------ bool numberIsNegativeFlag = false; GetSign(inputLine, ref positionIndex, ref numberIsNegativeFlag); //------------------------------------------------------------------ // Skip whitespace characters //------------------------------------------------------------------ SkipSpaces(inputLine, ref positionIndex); //------------------------------------------------------------------ // Check to see if this is a number or a variable. //------------------------------------------------------------------ string numberString = ""; bool haveNumberFlag = GetNumber(inputLine, ref positionIndex, ref numberString); //------------------------------------------------------------------ // If an error occurred then abort. //------------------------------------------------------------------ if (LastStatusValue != LinearEquationParserStatus.Success) { return(false); } //------------------------------------------------------------------ // If there was a number encountered then test to see if the // number has an exponent. //------------------------------------------------------------------ if (haveNumberFlag) { if (positionIndex < inputLine.Length) { //---------------------------------------------------------- // Does the number have an exponent? //---------------------------------------------------------- if (inputLine[positionIndex] == '^') { positionIndex++; //------------------------------------------------------ // Does the exponent have a sign. //------------------------------------------------------ bool negativeExponentFlag = false; GetSign(inputLine, ref positionIndex, ref negativeExponentFlag); //------------------------------------------------------ // Get the exponent digits. //------------------------------------------------------ string exponentString = ""; if (GetNumber(inputLine, ref positionIndex, ref exponentString)) { //-------------------------------------------------- // Is the exponent a valid exponent. //-------------------------------------------------- var exponentLength = exponentString.Length; if (exponentLength <= 2) { var exponent_error_flag = false; for (var i = 0; i < exponentLength; ++i) { if (!char.IsDigit(exponentString[i])) { exponent_error_flag = true; } } if (!exponent_error_flag) { numberString += 'E'; if (negativeExponentFlag) { numberString += '-'; } numberString += exponentString; } else { SetLastStatusValue(LinearEquationParserStatus.ErrorIllegalExponent, positionIndex); return(false); } } else { SetLastStatusValue(LinearEquationParserStatus.ErrorIllegalExponent, positionIndex); return(false); } } else { SetLastStatusValue(LinearEquationParserStatus.ErrorMissingExponent, positionIndex); return(false); } } } } //------------------------------------------------------------------ // Skip whitespace characters //------------------------------------------------------------------ SkipSpaces(inputLine, ref positionIndex); string variableName = ""; bool haveVariableNameFlag = GetVariableName(inputLine, ref positionIndex, ref variableName); //------------------------------------------------------------------ // Calculate the sign of the value. The sign is negated // if the equals sign has been encountered. //------------------------------------------------------------------ bool negativeFlag = m_equalSignInEquationFlag ^ m_negativeOperatorFlag ^ numberIsNegativeFlag; double value = 0.0; if (haveNumberFlag) { value = Convert.ToDouble(numberString); if (negativeFlag) { value = -value; } } else { value = 1.0; if (negativeFlag) { value = -value; } } //------------------------------------------------------------------ // If a variable was encountered then add to the aMatrix. //------------------------------------------------------------------ var haveTermFlag = false; if (haveVariableNameFlag) { //-------------------------------------------------------------- // If either a number or a variable is found then a term was // found. //-------------------------------------------------------------- haveTermFlag = true; //-------------------------------------------------------------- // Each equation must have at least one variable. // Record that a variable was encountered in this equation. //-------------------------------------------------------------- m_atLeastOneVariableInEquationFlag = true; //-------------------------------------------------------------- // If this variable has not been encountered before then add // the variable to the variableNameIndexMap. //-------------------------------------------------------------- if (!variableNameIndexMap.TryGetValue(variableName, out int variableNameIndex)) { // This is a new variable. Add it to the map. variableNameIndex = variableNameIndexMap.Count; variableNameIndexMap[variableName] = variableNameIndex; } aMatrix[m_equationIndex, variableNameIndex] = aMatrix[m_equationIndex, variableNameIndex] + value; } else if (haveNumberFlag) { //-------------------------------------------------------------- // If either a number or a variable is found then a term was // found. //-------------------------------------------------------------- haveTermFlag = true; //-------------------------------------------------------------- // Put the value in the B vector. //-------------------------------------------------------------- bVector[m_equationIndex] = bVector[m_equationIndex] - value; } else { haveTermFlag = false; SetLastStatusValue(LinearEquationParserStatus.ErrorNoTermEncountered, positionIndex); return(false); } //------------------------------------------------------------------ // There must be at least one term on each side of the equal sign. //------------------------------------------------------------------ if (m_equalSignInEquationFlag) { m_termAfterEqualSignExistsFlag = true; } else { m_termBeforeEqualSignExistsFlag = true; } //------------------------------------------------------------------ // Skip whitespace characters //------------------------------------------------------------------ SkipSpaces(inputLine, ref positionIndex); return(true); }
/// <summary> /// Copy constructor. /// </summary> /// <param name="sparseArray">The sparse array instance to be copied</param> public SparseArray(SparseArray <TKey, TValue> sparseArray) { _mDictionary = new Dictionary <TKey, TValue>(); Initialize(sparseArray); DefaultValue = sparseArray.DefaultValue; }
/// <summary> /// This function parses line that contains all or part of a simple /// linear equation. The equation contains terms separated by operators. /// The term can be a number, a variable, or a number and a variable. /// A term cannot be split between lines input to the parser method. /// The operators are either the plus character '+', the minus /// character '-', or the equal sign character '='. Numbers can have /// up to 15 digits, a decimal point, and an exponent of a power /// of 10 that follows the '^' character. /// </summary> /// <param name="inputLine">The input line of text to be parsed</param> /// <param name="aMatrix">The A matrix for the simultaneous equations. /// This is updated as each line of input is parsed. /// </param> /// <param name="bVector">The B vector for the simultaneous equations. /// This is updated as each line of input is parsed.</param> /// <param name="variableNameIndexMap">A map that stores the integer /// index for a variable using the variable name as a key.</param> /// <param name="numberOfEquations">A reference to an integer that /// will contain the number of equations when this method exits. /// </param> /// <returns>An enum of type LinearEquationParserStatus</returns> public LinearEquationParserStatus Parse(string inputLine, Sparse2DMatrix <int, int, double> aMatrix, SparseArray <int, double> bVector, SparseArray <string, int> variableNameIndexMap, ref int numberOfEquations) { //------------------------------------------------------------------ // Trim any space characters from the end of the line. //------------------------------------------------------------------ inputLine.TrimEnd(null); //------------------------------------------------------------------ // Assume success status. //------------------------------------------------------------------ int positionIndex = 0; SetLastStatusValue(LinearEquationParserStatus.Success, positionIndex); //------------------------------------------------------------------ // Skip whitespace characters //------------------------------------------------------------------ SkipSpaces(inputLine, ref positionIndex); //------------------------------------------------------------------ // Save the position of the first non-whitespace character. If // the first term is not found at this position then set the // error status to report that it is likely that the last // equation was not properly terminated. //------------------------------------------------------------------ m_startPosition = positionIndex; //------------------------------------------------------------------ // Separate the input string into tokens. // // Variables contains the letters A through Z and the underscore // '_' character. // // Operators include plus '+', minus '-', and times '*'. // // Delimiters include the equals sign '='. // // Numbers include the digits 0 through 9, the decimal point // (period character) '.', an optional exponent character which // is the letter '^', and up to two digits for the optional exponent. // // Check for sequences of terms and operators. // // Term: // <Space> <Sign> Number <Space> // <Space> <Sign> Variable <Space> // <Space> <Sign> Number Variable <Space> // // Operator: // <Space> Plus <Space> // <Space> Minus <Space> // <Space> Equals <Space> // //-------------------------------------------------------------- bool operatorFoundLast = false; while (positionIndex < inputLine.Length) { if (m_parserState == LinearEquationParserState.ParseTerm) { //------------------------------------------------------ // Skip whitespace characters //------------------------------------------------------ SkipSpaces(inputLine, ref positionIndex); if (positionIndex < inputLine.Length) { if (GetTerm(inputLine, ref positionIndex, aMatrix, bVector, variableNameIndexMap)) { m_parserState = LinearEquationParserState.ParseOperator; operatorFoundLast = false; } else { if (LastStatusValue == LinearEquationParserStatus.Success) { SetLastStatusValue(LinearEquationParserStatus.ErrorIllegalEquation, positionIndex); } break; } } else { break; } } else if (m_parserState == LinearEquationParserState.ParseOperator) { //------------------------------------------------------ // Skip whitespace characters //------------------------------------------------------ SkipSpaces(inputLine, ref positionIndex); if (positionIndex < inputLine.Length) { //------------------------------------------------------ // Check for plus sign, minus sign, or an equal sign. //------------------------------------------------------ if (GetOperator(inputLine, ref positionIndex)) { m_parserState = LinearEquationParserState.ParseTerm; operatorFoundLast = true; } else { if (LastStatusValue != LinearEquationParserStatus.Success) { if (positionIndex == m_startPosition) { SetLastStatusValue(LinearEquationParserStatus.ErrorIllegalEquation, positionIndex); } } break; } } } } // If an operator was found at if ((positionIndex >= inputLine.Length) && (positionIndex > 0) && (!operatorFoundLast)) { ResetForNewEquation(); numberOfEquations = m_equationIndex; } return(LastStatusValue); }