001/* 002 * $Id: IntegerProgram.java 5734 2017-02-18 20:30:54Z kredel $ 003 */ 004 005package edu.jas.application; 006 007 008import java.io.IOException; 009import java.io.Reader; 010import java.io.StringReader; 011import java.util.Arrays; 012 013import org.apache.log4j.Logger; 014 015import edu.jas.arith.BigInteger; 016import edu.jas.poly.ExpVector; 017import edu.jas.poly.ExpVectorLong; 018import edu.jas.poly.GenPolynomial; 019import edu.jas.poly.GenPolynomialTokenizer; 020import edu.jas.poly.PolynomialList; 021import edu.jas.poly.TermOrder; 022 023 024/** 025 * Solution of Integer Programming problems using Groebner bases. Integer 026 * Program is in standard form -> minimization of given Equation plus 027 * restrictions. See chapter 8 in Cox, Little, O'Shea "Using Algebraic 028 * Geometry", 1998. 029 * @author Maximilian Nohr 030 */ 031public class IntegerProgram implements java.io.Serializable { 032 033 034 private static final Logger logger = Logger.getLogger(IntegerProgram.class); 035 036 037 private static boolean DEBUG = logger.isDebugEnabled(); //false; 038 039 040 private boolean negVars; 041 042 043 private boolean success; 044 045 046 /* 047 Integer Program is in standard form -> minimization of given 048 Equation + restrictions 049 */ 050 int n; // # of variables including slack variables 051 052 053 int m; // # of restrictions 054 055 056 long[] C; // List of Coefficients c_1...c_n of objective function 057 058 059 long[] B; // List of b_1...b_m, restriction right hand side 060 061 062 long[][] A; // m x n Matrix of a_{11}....a_{mn}, restriction matrix 063 064 065 long[] D; // Polynomial degrees 1...n 066 067 068 long[][] Aa; // restriction matrix a_{11}..a_{mn} after Laurent transformation 069 070 071 Ideal<BigInteger> I; //the Ideal 072 073 074 Ideal<BigInteger> GB; // the Groebner base for the ideal 075 076 077 TermOrder to; // the Term order for the GB 078 079 080 PolynomialList<BigInteger> F; // The Polynomials that generate the Ideal 081 082 083 GenPolynomial<BigInteger> S; // The Polynomial that is reduced for the Solution 084 085 086 /** 087 * Constructor. Use one instance for every new problem since solve() is not 088 * reentrant. 089 */ 090 public IntegerProgram() { 091 } 092 093 094 /** 095 * Set DEBUG flag to parameter value. 096 * @param b 097 */ 098 public void setDebug(boolean b) { 099 DEBUG = b; 100 } 101 102 103 /* 104 * Setup the Ideal corresponding to the Integer Program. 105 */ 106 @SuppressWarnings("cast") 107 private void createIdeal() { 108 Aa = A.clone(); 109 negVars = negVarTest(); 110 String[] w = new String[n]; 111 String[] f = new String[n]; 112 String[] z = new String[m]; 113 String[] t = new String[n]; 114 StringBuilder sb = new StringBuilder(); 115 sb.append("Int("); 116 117 if (negVars) { //A or B has negative values 118 for (int i = 1; i <= n; i++) { 119 w[i - 1] = "w" + i; 120 } 121 for (int i = 1; i <= m; i++) { 122 z[i - 1] = "z" + i; 123 } 124 for (int i = 0; i < n; i++) { 125 StringBuffer h = new StringBuffer(""); 126 long min = 0; 127 for (int j = 0; j < m; j++) { 128 if (A[j][i] < min) { 129 min = A[j][i]; 130 } 131 } 132 if (min < 0) { 133 long e = -min; 134 h.append("t^" + e + " * "); 135 for (int j = 0; j < m; j++) { 136 Aa[j][i] = A[j][i] + e; 137 h.append(z[j] + "^" + Aa[j][i] + " * "); 138 } 139 } else { 140 for (int j = 0; j < m; j++) { 141 if (A[j][i] != 0) { 142 h.append(z[j] + "^" + A[j][i] + " * "); 143 } 144 } 145 } 146 f[i] = h.substring(0, h.length() - 3).toString(); 147 } 148 setDeg(); 149 setTO(); 150 for (int i = 0; i < n; i++) { 151 t[i] = f[i] + " - " + w[i]; 152 } 153 sb.append("t"); 154 for (int i = 0; i < m; i++) { 155 sb.append(",").append(z[i]); 156 } 157 for (int i = 0; i < n; i++) { 158 sb.append(",").append(w[i]); 159 } 160 sb.append(") W "); 161 //sb.append(to.weightToString().substring(6, to.weightToString().length())); 162 sb.append(to.weightToString()); 163 sb.append(" ( ( t"); 164 for (int i = 0; i < m; i++) { 165 sb.append(" * ").append(z[i]); 166 } 167 sb.append(" - 1 )"); 168 for (int i = 0; i < n; i++) { 169 sb.append(", (").append(t[i]).append(" )"); 170 } 171 sb.append(") "); 172 173 } else { //if neither A nor B contain negative values 174 for (int i = 1; i <= n; i++) { 175 w[i - 1] = "w" + i; 176 } 177 for (int i = 1; i <= m; i++) { 178 z[i - 1] = "z" + i; 179 } 180 for (int i = 0; i < n; i++) { 181 StringBuffer h = new StringBuffer(""); 182 for (int j = 0; j < m; j++) { 183 if (A[j][i] != 0) { 184 h.append(z[j] + "^" + A[j][i] + " * "); 185 } 186 } 187 f[i] = h.substring(0, h.length() - 3).toString(); 188 } 189 setDeg(); 190 setTO(); 191 for (int i = 0; i < n; i++) { 192 t[i] = f[i] + " - " + w[i]; 193 } 194 sb.append(z[0]); 195 for (int i = 1; i < m; i++) { 196 sb.append(",").append(z[i]); 197 } 198 for (int i = 0; i < n; i++) { 199 sb.append(",").append(w[i]); 200 } 201 sb.append(") W "); 202 //sb.append(to.weightToString().substring(6, to.weightToString().length())); 203 sb.append(to.weightToString()); 204 sb.append(" ( (").append(t[0]).append(")"); 205 for (int i = 1; i < n; i++) { 206 sb.append(", (").append(t[i]).append(" )"); 207 } 208 sb.append(") "); 209 } 210 if (DEBUG) { 211 logger.debug("list=" + sb.toString()); 212 } 213 214 Reader source = new StringReader(sb.toString()); 215 GenPolynomialTokenizer parser = new GenPolynomialTokenizer(source); 216 PolynomialList<BigInteger> F = null; 217 218 try { 219 F = (PolynomialList<BigInteger>) parser.nextPolynomialSet(); 220 } catch (ClassCastException e) { 221 e.printStackTrace(); 222 return; 223 } catch (IOException e) { 224 e.printStackTrace(); 225 return; 226 } 227 if (DEBUG) { 228 logger.debug("F=" + F); 229 } 230 I = new Ideal<BigInteger>(F); 231 return; 232 } 233 234 235 /** 236 * @return true if the last calculation had a solution, else false 237 */ 238 public boolean getSuccess() { 239 return success; 240 } 241 242 243 /** 244 * Solve Integer Program. 245 * @param A matrix of restrictions 246 * @param B restrictions right hand side 247 * @param C objective function 248 * @return solution s, such that s*C -> minimal and A*s = B, or s = null 249 * if no solution exists 250 */ 251 public long[] solve(long[][] A, long[] B, long[] C) { 252 this.A = Arrays.copyOf(A, A.length); 253 this.B = Arrays.copyOf(B, B.length); 254 this.C = Arrays.copyOf(C, C.length); 255 this.n = A[0].length; 256 this.m = A.length; 257 D = new long[n]; 258 259 createIdeal(); 260 GB = I.GB(); 261 return solve(B); 262 } 263 264 265 /** 266 * Solve Integer Program for new right hand side. Uses the GB (matrix A and 267 * C) from the last calculation. 268 * @param B restrictions right hand side 269 * @return solution s, such that s*C -> minimal and A*s = B, or s = null 270 * if no solution exists 271 */ 272 public long[] solve(long[] B) { 273 long[] returnMe = new long[n]; 274 if (B.length != m) { 275 System.out.println("ERROR: Dimensions don't match: " + B.length + " != " + m); 276 return returnMe; 277 } 278 long[] l; 279 this.B = Arrays.copyOf(B, B.length); 280 if (DEBUG) { 281 logger.debug("GB=" + GB); 282 } 283 if (negVars) { 284 l = new long[m + n + 1]; 285 long min = findMin(B); 286 if (min < 0) { 287 long r = -min; 288 l[m + n] = r; 289 for (int i = 0; i < m; i++) { 290 l[m + n - 1 - i] = B[i] + r; 291 } 292 } else { 293 for (int i = 0; i < m; i++) { 294 l[m + n - 1 - i] = B[i]; 295 } 296 } 297 } else { 298 l = new long[m + n]; 299 for (int i = 0; i < m; i++) { 300 l[m + n - 1 - i] = B[i]; 301 } 302 } 303 ExpVector e = new ExpVectorLong(l); 304 S = new GenPolynomial<BigInteger>(I.getRing(), e); 305 S = GB.normalform(S); 306 307 ExpVector E = S.exponentIterator().next(); 308 for (int i = 0; i < n; i++) { 309 returnMe[n - 1 - i] = E.getVal(i); 310 } 311 success = true; 312 for (int i = n; i < n + m; i++) { 313 if (E.getVal(i) != 0) { 314 success = false; 315 break; 316 } 317 } 318 if (success) { 319 if (DEBUG) { 320 logger.debug("The solution is: " + Arrays.toString(returnMe)); 321 } 322 } else { 323 logger.warn("The Problem does not have a feasible solution."); 324 returnMe = null; 325 } 326 return returnMe; 327 } 328 329 330 /* 331 * Set the degree. 332 */ 333 private void setDeg() { 334 for (int j = 0; j < n; j++) { 335 for (int i = 0; i < m; i++) { 336 D[j] += Aa[i][j]; 337 } 338 } 339 } 340 341 342 /* 343 * Set the term order. 344 */ 345 private void setTO() { 346 int h; 347 if (negVars) {//if A and/or B contains negative values 348 h = m + n + 1; 349 } else { 350 h = m + n; 351 } 352 long[] u1 = new long[h]; 353 long[] u2 = new long[h]; 354 for (int i = 0; i < h - n; i++) { //m+1 because t needs another 1 355 u1[h - 1 - i] = 1; 356 } 357 long[] h1 = new long[h]; // help vectors to construct u2 out of 358 long[] h2 = new long[h]; 359 for (int i = h - n; i < h; i++) { 360 h1[i] = C[i - (h - n)]; 361 h2[i] = D[i - (h - n)]; 362 } 363 long min = h1[0]; 364 for (int i = 0; i < h; i++) { 365 u2[h - 1 - i] = h1[i] + h2[i]; 366 if (u2[h - 1 - i] < min) { 367 min = u2[h - 1 - i]; 368 } 369 } 370 while (min < 0) { 371 min = u2[0]; 372 for (int i = 0; i < h; i++) { 373 u2[h - 1 - i] += h2[i]; 374 if (u2[h - 1 - i] < min) { 375 min = u2[h - 1 - i]; 376 } 377 } 378 } 379 long[][] wv = { u1, u2 }; 380 to = new TermOrder(wv); 381 } 382 383 384 /* 385 * @see java.lang.Object#toString() 386 */ 387 @Override 388 public String toString() { 389 StringBuilder sb = new StringBuilder(); 390 sb.append("Function to minimize:\n"); 391 392 char c = 'A'; // variables are named A, B, C, ... 393 394 boolean plus = false; 395 for (int i = 0; i < n; i++) { 396 if (C[i] != 0) { 397 if (C[i] < 0) { 398 sb.append("(").append(C[i]).append(")"); 399 sb.append("*"); 400 } else if (C[i] != 1) { 401 sb.append(C[i]); 402 sb.append("*"); 403 } 404 sb.append(c); 405 sb.append(" + "); 406 plus = true; 407 } 408 c++; 409 } 410 if (plus) { 411 sb.delete(sb.lastIndexOf("+"), sb.length()); 412 } 413 sb.append("\nunder the Restrictions:\n"); 414 for (int i = 0; i < m; i++) { 415 c = 'A'; 416 //System.out.println("A["+i+"] = " + Arrays.toString(A[i])); 417 plus = false; 418 for (int j = 0; j < n; j++) { 419 if (A[i][j] != 0) { 420 if (A[i][j] < 0) { 421 sb.append("(").append(A[i][j]).append(")"); 422 sb.append("*"); 423 } else if (A[i][j] != 1) { 424 sb.append(A[i][j]); 425 sb.append("*"); 426 } 427 sb.append(c); 428 sb.append(" + "); 429 plus = true; 430 } 431 c++; 432 } 433 if (plus) { 434 sb.delete(sb.lastIndexOf("+"), sb.length()); 435 } else { 436 sb.append("0 "); 437 } 438 sb.append("= ").append(B[i]).append("\n"); 439 } 440 return sb.toString(); 441 } 442 443 444 /* 445 * Test for negative variables. 446 * @return true if negative variables appear 447 */ 448 private boolean negVarTest() { 449 for (int i = 0; i < m; i++) { 450 if (B[i] < 0) { 451 return true; 452 } 453 for (int j = 0; j < n; j++) { 454 if (A[i][j] < 0) { 455 return true; 456 } 457 458 } 459 } 460 return false; 461 } 462 463 464 /* 465 * Find minimal element. 466 * @param B vector of at least one element, B.length >= 1 467 * @return minimal element of B 468 */ 469 private long findMin(long[] B) { 470 long min = B[0]; 471 for (int i = 1; i < B.length; i++) { 472 if (B[i] < min) { 473 min = B[i]; 474 } 475 } 476 return min; 477 } 478 479}