/************************************************************************\ |* gtf is a framework for analyzing two-player zero-sum games *| |* Copyright (C) 2005 Troels Bjerre Sorensen *| |* *| |* This program is free software; you can redistribute it and/or modify *| |* it under the terms of the GNU General Public License as published by *| |* the Free Software Foundation; either version 2 of the License, or *| |* (at your option) any later version. *| |* *| |* This program is distributed in the hope that it will be useful, but *| |* WITHOUT ANY WARRANTY; without even the implied warranty of *| |* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *| |* General Public License for more details. *| |* *| |* You should have received a copy of the GNU General Public License *| |* along with this program; if not, write to the *| |* Free Software Foundation, Inc., 59 Temple Place - Suite 330, *| |* Boston, MA 02111-1307, USA. *| \************************************************************************/ package gtf.util; import java.math.*; import java.io.*; import java.util.*; /** Each instance of LPDictionary holds a linear programming * dictionary, as defined by Chvátal. LPDictionary contains * methods for performing * the simplex algorithm. The implementation is optimized for * educational visualization purposes, rather than for efficiency and should * be used to solve toy instances only (unless the method {@link #PCxSolve() PCxSolve()} is used to call an external solver). * * LPDictionary is polymorphic; entries in the dictionary * can be objects of any class implementing {@link Real Real}. All * entries ought to be from the same class, though. *

Running the following program visualizes the example * presented by Chvátal, pages 13-17. *

 *public class Test {
 *
 *    public static void main(String[] args) {
 *        int[][] i = {{0,5,4,3},{5,-2,-3,-1},{11,-4,-1,-2},{8,-3,-4,-2}};
 *        LPDictionary d = new LPDictionary(i);
 *        System.out.println(d);
 *        while(!d.isOptimal()){
 *            d.pivot();
 *            System.out.println(d);
 *        }
 *    }
 *}
 *  
* @see dSoegOpt.PivotPair * @author Peter Bro Miltersen, February 2002. Kristoffer Hansen, March 2002. Troels Bjerre Sørensen, Feb 16. 2004 */ public class LPDictionary> { private T zero; private T one; /** is used to indicate the status of the dictionary */ public enum Status { INFEASIBLE, OPTIMAL, UNBOUNDED, FEASIBLE } /** is the number of non-basic variables in the dictionary. */ public int n; /** is the number of equations (and basic variables) in the * dictionary. */ public int m; /* basic_vars[1..m] holds the indices of the basic variables. * basic_vars[j] holds the index of the variable on the left hand * side of the j'th row of the tableau. */ private int[] basic_vars; /* non_basic_vars[1..n] holds the indices of the non-basic variables. * basic_vars[j] holds the index of the variable on the left hand * side of the j'th row of the tableau. */ private int[] non_basic_vars; /* tableau[0..m][0..n] holds the coefficients of the dictionary: * * tableau[i][*] holds the i'th row of the tableau. The objective * function is given as the 0'th row, while the i'th row, (i>0), * expresses the variable with index basic_vars[i] as a linear * combination of the non-basic variables. The entry tableau[i][0] * holds the constant coefficient of the i'th row. The coefficent of * the variable with index non_basic_vars[j] is given in tableau[i][j].*/ private T[][] tableau; /* If unbounded_var is not zero, the objective function can be made * arbitrarily large by increasing the variable with index * basic_vars[unbounded_var] */ private int unbounded_var = 0; /** constructs a dictionary whose tableau takes values from t. * The resulting dictionary has m = t.length - 1 * basic variables named xn+1,.., * xn+m,and n = t[0].length - 1 * non-basic variables, named x1,..,xn. *

t[i][*] should hold the i'th row of the tableau. * The objective * function should be given as the zeroth row, while the i'th row, for * i bigger than zero, * should express xn+i as a linear * combination of the non-basic variables. The entry t[i][0] * should hold the constant coefficient of the i'th row. * The coefficent of xj should be given in * t[i][j].*/ public LPDictionary(T[][] t){ //System.err.println("\rLPDictionary: " + t.length + " x " + t[0].length); m = t.length - 1; n = t[0].length - 1; zero = t[0][0].zero(); one = t[0][0].one(); non_basic_vars = new int[n + 1]; for (int i = 1 ; i <= n ; i++) non_basic_vars[i] = i; basic_vars = new int[m + 1]; for (int i = 1 ; i <= m ; i++) basic_vars[i] = i + n; tableau = t; } /** converts the int array t to a * {@link Rational Rational} array and then behaves as * {@link #LPDictionary(Real[][]) LPDictionary(Real[][])} */ public static LPDictionary make(int[][] t){ Rational[][] templeau = new Rational[t.length][t[0].length]; for(int i = 0 ; i < t.length ; i++) for(int j = 0 ; j < t[i].length ; j++) templeau[i][j] = new Rational(t[i][j]); return new LPDictionary(templeau); } /** converts the double array t to a * {@link DoubleReal DoubleReal} array and then behaves as * {@link #LPDictionary(Real[][]) LPDictionary(Real[][])} */ public static LPDictionary make(double[][] t){ DoubleReal[][] templeau = new DoubleReal[t.length][t[0].length]; for(int i = 0 ; i <= t.length ; i++) for(int j = 0 ; j <= t[i].length ; j++) templeau[i][j] = new DoubleReal(t[i][j]); return new LPDictionary(templeau); } private String rowToString(T row[], String vname){ String s = ""; T val; boolean notfirst = false; if(!(row[0].isZero())) { s = row[0].toString(); notfirst = true; } for (int j = 1 ; j <= n ; j++) { if (!row[j].isZero()) { if (notfirst){// we already had a non-zero term if (row[j].isPositive()){s = s+ " + "; val = row[j];} else {s = s + " - "; val = row[j].negate();} if (val.isOne())s = s + vname + non_basic_vars[j]; else s = s + val.toString() + " " + vname + non_basic_vars[j]; } else { // the j'th term is the first non-zero one if (row[j].isPositive()){val = row[j];} else {s="-"; val = row[j].negate();} if (val.isOne())s = s + vname + non_basic_vars[j]; else s = s + val.toString() + " " + vname + non_basic_vars[j]; notfirst = true; } } } if (s.equals("")) s = "0"; return s; } private String rowToLatex(T row[], String vname){ String s = ""; T val; boolean notfirst = false; if (!(row[0].isZero())) {s=row[0].toLaTeX(); notfirst = true;} for (int j=1; j<=n; j++) { if (row[j].isZero()) {//The term is zero, so we write nothing. s+="& &"; } else if (notfirst) {// we already had a non-zero term if (row[j].isPositive()){s = s+ "& + & "; val = row[j];} else {s = s + "& - & "; val = row[j].negate();} if(val.isOne())s = s + vname + non_basic_vars[j]; else s = s + val.toLaTeX() + vname + non_basic_vars[j]; } else { // the j'th term is the first non-zero one s+="& &"; if (row[j].isPositive()){val = row[j];} else {s="-"; val = row[j].negate();} if(val.isOne())s = s + vname + non_basic_vars[j]; else s = s + val.toLaTeX() + vname + non_basic_vars[j]; notfirst = true;} } if(s.equals("")){s = "0";} return s; } /** returns a String representation of the dictionary, closely * following the conventions of Chvátal. */ public String toString(){ String term; String s = ""; for(int i = 1 ; i <= m ; i++) s = s + "x" + basic_vars[i] + " = " + rowToString(tableau[i], "x") + "\n"; s = s + "-----------------------------------\n"; s = s + "z = " + rowToString(tableau[0],"x") + "\n"; return s; } /** returns a LaTeX representation of the dictionary, closely * following the conventions of Chvátal. */ public String toLaTeX(){ String s="\\[\n\\begin{array}{"; for (int j=0; j<(n+1)*2; j++) s+="r@{\\hspace{5pt}}"; s+="r}"; for (int i=1; i<=m; i++) { s+="\nx_"+basic_vars[i]+" & = & " + rowToLatex(tableau[i],"x_"); if (i==m) s+="\\smallskip"; s+=" \\\\"; } s+="\\hline"; s = s + "\n\\rule{0pt}{10pt} z & = & " + rowToLatex(tableau[0],"x_")+" \\\\"; s = s + "\n\\end{array}\n\\]"; return s; } /** prints the dictionary in a new file in the MPS format. * @author Troels Bjerre Sørensen */ public void toMPS(String name) { try { File mpsfile = new File(name + ".mps"); PrintStream out = new PrintStream(new FileOutputStream(mpsfile)); out.println("NAME " + name); out.println("ROWS"); out.println(" N COST"); for(int y = 1 ; y <= m ; y++) out.println(" L Y" + y); out.println("COLUMNS"); for(int x = 1 ; x <= n ; x++) { String varname = "X" + x; for(int y = 0 ; y <= m ; y++) if(!tableau[y][x].isZero()) { String eqname = y == 0 ? "COST" : "Y" + y; String value = asciify(-tableau[y][x].doubleValue()); out.println(" " + varname + blanks(10 - varname.length()) + eqname + blanks(22 - eqname.length() - value.length()) + value); } } out.println("RHS"); for(int y = 1 ; y <= m ; y++) { String eqname = "Y" + y; String value = asciify(tableau[y][0].doubleValue()); out.println(" RHS " + eqname + blanks(22 - eqname.length() - value.length()) + value); } out.println("BOUNDS"); out.println("ENDATA"); out.close(); } catch (FileNotFoundException e) { System.err.println("SURT: "+e); System.exit(1); } } private String blanks(int k) { char[] result = new char[k]; Arrays.fill(result, ' '); return new String(result); } private String asciify(double val) { String result = ""+val; if(result.length() <= 12) return result; int index = result.indexOf("E"); if(index == -1) { return result.substring(0, 12); } else { String exppart = result.substring(index, result.length()); return result.substring(0, 12 - exppart.length()) + exppart; } } /** returns the index of the column of variable * xv if * xv is a non-basic * variable. If not, 0 is returned. */ public int columnOf(int v){ for(int i = 1 ; i <= n ; i++) if(non_basic_vars[i] == v) return i; return 0; } /** returns the index of the row of variable * xv * if xv is a basic * variable. If not, 0 is returned. */ public int rowOf(int v) { for(int i = 1 ; i <= m ; i++) if(basic_vars[i] == v) return i; return 0; } /** returns the index of the (non-basic) variable in column * number i. */ public int variableOfColumn(int i) { return non_basic_vars[i]; } /** returns the index of the basic variable of row number i. */ public int variableOfRow(int i) { return basic_vars[i]; } /** returns the value of the objective function on the solution * associated with the dictionary (even if the solution is not * feasible). */ public T getValue(){ return tableau[0][0]; } /** returns the solution associated with the dictionary as a Real * array of size m+n+1. The value of variable * xi is in entry i of the array. */ public T[] getSolution() { T[] sol = new ArrayList(Collections.nCopies(m + n + 1, zero)).toArray(tableau[0]); // won't fit, so new array with right type will be made... for(int row = 1 ; row <= m ; row++) sol[variableOfRow(row)] = tableau[row][0]; return sol; } /** returns a Real array of size m+n+1 which, if * this dictionary is optimal, contains the optimal dual * solution. The value of variable yj is in entry * j of the array. Here, yj for j * in 1,..,n are the dual slack variables corresponding to the * non-slack variables of the original dictionary (i.e., * x1,..,xn), while * the variables yn+1, * ..yn+m are the non-slack dual * variables. */ public T[] getDualSolution(){ T[] sol = new ArrayList(Collections.nCopies(m + n + 1, zero)).toArray(tableau[0]); // won't fit, so new array with right type will be made... for(int col = 1; col <= n ; col++) sol[variableOfColumn(col)] = tableau[0][col].negate(); return sol; } /** return true iff the dictionary is optimal **/ public boolean isOptimal(){ for(int j=1; j<=n; j++) if((tableau[0][j]).isPositive()){return false;} return true; } /** returns true iff the dictionary is feasible **/ public boolean isFeasible(){ for(int i=1; i<=m; i++) if((tableau[i][0]).isNegative()){return false;} return true; } int pivotnumber = 0; /** performs a pivot on the dictionary with p.in entering the * basis and p.out leaving it. **/ public void pivot(PivotPair p){ unbounded_var = 0; int in = p.in; int out = p.out; if (in != 0 && out != 0){ int name_in = non_basic_vars[in]; int name_out = basic_vars[out]; T cof = tableau[out][in]; T minuscof = cof.negate(); T invcof = cof.invert(); for (int j = 0 ; j <= n ; j++) { if (j == in) { tableau[out][j] = invcof; } else { tableau[out][j] = tableau[out][j].divide(minuscof); } } for (int i = 0 ; i <= m; i++) if (i != out) { cof = tableau[i][in]; tableau[i][in] = zero; for(int j = 0 ; j <= n ; j++) tableau[i][j] = tableau[i][j].add(tableau[out][j].multiply(cof)); } basic_vars[out] = name_in; non_basic_vars[in] = name_out; } //System.err.println((++pivotnumber) + ":\t" + p + "\t" + tableau[0][0].doubleValue()); //System.err.println(this); } /** performs the pivot on the dictionary suggested by Bland's rule */ public void pivot(){ pivot(bland()); } private T increaseOf(int in){ boolean found = false; int v = 0; T limit = zero; for(int i=1; i<=m; i++) if((tableau[i][in].compareTo(zero))<0){ if(!found){ found = true; v = i; limit = (tableau[i][0].divide(tableau[i][in])).negate(); } else{ T newlimit = (tableau[i][0].divide(tableau[i][in])).negate(); int c = newlimit.compareTo(limit); if((c<0)||((c==0)&&(basic_vars[i]0){ if(!found){ found = true; v = j; increase = increaseOf(v); if(increase==null){return v;} } else{ T newincrease = increaseOf(j); if(newincrease==null){return j;} if(newincrease.greater(increase)) {v=j; increase=newincrease;} } } } return v; } private int largestCoefficientIn(){ boolean found = false; int v=0; for(int j=1; j<=n; j++){ if((tableau[0][j]).compareTo(zero)>0){ if(!found){ found = true; v = j; } else if((tableau[0][j]).greater(tableau[0][v])){v=j;} } } return v; } /** returns the index of the column containing the non-basic variable * that Bland's rule suggests should enter the basis. */ private int blandIn(){ boolean found = false; int v=0; for(int j=1; j<=n; j++) { if((tableau[0][j]).compareTo(zero)>0){ if(!found){ found = true; v = j; } else{ if(non_basic_vars[j] < non_basic_vars[v]){v = j;} } } } return v; } /** if in is the index of a column of a variable that should * enter the * basis, the index of the row containing the basic variable with * the smallest index that * might leave the basic is returned. */ public int blandOut(int in){ boolean found = false; int v = 0; T limit = zero; for(int i=1; i<=m; i++) if((tableau[i][in].compareTo(zero))<0){ if(!found){ found = true; v = i; limit = (tableau[i][0].divide(tableau[i][in])).negate(); } else{ T newlimit = (tableau[i][0].divide(tableau[i][in])).negate(); int c = newlimit.compareTo(limit); if((c<0)||((c==0)&&(basic_vars[i]not performed on the dictionary. */ public PivotPair bland(){ int in = blandIn(); if(in == 0){return new PivotPair(0,0);} int out = blandOut(in); return new PivotPair(in, out); } /** returns the pivot that the largest coefficient rule suggests. * The pivot is not performed on the dictionary. */ public PivotPair largestCoefficient(){ int in = largestCoefficientIn(); if(in == 0){return new PivotPair(0,0);} int out = blandOut(in); return new PivotPair(in, out); } /** returns the pivot that the largest increase rule suggests. * The pivot is not performed on the dictionary. */ public PivotPair largestIncrease(){ int in = largestIncreaseIn(); if(in == 0){return new PivotPair(0,0);} int out = blandOut(in); return new PivotPair(in, out); } /** Performs the one-phase simplex algorithm on the dictionary. * If the dictionary is initially infeasible, * {@link #INFEASIBLE INFEASIBLE} is returned. * Otherwise, the basic (one-phase) simplex algorithm is performed * on the dictionary, using Bland's rule. If the algorithm terminates * with an optimal dictionary, {@link #OPTIMAL OPTIMAL} is returned. * If the final dictionary is unbounded, {@link #UNBOUNDED UNBOUNDED} is * returned. */ public Status onePhaseSimplex() { if(!isFeasible()) { return Status.INFEASIBLE; } while(true){ PivotPair b = bland(); if(b.in == 0){return Status.OPTIMAL;} else if(b.out == 0){unbounded_var = b.in; return Status.UNBOUNDED;} else{pivot(b);}} } public Status twoPhaseSimplex() { //System.out.println("twoPhaseSimplex()\n" + this); // Check whether we need twoPhase //System.err.println("\rfirst phase..."); int out = 0; for (int row = 1 ; row <= m ; row++) { if (out != 0) { if (tableau[row][0].less(tableau[out][0])) out = row; } else { if (tableau[row][0].less(zero)) out = row; } } if (out == 0) return onePhaseSimplex(); //System.out.println("We need two phase"); // Backup cost function T[] cost = tableau[0].clone(); // Add new variable for (int row = 0 ; row <= m ; row++) { ArrayList temp = new ArrayList(tableau[row].length); for (T elm : tableau[row]) temp.add(elm); temp.add(one); tableau[row] = temp.toArray(tableau[row]); } n++; int[] temp = new int[n + 1]; System.arraycopy(non_basic_vars, 0, temp, 0, non_basic_vars.length); temp[n] = n + m; // name of new var non_basic_vars = temp; Arrays.fill(tableau[0], zero); tableau[0][n] = one.negate(); //System.out.println("Before pivot:\n" + this); // Make pseudo pivot pivot(new PivotPair(n, out)); //System.out.println("After pivot:\n" + this); // Perform single phase with new variable if (onePhaseSimplex() != Status.OPTIMAL) throw new RuntimeException("LPDictionary: trold made some mistake... feed him"); //System.out.println("After one phase simplex:\n" + this); if (! getValue().isZero()) return Status.INFEASIBLE; // Remove new var from basis, if it is there //System.err.println("\rfirst phase complete"); for (int row = 1 ; row <= m ; row++) { //System.err.println("variableOfRow(" + row + ") = " + variableOfRow(row)); if (variableOfRow(row) == m + n) { // found the new var. Pivot it out. //System.out.println("Phase one var in basis. Get it out"); for (int col = 1 ; col <= n ; col++) { if (! tableau[row][col].isZero()) { //System.out.println("Found it. Pivoting " + col + " and " + row); pivot(new PivotPair(col, row)); break; } } break; } } /* System.out.println("tableau:"); for (T[] row : tableau) { for (T e : row) { System.out.print(e + "\t"); } System.out.println(); } System.out.println(); */ // Delete new variable boolean found = false; for (int col = 1 ; col < n ; col++) { if (variableOfColumn(col) == m + n) { found = true; for (int row = 1 ; row <= m ; row++) { tableau[row][col] = tableau[row][n]; tableau[row][n] = zero; } non_basic_vars[col] = non_basic_vars[n]; } } if (! found) throw new RuntimeException("LPDictionary: Did not find new variable... trold puched uf"); n--; //System.out.println("After delete of first phase var:\n" + this); // Restore old cost function Arrays.fill(tableau[0], zero); for (int row = 1 ; row <= m ; row++) { if (variableOfRow(row) <= n) { // variable used to be in cost function. Substitute for (int col = 0 ; col <= n ; col++) { tableau[0][col] = tableau[0][col] .add(tableau[row][col].multiply(cost[variableOfRow(row)])); } } } for (int col = 1 ; col <= n ; col++) { if (variableOfColumn(col) <= n) { // variable used to be in cost function. Copy from old tableau[0][col] = tableau[0][col] .add(cost[variableOfColumn(col)]); } } //System.out.println("After restoring cost function:\n" + this); //System.err.println("\rsecond phase..."); return onePhaseSimplex(); } private T[] primal_solution; private T[] dual_solution; private T objective_value; public double[] pcx_primal; public double[] pcx_dual; public double pcx_objective; /** Executes a professional quality external LP-solver (PCx) on the * LP instance of the dictionary. The dictionary itself is not * updated, so the methods {@link #getSolution() getSolution()}, {@link #getDualSolution() getDualSolution()} and * {@link #getValue() getValue()} extracting the optimal solutions and their * value from the dictionary do not in general give the * optimal value after applying {@link #PCxSolve() PCxSolve()}. * Instead, an optimal primal solution, an optimal dual solution and * the optimal value of the objective function is printed out by {@link #PCxSolve() PCxSolve()}. * If the dictionary is * initially infeasible, {@link #INFEASIBLE INFEASIBLE} is * returned. If the external solver terminates with an optimal * solution, {@link #OPTIMAL OPTIMAL} is returned. If the external solver * finds the instance to be unbounded, {@link #UNBOUNDED UNBOUNDED} is * returned.

* This method is only supported on Linux machines on DAIMI * @author Troels Bjerre Sørensen, Feb 16. 2004*/ public Status PCxSolve() { pcx_primal = new double[n + 1]; pcx_dual = new double[m + 1]; String name = "MPS" + System.currentTimeMillis(); toMPS(name); try { Runtime.getRuntime().exec("/users/trold/bin/i686/PCx.linux " + name + ".mps").waitFor(); } catch (InterruptedException e) { } catch (IOException e){ System.err.println("Could not execute external solver. Are you sure you are running Linux on DAIMI?"); System.exit(1); } try { File outfile = new File(name+".out"); File logfile = new File(name+".log"); String line = ""; BufferedReader varfile = new BufferedReader(new FileReader(outfile)); String firstLine = varfile.readLine(); if(firstLine.startsWith("INFEASIBLE")) { new File(name+".mps").delete(); logfile.delete(); outfile.delete(); return Status.INFEASIBLE; } varfile.readLine();varfile.readLine(); //skip header StringTokenizer st; for(int i = 1 ; i <= n ; i++) { line = varfile.readLine(); st = new StringTokenizer(line, " ", false); st.nextToken(); //skip line number st.nextToken(); //skip var name, since we know what it is... pcx_primal[i] = Double.parseDouble(st.nextToken()); } varfile.readLine();varfile.readLine();varfile.readLine(); //skip header for(int i = 1 ; i <= m ; i++) { line = varfile.readLine(); st = new StringTokenizer(line, " ", false); st.nextToken();st.nextToken();st.nextToken();st.nextToken();st.nextToken(); pcx_dual[i] = - Double.parseDouble(st.nextToken()); } varfile = new BufferedReader(new FileReader(logfile)); while(!line.startsWith("Primal Objective = ")) line = varfile.readLine(); pcx_objective = - Double.parseDouble(line.substring(19)); new File(name+".mps").delete(); logfile.delete(); outfile.delete(); } catch (IOException e) { if(e.toString().startsWith("java.io.FileNotFoundException")) { new File(name+".mps").delete(); return Status.UNBOUNDED; } System.err.println("Something went wrong: " + e); System.exit(1); } return Status.OPTIMAL; } public T[] approxsolution; private void fixSolution() { for (int row = 1 ; row <= m ; row++) { if (approxsolution[variableOfRow(row)].isNegative()) { for (int col = 1 ; col <= n ; col++) { if (tableau[row][col].isPositive()) { T increase = approxsolution[variableOfRow(row)] .divide(tableau[row][col]) .negate(); approxsolution[variableOfColumn(col)] = approxsolution[variableOfColumn(col)].add(increase); for (int trow = 1 ; trow <= m ; trow++) { approxsolution[variableOfRow(trow)] = approxsolution[variableOfRow(trow)] .add(tableau[trow][col].multiply(increase)); } assert approxsolution[variableOfRow(row)].isZero() : "col " + col + ", X" + variableOfRow(row) + " == " + approxsolution[variableOfRow(row)]; break; } } } } for (int row = m ; row >= 1 ; row--) { if (approxsolution[variableOfRow(row)].isNegative()) { for (int col = 1 ; col <= n ; col++) { if (tableau[row][col].isPositive()) { T increase = approxsolution[variableOfRow(row)] .divide(tableau[row][col]) .negate(); approxsolution[variableOfColumn(col)] = approxsolution[variableOfColumn(col)].add(increase); for (int trow = 1 ; trow <= m ; trow++) { approxsolution[variableOfRow(trow)] = approxsolution[variableOfRow(trow)] .add(tableau[trow][col].multiply(increase)); } assert approxsolution[variableOfRow(row)].isZero() : "col " + col + ", X" + variableOfRow(row) + " == " + approxsolution[variableOfRow(row)]; break; } } } } } private boolean verifyApproxSolution() { for (int row = 1 ; row <= m ; row++) { T val = tableau[row][0]; for (int col = 1 ; col <= n ; col++) { val = val.add(tableau[row][col].multiply(approxsolution[variableOfColumn(col)])); } if (! approxsolution[variableOfRow(row)].equals(val)) return false; } return true; } public Status pivotToSolution(T[] sol) { approxsolution = getSolution(); for (int col = 1 ; col <= n ; col++) { approxsolution[variableOfColumn(col)] = sol[variableOfColumn(col)]; } for (int row = 1 ; row <= m ; row++) { T val = tableau[row][0]; for (int col = 1 ; col <= n ; col++) { val = val.add(tableau[row][col].multiply(approxsolution[variableOfColumn(col)])); } //if (val.isNegative()) return Status.INFEASIBLE; approxsolution[variableOfRow(row)] = val; } fixSolution(); for (int row = 1 ; row <= m ; row++) { if (approxsolution[variableOfRow(row)].isNegative()) { return Status.INFEASIBLE; } } //assert verifyApproxSolution(); for (int col = 1 ; col <= n ; col++) { //assert ! approxsolution[variableOfColumn(col)].isNegative() : approxsolution[variableOfColumn(col)].doubleValue(); if (approxsolution[variableOfColumn(col)].isPositive()) { //System.err.print("\rcol: " + col + ", "); if (tableau[0][col].isPositive()) { // This variable should increase T limitinc = null; int limitrow = 0; for (int row = 1 ; row <= m ; row++) { if (tableau[row][col].isNegative()) { T newlimit = approxsolution[variableOfRow(row)] .divide(tableau[row][col]).negate(); if (limitinc == null || newlimit.less(limitinc)) { limitinc = newlimit; limitrow = row; } } } if (limitinc == null) return Status.UNBOUNDED; //assert ! limitinc.isNegative(); if (limitinc.isPositive()) { approxsolution[variableOfColumn(col)] = approxsolution[variableOfColumn(col)].add(limitinc); for (int row = 1 ; row <= m ; row++) { approxsolution[variableOfRow(row)] = approxsolution[variableOfRow(row)] .add(limitinc.multiply(tableau[row][col])); } } if (approxsolution[variableOfColumn(col)].isPositive()) { //assert approxsolution[variableOfRow(limitrow)].isZero(); pivot(new PivotPair(col, limitrow)); } else { //assert approxsolution[variableOfColumn(col)].isZero(); } } else { // This variable should decrease T limitdec = null; int limitrow = 0; for (int row = 1 ; row <= m ; row++) { if (tableau[row][col].isPositive()) { T newlimit = approxsolution[variableOfRow(row)] .divide(tableau[row][col]); if (limitdec == null || newlimit.less(limitdec)) { limitdec = newlimit; limitrow = row; } } } if (limitdec == null || approxsolution[variableOfColumn(col)].less(limitdec)) { limitdec = approxsolution[variableOfColumn(col)]; } //assert ! limitdec.isNegative(); if (limitdec.isPositive()) { approxsolution[variableOfColumn(col)] = approxsolution[variableOfColumn(col)].subtract(limitdec); for (int row = 1 ; row <= m ; row++) { approxsolution[variableOfRow(row)] = approxsolution[variableOfRow(row)] .subtract(limitdec.multiply(tableau[row][col])); } } if (approxsolution[variableOfColumn(col)].isPositive()) { //assert approxsolution[variableOfRow(limitrow)].isZero(); pivot(new PivotPair(col, limitrow)); } else { //assert approxsolution[variableOfColumn(col)].isZero() : approxsolution[variableOfColumn(col)].doubleValue(); } } } } /*assert verifyApproxSolution(); for (int col = 1 ; col <= n ; col++) assert approxsolution[variableOfColumn(col)].isZero() : "as[" + variableOfColumn(col) + "] = " + approxsolution[variableOfColumn(col)]; for (int row = 1 ; row <= m ; row++) assert ! approxsolution[variableOfRow(row)].isNegative(); */ return Status.FEASIBLE; } public Status pivotToPCxSolution() { PCxSolve(); approxsolution = getSolution(); Arrays.fill(approxsolution, zero); for (int col = 1 ; col <= n ; col++) { approxsolution[variableOfColumn(col)] = one.fromDouble(pcx_primal[variableOfColumn(col)]); } return pivotToSolution(approxsolution); } /** returns a matrix containing the tableau of the dictionary. * The format follows the same conventions as in * {@link #LPDictionary(Real[][]) #LPDictionary(Real[][])}. * Changing the matrix will also cause this dictionary to * change accordingly. */ public T[][] getTableau(){ return tableau; } /** returns a table of the basic variables of the tableau. The * i'th entry of the table, i between 1 and * m, is the index of the basic variable of the i'th * row of the tableau. Changing the table will also cause this * dictionary to change accordingly. */ public int[] getBasicVariables(){ return basic_vars; } /** returns a table of the non-basic variables of the tableau. * The j'th entry of the table, j between 1 and * n, is the index of the non-basic variable of the j'th * row of the tableau. Changing the table will also cause this * dictionary to change accordingly. */ public int[] getNonBasicVariables(){ return non_basic_vars; } }