/// <summary> /// ctor, denominator will be set to one /// </summary> /// <param name="numerator"></param> public Rational(T numerator) : this(numerator, Policy.One()) { }
/// <summary> /// Create the tableau by itself. /// </summary> /// <param name="maximize">if true, goal is to maximize the objective function</param> /// <returns>created tableau</returns> protected Matrix <T, TPolicy> CreateTableau(bool maximize) { // create a matrix of the correct size int width = numDecisionVariables + numSlackVariables + numArtificialVariables + NumObjectiveFunctions + 1; // + 1 is for RHS int height = constraints.Count + NumObjectiveFunctions; var matrix = new Matrix <T, TPolicy>(width, height); var minusOne = Policy.Sub(Policy.Zero(), Policy.One()); // initialize the objective function rows if (NumObjectiveFunctions == 2) { matrix[0, 0] = minusOne; } int zIndex = (NumObjectiveFunctions == 1) ? 0 : 1; matrix[zIndex, zIndex] = maximize ? Policy.One() : minusOne; Vector <T, TPolicy> objectiveCoefficients = maximize ? f.Coefficients * minusOne : f.Coefficients; var rowData = matrix.GetRow(zIndex); CopyArray(objectiveCoefficients.Data, rowData); matrix.SetRow(zIndex, rowData); matrix[width - 1, zIndex] = maximize ? f.ConstantTerm : Policy.Mul(minusOne, f.ConstantTerm); if (!restrictToNonNegative) { matrix[SlackVariableOffset - 1, zIndex] = InvertedCoefficientSum(objectiveCoefficients); } // initialize the constraint rows int slackVar = 0; int artificialVar = 0; for (int i = 0; i < constraints.Count; i++) { var constraint = constraints[i]; int row = NumObjectiveFunctions + i; rowData = matrix.GetRow(row); // decision variable coefficients CopyArray(constraint.Coefficients.Data, rowData); matrix.SetRow(row, rowData); // x- if (!restrictToNonNegative) { matrix[SlackVariableOffset - 1, row] = InvertedCoefficientSum(constraint.Coefficients); } // RHS matrix[width - 1, row] = constraint.Value; // slack variables if (constraint.Relationship == Relationship.LEQ) { matrix[SlackVariableOffset + slackVar++, row] = Policy.One(); // slack } else if (constraint.Relationship == Relationship.GEQ) { matrix[SlackVariableOffset + slackVar++, row] = minusOne; // excess } // artificial variables if ((constraint.Relationship == Relationship.EQ) || (constraint.Relationship == Relationship.GEQ)) { matrix[ArtificialVariableOffset + artificialVar, 0] = Policy.One(); matrix[ArtificialVariableOffset + artificialVar++, row] = Policy.One(); matrix.SetRowVector(0, matrix.GetRowVector(0) - matrix.GetRowVector(row)); } } return(matrix); }
/// <summary> /// Returns the row with the minimum ratio as given by the minimum ratio test (MRT). /// </summary> /// <param name="tableau"></param> /// <param name="col"></param> /// <returns></returns> private int?GetPivotRow(SimplexTableau <T, TPolicy> tableau, int col) { // create a list of all the rows that tie for the lowest score in the minimum ratio test List <int?> minRatioPositions = new List <int?>(); T minRatio = default(T); bool minRationUnassigned = true; for (int i = tableau.NumObjectiveFunctions; i < tableau.Height; i++) { T rhs = tableau.GetEntry(i, tableau.Width - 1); T entry = tableau.GetEntry(i, col); // only consider pivot elements larger than the cutOff threshold // selecting others may lead to degeneracy or numerical instabilities if (Precision <T, TPolicy> .CompareTo(entry, Policy.Zero(), cutOff) > 0) { T ratio = Policy.Abs(Policy.Div(rhs, entry)); // check if the entry is strictly equal to the current min ratio // do not use a ulp/epsilon check int cmp; if (minRationUnassigned) { cmp = -1; } else { cmp = Policy.Compare(ratio, minRatio); } if (cmp == 0) { minRatioPositions.Add(i); } else if (cmp < 0) { minRatio = ratio; minRationUnassigned = false; minRatioPositions.Clear(); minRatioPositions.Add(i); } } } if (minRatioPositions.Count == 0) { return(null); } else if (minRatioPositions.Count > 1) { // there's a degeneracy as indicated by a tie in the minimum ratio test // 1. check if there's an artificial variable that can be forced out of the basis if (tableau.NumArtificialVariables > 0) { foreach (int?row in minRatioPositions) { for (int i = 0; i < tableau.NumArtificialVariables; i++) { int column = i + tableau.ArtificialVariableOffset; T entry = tableau.GetEntry(row.Value, column); if (Precision <T, TPolicy> .Equals(entry, Policy.One(), epsilon) && row.Equals(tableau.GetBasicRow(column))) { return(row); } } } } // 2. apply Bland's rule to prevent cycling: // take the row for which the corresponding basic variable has the smallest index // // see http://www.stanford.edu/class/msande310/blandrule.pdf // see http://en.wikipedia.org/wiki/Bland%27s_rule (not equivalent to the above paper) int?minRow = null; int minIndex = tableau.Width; foreach (int?row in minRatioPositions) { int basicVar = tableau.GetBasicVariable(row.Value); if (basicVar < minIndex) { minIndex = basicVar; minRow = row; } } return(minRow); } return(minRatioPositions[0]); }