# HG changeset patch
# User Nathann Cohen <nathann.cohen@gmail.com>
# Date 1291485477 -3600
# Node ID cd198aaa8581071c9dea2518f7ff21d290c5f8f8
# Parent 214981100765f40322a4c90db831d069bbec0689
trac 10341 -- Fixing MixedIntegerLinearProgram.show() and how the variables' names are managed
diff -r 214981100765 -r cd198aaa8581 sage/numerical/backends/cplex_backend.pyx
a
|
b
|
|
695 | 695 | status = CPXgetlb(self.env, self.lp, &lb, index, index) |
696 | 696 | check(status) |
697 | 697 | |
698 | | return (lb if lb != -CPX_INFBOUND else None, |
699 | | ub if ub != +CPX_INFBOUND else None) |
| 698 | return (None if lb <= -int(CPX_INFBOUND) else lb, |
| 699 | None if ub >= +int(CPX_INFBOUND) else ub) |
700 | 700 | |
701 | 701 | cpdef add_col(self, list indices, list coeffs): |
702 | 702 | r""" |
… |
… |
|
1138 | 1138 | status = CPXgetub(self.env, self.lp, &ub, index, index) |
1139 | 1139 | check(status) |
1140 | 1140 | |
1141 | | return ub if ub != CPX_INFBOUND else None |
| 1141 | return ub if ub < int(CPX_INFBOUND) else None |
1142 | 1142 | |
1143 | 1143 | else: |
1144 | 1144 | |
… |
… |
|
1179 | 1179 | if value == False: |
1180 | 1180 | status = CPXgetlb(self.env, self.lp, &lb, index, index) |
1181 | 1181 | check(status) |
1182 | | return lb if lb != -CPX_INFBOUND else None |
| 1182 | return None if lb <= int(-CPX_INFBOUND) else lb |
1183 | 1183 | |
1184 | 1184 | else: |
1185 | 1185 | x = 'L' |
diff -r 214981100765 -r cd198aaa8581 sage/numerical/mip.pxd
a
|
b
|
|
25 | 25 | cdef dict _dict |
26 | 26 | cdef int _vtype |
27 | 27 | cdef char * _name |
| 28 | cdef bint _hasname |
diff -r 214981100765 -r cd198aaa8581 sage/numerical/mip.pyx
a
|
b
|
|
68 | 68 | 0.0 <= x_0 +x_1 +x_2 -14.0 x_3 <= 0.0 |
69 | 69 | 0.0 <= x_1 +2.0 x_2 -8.0 x_3 <= 0.0 |
70 | 70 | 0.0 <= 2.0 x_2 -3.0 x_3 <= 0.0 |
71 | | -1.0 x_0 +x_1 +x_2 <= 0.0 |
72 | | -1.0 x_3 <= -1.0 |
| 71 | -x_0 +x_1 +x_2 <= 0.0 |
| 72 | -x_3 <= -1.0 |
73 | 73 | Variables: |
74 | 74 | x_0 is an integer variable (min=0.0, max=+oo) |
75 | 75 | x_1 is an integer variable (min=-oo, max=+oo) |
… |
… |
|
87 | 87 | |
88 | 88 | include "../ext/stdsage.pxi" |
89 | 89 | include "../ext/interrupt.pxi" |
| 90 | include "../ext/cdefs.pxi" |
90 | 91 | from copy import deepcopy |
91 | 92 | |
92 | 93 | cdef class MixedIntegerLinearProgram: |
… |
… |
|
274 | 275 | |
275 | 276 | self._backend.problem_name(name) |
276 | 277 | |
277 | | # def _update_variables_name(self): |
278 | | # r""" |
279 | | # Updates the names of the variables. |
280 | | |
281 | | # Only called before writing the Problem to a MPS or LP file. |
282 | | |
283 | | # EXAMPLE:: |
284 | | |
285 | | # sage: p=MixedIntegerLinearProgram() |
286 | | # sage: v=p.new_variable(name="Test") |
287 | | # sage: v[5]+v[99] |
288 | | # x_0 +x_1 |
289 | | # sage: p._update_variables_name() |
290 | | # """ |
291 | | |
292 | | # for v in self._mipvariables: |
293 | | # v._update_variables_name() |
294 | | |
295 | | |
296 | | def new_variable(self, real=False, binary=False, integer=False, dim=1,name=None): |
| 278 | def new_variable(self, real=False, binary=False, integer=False, dim=1,name=""): |
297 | 279 | r""" |
298 | 280 | Returns an instance of ``MIPVariable`` associated |
299 | 281 | to the current instance of ``MixedIntegerLinearProgram``. |
… |
… |
|
345 | 327 | ... |
346 | 328 | ValueError: Exactly one of the available types has to be True |
347 | 329 | """ |
348 | | if name==None: |
349 | | name="V"+str(len(self._mipvariables)) |
350 | | |
351 | 330 | if sum([real, binary, integer]) >= 2: |
352 | 331 | raise ValueError("Exactly one of the available types has to be True") |
353 | 332 | |
… |
… |
|
369 | 348 | |
370 | 349 | EXAMPLES: |
371 | 350 | |
372 | | When constraints have names :: |
| 351 | When constraints and variables have names :: |
| 352 | |
| 353 | sage: p = MixedIntegerLinearProgram() |
| 354 | sage: x = p.new_variable(name="Hey") |
| 355 | sage: p.set_objective(x[1] + x[2]) |
| 356 | sage: p.add_constraint(-3*x[1] + 2*x[2], max=2, name="Constraint_1") |
| 357 | sage: p.show() |
| 358 | Maximization: |
| 359 | Hey[1] +Hey[2] |
| 360 | Constraints: |
| 361 | Constraint_1: -3.0 Hey[1] +2.0 Hey[2] <= 2.0 |
| 362 | Variables: |
| 363 | Hey[1] is a continuous variable (min=0.0, max=+oo) |
| 364 | Hey[2] is a continuous variable (min=0.0, max=+oo) |
| 365 | |
| 366 | Without any names :: |
373 | 367 | |
374 | 368 | sage: p = MixedIntegerLinearProgram() |
375 | 369 | sage: x = p.new_variable() |
376 | 370 | sage: p.set_objective(x[1] + x[2]) |
377 | | sage: p.add_constraint(-3*x[1] + 2*x[2], max=2, name="Constraint_1") |
| 371 | sage: p.add_constraint(-3*x[1] + 2*x[2], max=2) |
378 | 372 | sage: p.show() |
379 | 373 | Maximization: |
380 | | x_0 +x_1 |
| 374 | x_0 +x_1 |
381 | 375 | Constraints: |
382 | | Constraint_1: -3.0 x_0 +2.0 x_1 <= 2.0 |
| 376 | -3.0 x_0 +2.0 x_1 <= 2.0 |
383 | 377 | Variables: |
384 | | x_0 is a real variable (min=0.0, max=+oo) |
385 | | x_1 is a real variable (min=0.0, max=+oo) |
| 378 | x_0 is a continuous variable (min=0.0, max=+oo) |
| 379 | x_1 is a continuous variable (min=0.0, max=+oo) |
386 | 380 | """ |
387 | 381 | |
388 | 382 | cdef int i, j |
389 | 383 | cdef double c |
390 | 384 | cdef GenericBackend b = self._backend |
391 | | #self._update_variables_name() |
392 | 385 | |
| 386 | # inv_variables associates a MIPVariable object to an id |
393 | 387 | inv_variables = [0]*len(self._variables) |
394 | | for (v,id) in self._variables.iteritems(): |
| 388 | for (v, id) in self._variables.iteritems(): |
395 | 389 | inv_variables[id]=v |
396 | 390 | |
397 | | value = ( "Maximization:\n" |
398 | | if b.is_maximization() |
399 | | else "Minimization:\n" ) |
400 | | value+=" " |
| 391 | |
| 392 | # varid_name associates variables id to names |
| 393 | varid_name = {} |
| 394 | |
| 395 | for 0<= i < b.ncols(): |
| 396 | s = b.col_name(i) |
| 397 | varid_name[i] = ("x_"+str(i)) if s == "" else s |
| 398 | |
| 399 | ##### Sense and objective function |
| 400 | |
| 401 | print ("Maximization:" if b.is_maximization() else "Minimization:") |
| 402 | print " ", |
401 | 403 | |
402 | 404 | first = True |
403 | 405 | for 0<= i< b.ncols(): |
404 | 406 | c = b.objective_coefficient(i) |
405 | | if c != 0: |
| 407 | if c == 0: |
| 408 | continue |
406 | 409 | |
407 | | value+= ((" +" if (not first and c>0) else " ") + |
408 | | str(inv_variables[i]*b.objective_coefficient(i)) |
409 | | ) |
410 | | first = False |
| 410 | print (("+" if (not first and c>0) else "") + |
| 411 | ("" if c == 1 else ("-" if c == -1 else str(c)+" "))+varid_name[i] |
| 412 | ), |
| 413 | first = False |
411 | 414 | |
412 | | value += "\nConstraints:" |
| 415 | print |
413 | 416 | |
| 417 | ##### Constraints |
| 418 | print "Constraints:" |
414 | 419 | |
415 | 420 | for 0<= i < b.nrows(): |
416 | 421 | |
… |
… |
|
418 | 423 | |
419 | 424 | lb, ub = b.row_bounds(i) |
420 | 425 | |
421 | | value += ("\n "+ |
422 | | (b.row_name(i)+": " if b.row_name(i)!="" else "")+ |
423 | | (str(lb)+" <= " if lb!=None else "") |
424 | | ) |
| 426 | print " ", |
| 427 | |
| 428 | |
| 429 | # Constraint's name |
| 430 | if b.row_name(i): |
| 431 | print b.row_name(i)+":", |
| 432 | |
| 433 | # Lower bound |
| 434 | if lb is not None: |
| 435 | print str(lb)+" <=", |
425 | 436 | |
426 | 437 | first = True |
427 | 438 | |
428 | | for j,v in sorted(zip(indices, values)): |
| 439 | for j,c in sorted(zip(indices, values)): |
429 | 440 | |
430 | | value += (("+" if (not first and v>=0) else "") + |
431 | | str(inv_variables[j]*v) +" ") |
| 441 | if c == 0: |
| 442 | continue |
432 | 443 | |
| 444 | print (("+" if (not first and c>0) else "") + |
| 445 | ("" if c == 1 else ("-" if c == -1 else str(c)+" "))+varid_name[j] |
| 446 | ), |
433 | 447 | first = False |
434 | 448 | |
435 | | value += ("<= "+str(ub) if ub!=None else "") |
| 449 | # Upper bound |
| 450 | print ("<= "+str(ub) if ub!=None else "") |
436 | 451 | |
437 | | value += "\nVariables:" |
| 452 | |
| 453 | ##### Variables |
| 454 | print "Variables:" |
438 | 455 | |
439 | 456 | for 0<= i < b.ncols(): |
440 | | value += "\n " + str(inv_variables[i]) + " is" |
| 457 | print " " + varid_name[i] + " is", |
441 | 458 | |
442 | 459 | if b.is_variable_integer(i): |
443 | | value += " an integer variable" |
| 460 | print "an integer variable", |
444 | 461 | elif b.is_variable_binary(i): |
445 | | value += " an boolean variable" |
| 462 | print "a boolean variable", |
446 | 463 | else: |
447 | | value += " a real variable" |
| 464 | print "a continuous variable", |
448 | 465 | |
449 | 466 | lb, ub = b.col_bounds(i) |
450 | 467 | |
451 | | value += " (min=" + \ |
452 | | ( str(lb) |
453 | | if lb != None |
454 | | else "-oo" ) + \ |
455 | | ", max=" + \ |
456 | | ( str(ub) |
457 | | if ub != None |
458 | | else "+oo" ) + \ |
459 | | ")" |
460 | | print value |
| 468 | print "(min=" + ( str(lb) if lb != None else "-oo" )+",", |
| 469 | print "max=" + ( str(ub) if ub != None else "+oo" )+")" |
| 470 | |
461 | 471 | |
462 | 472 | def write_mps(self,filename,modern=True): |
463 | 473 | r""" |
… |
… |
|
734 | 744 | Maximization: |
735 | 745 | <BLANKLINE> |
736 | 746 | Constraints: |
737 | | -2.0 x_0 -1.0 x_1 <= 9.0 |
| 747 | -2.0 x_0 -x_1 <= 9.0 |
738 | 748 | Variables: |
739 | | x_0 is a real variable (min=0.0, max=+oo) |
740 | | x_1 is a real variable (min=0.0, max=+oo) |
| 749 | x_0 is a continuous variable (min=0.0, max=+oo) |
| 750 | x_1 is a continuous variable (min=0.0, max=+oo) |
741 | 751 | |
742 | 752 | Empty constraint:: |
743 | 753 | |
… |
… |
|
1082 | 1092 | |
1083 | 1093 | return self._backend.get_objective_value() |
1084 | 1094 | |
1085 | | |
1086 | | def _add_element_to_ring(self, vtype): |
1087 | | r""" |
1088 | | Creates a new variable in the (Mixed) Integer Linear Program. |
1089 | | |
1090 | | INPUT: |
1091 | | |
1092 | | - ``vtype`` (integer) -- Defines the type of the variables |
1093 | | (default is ``REAL``). |
1094 | | |
1095 | | OUTPUT: |
1096 | | |
1097 | | - The newly created variable. |
1098 | | |
1099 | | EXAMPLE:: |
1100 | | |
1101 | | sage: p = MixedIntegerLinearProgram() |
1102 | | sage: v = p.new_variable() |
1103 | | sage: print p |
1104 | | Mixed Integer Program ( maximization, 0 variables, 0 constraints ) |
1105 | | sage: p._add_element_to_ring(-1) |
1106 | | x_0 |
1107 | | sage: print p |
1108 | | Mixed Integer Program ( maximization, 1 variables, 0 constraints ) |
1109 | | """ |
1110 | | |
1111 | | v = LinearFunction({len(self._variables) : 1}) |
1112 | | |
1113 | | self._variables[v] = len(self._variables) |
1114 | | self._backend.add_variable() |
1115 | | self._backend.set_variable_type(self._backend.ncols()-1,vtype) |
1116 | | return v |
1117 | | |
1118 | 1095 | def set_min(self, v, min): |
1119 | 1096 | r""" |
1120 | 1097 | Sets the minimum value of a variable. |
… |
… |
|
1136 | 1113 | sage: p.set_min(v[1],6) |
1137 | 1114 | sage: p.get_min(v[1]) |
1138 | 1115 | 6.0 |
| 1116 | sage: p.set_min(v[1], None) |
| 1117 | sage: p.get_min(v[1]) |
| 1118 | |
1139 | 1119 | """ |
1140 | 1120 | self._backend.variable_lower_bound(self._variables[v], min) |
1141 | 1121 | |
… |
… |
|
1186 | 1166 | sage: p.set_min(v[1],6) |
1187 | 1167 | sage: p.get_min(v[1]) |
1188 | 1168 | 6.0 |
| 1169 | sage: p.set_min(v[1], None) |
| 1170 | sage: p.get_min(v[1]) |
1189 | 1171 | """ |
1190 | 1172 | |
1191 | 1173 | return self._backend.variable_lower_bound(self._variables[v]) |
… |
… |
|
1287 | 1269 | ``MixedIntegerLinearProgram``. |
1288 | 1270 | """ |
1289 | 1271 | |
1290 | | def __init__(self, p, vtype, dim=1, name=""): |
| 1272 | def __cinit__(self, p, vtype, dim=1, name=""): |
1291 | 1273 | r""" |
1292 | 1274 | Constructor for ``MIPVariable``. |
1293 | 1275 | |
… |
… |
|
1295 | 1277 | |
1296 | 1278 | - ``p`` -- the instance of ``MixedIntegerLinearProgram`` to which the |
1297 | 1279 | variable is to be linked. |
| 1280 | |
1298 | 1281 | - ``vtype`` (integer) -- Defines the type of the variables |
1299 | 1282 | (default is ``REAL``). |
| 1283 | |
1300 | 1284 | - ``dim`` -- the integer defining the definition of the variable. |
| 1285 | |
1301 | 1286 | - ``name`` -- A name for the ``MIPVariable``. |
1302 | 1287 | |
1303 | 1288 | For more informations, see the method |
… |
… |
|
1313 | 1298 | self._p = p |
1314 | 1299 | self._vtype = vtype |
1315 | 1300 | |
1316 | | ##################################################### |
1317 | | self._name="x" |
| 1301 | self._hasname = (len(name) >0) |
1318 | 1302 | |
| 1303 | # create a temporary char * |
| 1304 | cdef char *name_c = name |
| 1305 | # and copy it over |
| 1306 | self._name = <char*>sage_malloc(len(name)+1) |
| 1307 | strcpy(self._name, name_c) |
| 1308 | |
| 1309 | def __dealloc__(self): |
| 1310 | if self._name: |
| 1311 | sage_free(self._name) |
1319 | 1312 | |
1320 | 1313 | def __getitem__(self, i): |
1321 | 1314 | r""" |
… |
… |
|
1332 | 1325 | sage: v[0] |
1333 | 1326 | x_0 |
1334 | 1327 | """ |
| 1328 | cdef MIPVariable s = self |
| 1329 | |
| 1330 | cdef int j |
| 1331 | |
1335 | 1332 | if self._dict.has_key(i): |
1336 | 1333 | return self._dict[i] |
1337 | 1334 | elif self._dim == 1: |
1338 | | self._dict[i] = self._p._add_element_to_ring(self._vtype) |
| 1335 | |
| 1336 | j = self._p._backend.add_variable(0.0, None, False, True, False, 0.0, |
| 1337 | (str(self._name) + "[" + str(i) + "]") |
| 1338 | if self._hasname else None) |
| 1339 | |
| 1340 | v = LinearFunction({j : 1}) |
| 1341 | self._p._variables[v] = j |
| 1342 | self._p._backend.set_variable_type(j,self._vtype) |
| 1343 | self._dict[i] = v |
| 1344 | |
| 1345 | return v |
| 1346 | |
| 1347 | else: |
| 1348 | self._dict[i] = MIPVariable( |
| 1349 | self._p, |
| 1350 | self._vtype, |
| 1351 | dim=self._dim-1, |
| 1352 | name = ("" if not self._hasname |
| 1353 | else (str(self._name) + "[" + str(i) + "]"))) |
| 1354 | |
1339 | 1355 | return self._dict[i] |
1340 | | else: |
1341 | | self._dict[i] = MIPVariable(self._p, self._vtype, dim=self._dim-1) |
1342 | | return self._dict[i] |
1343 | | |
1344 | | # def _update_variables_name(self, prefix=None): |
1345 | | # r""" |
1346 | | # Updates the names of the variables in the parent instant of ``MixedIntegerLinearProgram``. |
1347 | | |
1348 | | # Only called before writing the Problem to a MPS or LP file. |
1349 | | |
1350 | | # EXAMPLE:: |
1351 | | |
1352 | | # sage: p=MixedIntegerLinearProgram() |
1353 | | # sage: v=p.new_variable(name="Test") |
1354 | | # sage: v[5]+v[99] |
1355 | | # x_0 +x_1 |
1356 | | # sage: v._update_variables_name() |
1357 | | # """ |
1358 | | |
1359 | | # if prefix == None: |
1360 | | # prefix = self._name |
1361 | | |
1362 | | # if self._dim == 1: |
1363 | | # for (k,v) in self._dict.iteritems(): |
1364 | | # name = prefix + "[" + str(k) + "]" |
1365 | | # self._p._backend.col_name(self._p._variables[v], name) |
1366 | | # #self._p._variables_name[self._p._variables[v]]=prefix + "[" + str(k) + "]" |
1367 | | # else: |
1368 | | # for v in self._dict.itervalues(): |
1369 | | # v._update_variables_name(prefix=prefix + "(" + str(k) + ")") |
1370 | | |
1371 | 1356 | |
1372 | 1357 | def __repr__(self): |
1373 | 1358 | r""" |