Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/numerical/mip.pyx
4057 views
1
r"""
2
Mixed integer linear programming
3
4
A linear program (`LP <http://en.wikipedia.org/wiki/Linear_programming>`_)
5
is an `optimization problem <http://en.wikipedia.org/wiki/Optimization_%28mathematics%29>`_
6
in the following form
7
8
.. MATH::
9
\max \{ c^T x \;|\; A x \leq b, x \geq 0 \}
10
11
with given `A \in \mathbb{R}^{m,n}`, `b \in \mathbb{R}^m`,
12
`c \in \mathbb{R}^n` and unknown `x \in \mathbb{R}^{n}`.
13
If some or all variables in the vector `x` are restricted over
14
the integers `\mathbb{Z}`, the problem is called mixed integer
15
linear program (`MILP <http://en.wikipedia.org/wiki/Mixed_integer_linear_programming>`_).
16
A wide variety of problems in optimization
17
can be formulated in this standard form. Then, solvers are
18
able to calculate a solution.
19
20
Imagine you want to solve the following linear system of three equations:
21
22
- `w_0 + w_1 + w_2 - 14 w_3 = 0`
23
- `w_1 + 2 w_2 - 8 w_3 = 0`
24
- `2 w_2 - 3 w_3 = 0`
25
26
and this additional inequality:
27
28
- `w_0 - w_1 - w_2 \geq 0`
29
30
where all `w_i \in \mathbb{Z}`. You know that the trivial solution is
31
`w_i = 0 \; \forall i`, but what is the first non-trivial one with
32
`w_3 \geq 1`?
33
34
A mixed integer linear program can give you an answer:
35
36
#. You have to create an instance of :class:`MixedIntegerLinearProgram` and
37
-- in our case -- specify that it is a minimization.
38
#. Create a variable vector ``w`` via ``w = p.new_variable(integer=True)`` and
39
tell the system that it is over the integers.
40
#. Add those three equations as equality constraints via
41
:meth:`add_constraint <sage.numerical.mip.MixedIntegerLinearProgram.add_constraint>`.
42
#. Also add the inequality constraint.
43
#. Add an inequality constraint `w_3 \geq 1` to exclude the trivial solution.
44
#. By default, all variables have a minimum of `0`. We remove that constraint
45
via ``p.set_min(variable, None)``, see :meth:`set_min <sage.numerical.mip.MixedIntegerLinearProgram.set_min>`.
46
#. Specify the objective function via :meth:`set_objective <sage.numerical.mip.MixedIntegerLinearProgram.set_objective>`.
47
In our case that is just `w_3`. If it
48
is a pure constraint satisfaction problem, specify it as ``None``.
49
#. To check if everything is set up correctly, you can print the problem via
50
:meth:`show <sage.numerical.mip.MixedIntegerLinearProgram.show>`.
51
#. :meth:`Solve <sage.numerical.mip.MixedIntegerLinearProgram.solve>` it and print the solution.
52
53
The following example shows all these steps::
54
55
sage: p = MixedIntegerLinearProgram(maximization=False, solver = "GLPK")
56
sage: w = p.new_variable(integer=True)
57
sage: p.add_constraint(w[0] + w[1] + w[2] - 14*w[3] == 0)
58
sage: p.add_constraint(w[1] + 2*w[2] - 8*w[3] == 0)
59
sage: p.add_constraint(2*w[2] - 3*w[3] == 0)
60
sage: p.add_constraint(w[0] - w[1] - w[2] >= 0)
61
sage: p.add_constraint(w[3] >= 1)
62
sage: _ = [ p.set_min(w[i], None) for i in range(1,4) ]
63
sage: p.set_objective(w[3])
64
sage: p.show()
65
Minimization:
66
x_3
67
Constraints:
68
0.0 <= x_0 + x_1 + x_2 - 14.0 x_3 <= 0.0
69
0.0 <= x_1 + 2.0 x_2 - 8.0 x_3 <= 0.0
70
0.0 <= 2.0 x_2 - 3.0 x_3 <= 0.0
71
- x_0 + x_1 + x_2 <= 0.0
72
- x_3 <= -1.0
73
Variables:
74
x_0 is an integer variable (min=0.0, max=+oo)
75
x_1 is an integer variable (min=-oo, max=+oo)
76
x_2 is an integer variable (min=-oo, max=+oo)
77
x_3 is an integer variable (min=-oo, max=+oo)
78
sage: print 'Objective Value:', p.solve()
79
Objective Value: 2.0
80
sage: for i, v in p.get_values(w).iteritems():\
81
print 'w_%s = %s' % (i, int(round(v)))
82
w_0 = 15
83
w_1 = 10
84
w_2 = 3
85
w_3 = 2
86
"""
87
88
include "../ext/stdsage.pxi"
89
include "../ext/interrupt.pxi"
90
include "../ext/cdefs.pxi"
91
from copy import copy,deepcopy
92
93
cdef class MixedIntegerLinearProgram:
94
r"""
95
The ``MixedIntegerLinearProgram`` class is the link between Sage, linear
96
programming (LP) and mixed integer programming (MIP) solvers.
97
98
See the Wikipedia article on `linear programming
99
<http://en.wikipedia.org/wiki/Linear_programming>`_ for further information
100
on linear programming and the documentation of the :mod:`MILP module
101
<sage.numerical.mip>` for its use in Sage.
102
103
A mixed integer program consists of variables, linear constraints on these
104
variables, and an objective function which is to be maximised or minimised
105
under these constraints. An instance of ``MixedIntegerLinearProgram`` also
106
requires the information on the direction of the optimization.
107
108
INPUT:
109
110
- ``solver`` -- 4 solvers should be available through this class:
111
112
- GLPK (``solver="GLPK"``). See the `GLPK
113
<http://www.gnu.org/software/glpk/>`_ web site.
114
115
- COIN Branch and Cut (``solver="Coin"``). See the `COIN-OR
116
<http://www.coin-or.org>`_ web site.
117
118
- CPLEX (``solver="CPLEX"``). See the `CPLEX
119
<http://www.ilog.com/products/cplex/>`_ web site.
120
121
- Gurobi (``solver="Gurobi"``). See the `Gurobi <http://www.gurobi.com/>`_
122
web site.
123
124
- If ``solver=None`` (default), the default solver is used (see
125
``default_mip_solver`` method.
126
127
- ``maximization``
128
129
- When set to ``True`` (default), the ``MixedIntegerLinearProgram``
130
is defined as a maximization.
131
132
- When set to ``False``, the ``MixedIntegerLinearProgram`` is
133
defined as a minimization.
134
135
- ``constraint_generation`` -- whether to require the returned solver to
136
support constraint generation (excludes Coin). ``False by default``.
137
138
.. SEEALSO::
139
140
- :func:`default_mip_solver` -- Returns/Sets the default MIP solver.
141
142
EXAMPLES:
143
144
Computation of a maximum stable set in Petersen's graph::
145
146
sage: g = graphs.PetersenGraph()
147
sage: p = MixedIntegerLinearProgram(maximization=True)
148
sage: b = p.new_variable()
149
sage: p.set_objective(sum([b[v] for v in g]))
150
sage: for (u,v) in g.edges(labels=None):
151
... p.add_constraint(b[u] + b[v], max=1)
152
sage: p.set_binary(b)
153
sage: p.solve(objective_only=True)
154
4.0
155
"""
156
157
def __init__(self, solver = None, maximization=True, constraint_generation = False, check_redundant = False):
158
r"""
159
Constructor for the ``MixedIntegerLinearProgram`` class.
160
161
INPUT:
162
163
- ``solver`` -- 4 solvers should be available through this class:
164
165
- GLPK (``solver="GLPK"``). See the `GLPK
166
<http://www.gnu.org/software/glpk/>`_ web site.
167
168
- COIN Branch and Cut (``solver="Coin"``). See the `COIN-OR
169
<http://www.coin-or.org>`_ web site.
170
171
- CPLEX (``solver="CPLEX"``). See the `CPLEX
172
<http://www.ilog.com/products/cplex/>`_ web site. An interface to
173
CPLEX is not yet implemented.
174
175
- Gurobi (``solver="Gurobi"``). See the `Gurobi
176
<http://www.gurobi.com/>`_ web site.
177
178
-If ``solver=None`` (default), the default solver is used (see
179
``default_mip_solver`` method.
180
181
- ``maximization``
182
183
- When set to ``True`` (default), the ``MixedIntegerLinearProgram``
184
is defined as a maximization.
185
- When set to ``False``, the ``MixedIntegerLinearProgram`` is
186
defined as a minimization.
187
188
- ``constraint_generation`` -- whether to require the returned solver to
189
support constraint generation (excludes Coin). ``False by default``.
190
191
- ``check_redundant`` -- whether to check that constraints added to the
192
program are redundant with constraints already in the program.
193
Only obvious redundancies are checked: to be considered redundant,
194
either a constraint is equal to another constraint in the program,
195
or it is a constant multiple of the other. To make this search
196
effective and efficient, constraints are normalized; thus, the
197
constraint `-x_1 < 0` will be stored as `x_1 > 0`.
198
199
.. SEEALSO::
200
201
- :meth:`default_mip_solver` -- Returns/Sets the default MIP solver.
202
203
EXAMPLE::
204
205
sage: p = MixedIntegerLinearProgram(maximization=True)
206
207
TESTS:
208
209
Checks that the objects are deallocated without invoking the cyclic garbage
210
collector (cf. :trac:`12616`)::
211
212
sage: del p
213
sage: def just_create_variables():
214
... p = MixedIntegerLinearProgram()
215
... b = p.new_variable()
216
... p.add_constraint(b[3]+b[6] <= 2)
217
... p.solve()
218
sage: C = sage.numerical.mip.MixedIntegerLinearProgram
219
sage: import gc
220
sage: _ = gc.collect() # avoid side effects of other doc tests
221
sage: len([x for x in gc.get_objects() if isinstance(x,C)])
222
0
223
224
We now disable the cyclic garbage collector. Since :trac:`12616` avoids
225
a reference cycle, the mixed integer linear program created in
226
``just_create_variables()`` is removed even without the cyclic garbage
227
collection::
228
229
sage: gc.disable()
230
sage: just_create_variables()
231
sage: len([x for x in gc.get_objects() if isinstance(x,C)])
232
0
233
sage: gc.enable()
234
"""
235
236
from sage.numerical.backends.generic_backend import get_solver
237
238
self._backend = get_solver(solver=solver,
239
constraint_generation=constraint_generation)
240
241
if not maximization:
242
self._backend.set_sense(-1)
243
244
self.__BINARY = 0
245
self.__REAL = -1
246
self.__INTEGER = 1
247
248
249
# Associates an index to the variables
250
self._variables = {}
251
252
# Check for redundant constraints
253
self._check_redundant = check_redundant
254
if check_redundant:
255
self._constraints = list()
256
257
def __repr__(self):
258
r"""
259
Returns a short description of the ``MixedIntegerLinearProgram``.
260
261
EXAMPLE::
262
263
sage: p = MixedIntegerLinearProgram()
264
sage: v = p.new_variable()
265
sage: p.add_constraint(v[1] + v[2], max=2)
266
sage: print p
267
Mixed Integer Program ( maximization, 2 variables, 1 constraints )
268
"""
269
cdef GenericBackend b = self._backend
270
271
return ("Mixed Integer Program "+
272
273
( "\"" +self._backend.problem_name()+ "\""
274
if (str(self._backend.problem_name()) != "") else "")+
275
276
" ( " + ("maximization" if b.is_maximization() else "minimization" ) +
277
278
", " + str(b.ncols()) + " variables, " +
279
str(b.nrows()) + " constraints )")
280
281
def __copy__(self):
282
r"""
283
Returns a copy of the current ``MixedIntegerLinearProgram`` instance.
284
285
EXAMPLE::
286
287
sage: p = MixedIntegerLinearProgram()
288
sage: p.add_constraint(p[0] + p[1], max = 10)
289
sage: q = copy(p)
290
sage: q.number_of_constraints()
291
1
292
"""
293
cdef MixedIntegerLinearProgram p = MixedIntegerLinearProgram(solver="GLPK")
294
295
try:
296
p._variables = copy(self._variables)
297
except AttributeError:
298
pass
299
300
try:
301
p._default_mipvariable = self._default_mipvariable
302
except AttributeError:
303
pass
304
305
try:
306
p._check_redundant = self._check_redundant
307
p._constraints = copy(self._constraints)
308
except AttributeError:
309
pass
310
311
p._backend = (<GenericBackend> self._backend).copy()
312
return p
313
314
def __getitem__(self, v):
315
r"""
316
Returns the symbolic variable corresponding to the key
317
from a default dictionary.
318
319
It returns the element asked, and otherwise creates it.
320
If necessary, it also creates the default dictionary.
321
322
This method lets the user define LinearProgram without having to
323
define independent dictionaries when it is not necessary for him.
324
325
EXAMPLE::
326
327
sage: p = MixedIntegerLinearProgram()
328
sage: p.set_objective(p['x'] + p['z'])
329
sage: p['x']
330
x_0
331
"""
332
333
try:
334
return self._default_mipvariable[v]
335
except TypeError:
336
self._default_mipvariable = self.new_variable()
337
return self._default_mipvariable[v]
338
339
def set_problem_name(self,name):
340
r"""
341
Sets the name of the ``MixedIntegerLinearProgram``.
342
343
INPUT:
344
345
- ``name`` -- A string representing the name of the
346
``MixedIntegerLinearProgram``.
347
348
EXAMPLE::
349
350
sage: p=MixedIntegerLinearProgram()
351
sage: p.set_problem_name("Test program")
352
sage: p
353
Mixed Integer Program "Test program" ( maximization, 0 variables, 0 constraints )
354
"""
355
356
self._backend.problem_name(name)
357
358
def new_variable(self, real=False, binary=False, integer=False, dim=1,name=""):
359
r"""
360
Returns an instance of ``MIPVariable`` associated
361
to the current instance of ``MixedIntegerLinearProgram``.
362
363
A new variable ``x`` is defined by::
364
365
sage: p = MixedIntegerLinearProgram()
366
sage: x = p.new_variable()
367
368
It behaves exactly as a usual dictionary would. It can use any key
369
argument you may like, as ``x[5]`` or ``x["b"]``, and has methods
370
``items()`` and ``keys()``.
371
372
Any of its fields exists, and is uniquely defined.
373
374
By default, all ``x[i]`` are assumed to be non-negative
375
reals. They can be defined as binary through the parameter
376
``binary=True`` (or integer with ``integer=True``). Lower and
377
upper bounds can be defined or re-defined (for instance when you want
378
some variables to be negative) using ``MixedIntegerLinearProgram`` methods
379
``set_min`` and ``set_max``.
380
381
INPUT:
382
383
- ``dim`` (integer) -- Defines the dimension of the dictionary.
384
If ``x`` has dimension `2`, its fields will be of the form
385
``x[key1][key2]``.
386
387
- ``binary, integer, real`` (boolean) -- Set one of these arguments
388
to ``True`` to ensure that the variable gets the corresponding
389
type. The default type is ``real``.
390
391
- ``name`` (string) -- Associates a name to the variable. This is
392
only useful when exporting the linear program to a file using
393
``write_mps`` or ``write_lp``, and has no other effect.
394
395
EXAMPLE::
396
397
sage: p = MixedIntegerLinearProgram()
398
399
To define two dictionaries of variables, the first being
400
of real type, and the second of integer type ::
401
402
sage: x = p.new_variable(real=True)
403
sage: y = p.new_variable(dim=2, integer=True)
404
sage: p.add_constraint(x[2] + y[3][5], max=2)
405
sage: p.is_integer(x[2])
406
False
407
sage: p.is_integer(y[3][5])
408
True
409
410
An exception is raised when two types are supplied ::
411
412
sage: z = p.new_variable(real = True, integer = True)
413
Traceback (most recent call last):
414
...
415
ValueError: Exactly one of the available types has to be True
416
"""
417
if sum([real, binary, integer]) >= 2:
418
raise ValueError("Exactly one of the available types has to be True")
419
420
if binary:
421
vtype = self.__BINARY
422
elif integer:
423
vtype = self.__INTEGER
424
else:
425
vtype = self.__REAL
426
427
v=MIPVariable(self, vtype, dim=dim,name=name)
428
return v
429
430
cpdef int number_of_constraints(self):
431
r"""
432
Returns the number of constraints assigned so far.
433
434
EXAMPLE::
435
sage: p = MixedIntegerLinearProgram()
436
sage: p.add_constraint(p[0] - p[2], min = 1, max = 4)
437
sage: p.add_constraint(p[0] - 2*p[1], min = 1)
438
sage: p.number_of_constraints()
439
2
440
"""
441
return self._backend.nrows()
442
443
def constraints(self, indices = None):
444
r"""
445
Returns a list of constraints, as 3-tuples.
446
447
INPUT:
448
449
- ``indices`` -- select which constraint(s) to return
450
451
- If ``indices = None``, the method returns the list of all the
452
constraints.
453
454
- If ``indices`` is an integer `i`, the method returns constraint
455
`i`.
456
457
- If ``indices`` is a list of integers, the method returns the list
458
of the corresponding constraints.
459
460
OUTPUT:
461
462
Each constraint is returned as a triple ``lower_bound, (indices,
463
coefficients), upper_bound``. For each of those entries, the
464
corresponding linear function is the one associating to variable
465
``indices[i]`` the coefficient ``coefficients[i]``, and `0` to all the
466
others.
467
468
``lower_bound`` and ``upper_bound`` are numerical values.
469
470
EXAMPLE:
471
472
First, let us define a small LP::
473
474
sage: p = MixedIntegerLinearProgram()
475
sage: p.add_constraint(p[0] - p[2], min = 1, max = 4)
476
sage: p.add_constraint(p[0] - 2*p[1], min = 1)
477
478
To obtain the list of all constraints::
479
480
sage: p.constraints() # not tested
481
[(1.0, ([1, 0], [-1.0, 1.0]), 4.0), (1.0, ([2, 0], [-2.0, 1.0]), None)]
482
483
Or constraint `0` only::
484
485
sage: p.constraints(0) # not tested
486
(1.0, ([1, 0], [-1.0, 1.0]), 4.0)
487
488
A list of constraints containing only `1`::
489
490
sage: p.constraints([1]) # not tested
491
[(1.0, ([2, 0], [-2.0, 1.0]), None)]
492
493
494
TESTS:
495
496
As the ordering of the variables in each constraint depends on the
497
solver used, we define a short function reordering it before it is
498
printed. The output would look the same without this function applied::
499
500
sage: def reorder_constraint((lb,(ind,coef),ub)):
501
... d = dict(zip(ind, coef))
502
... ind.sort()
503
... return (lb, (ind, [d[i] for i in ind]), ub)
504
505
Running the examples from above, reordering applied::
506
507
sage: p = MixedIntegerLinearProgram(solver = "GLPK")
508
sage: p.add_constraint(p[0] - p[2], min = 1, max = 4)
509
sage: p.add_constraint(p[0] - 2*p[1], min = 1)
510
sage: sorted(map(reorder_constraint,p.constraints()))
511
[(1.0, ([0, 1], [1.0, -1.0]), 4.0), (1.0, ([0, 2], [1.0, -2.0]), None)]
512
sage: reorder_constraint(p.constraints(0))
513
(1.0, ([0, 1], [1.0, -1.0]), 4.0)
514
sage: sorted(map(reorder_constraint,p.constraints([1])))
515
[(1.0, ([0, 2], [1.0, -2.0]), None)]
516
517
"""
518
from sage.rings.integer import Integer as Integer
519
cdef int i
520
cdef str s
521
cdef GenericBackend b = self._backend
522
523
result = list()
524
525
# If indices == None, we actually want to return all constraints
526
if indices == None:
527
indices = range(b.nrows())
528
529
# Only one constraint
530
if isinstance(indices, int) or isinstance(indices, Integer):
531
lb, ub = b.row_bounds(indices)
532
return (lb, b.row(indices), ub)
533
534
# List of constraints
535
elif isinstance(indices, list):
536
for i in indices:
537
lb, ub = b.row_bounds(i)
538
result.append((lb, b.row(i), ub))
539
540
return result
541
542
# Weird Input
543
else:
544
raise ValueError, "constraints() requires a list of integers, though it will accommodate None or an integer."
545
546
def show(self):
547
r"""
548
Displays the ``MixedIntegerLinearProgram`` in a human-readable
549
way.
550
551
EXAMPLES:
552
553
When constraints and variables have names ::
554
555
sage: p = MixedIntegerLinearProgram(solver = "GLPK")
556
sage: x = p.new_variable(name="Hey")
557
sage: p.set_objective(x[1] + x[2])
558
sage: p.add_constraint(-3*x[1] + 2*x[2], max=2, name="Constraint_1")
559
sage: p.show()
560
Maximization:
561
Hey[1] + Hey[2]
562
Constraints:
563
Constraint_1: -3.0 Hey[1] + 2.0 Hey[2] <= 2.0
564
Variables:
565
Hey[1] is a continuous variable (min=0.0, max=+oo)
566
Hey[2] is a continuous variable (min=0.0, max=+oo)
567
568
Without any names ::
569
570
sage: p = MixedIntegerLinearProgram(solver = "GLPK")
571
sage: x = p.new_variable()
572
sage: p.set_objective(x[1] + x[2])
573
sage: p.add_constraint(-3*x[1] + 2*x[2], max=2)
574
sage: p.show()
575
Maximization:
576
x_0 + x_1
577
Constraints:
578
-3.0 x_0 + 2.0 x_1 <= 2.0
579
Variables:
580
x_0 is a continuous variable (min=0.0, max=+oo)
581
x_1 is a continuous variable (min=0.0, max=+oo)
582
"""
583
584
cdef int i, j
585
cdef double c
586
cdef GenericBackend b = self._backend
587
588
# inv_variables associates a MIPVariable object to an id
589
inv_variables = {}
590
591
for (v, id) in self._variables.iteritems():
592
inv_variables[id]=v
593
594
# varid_name associates variables id to names
595
varid_name = {}
596
597
for 0<= i < b.ncols():
598
s = b.col_name(i)
599
varid_name[i] = ("x_"+str(i)) if s == "" else s
600
601
##### Sense and objective function
602
603
print ("Maximization:" if b.is_maximization() else "Minimization:")
604
print " ",
605
606
first = True
607
for 0<= i< b.ncols():
608
c = b.objective_coefficient(i)
609
if c == 0:
610
continue
611
612
print (("+ " if (not first and c>0) else "") +
613
("" if c == 1 else ("- " if c == -1 else str(c)+" "))+varid_name[i]
614
),
615
first = False
616
617
if b.obj_constant_term > 0.0: print "+", b.obj_constant_term
618
elif b.obj_constant_term < 0.0: print "-", -b.obj_constant_term
619
620
print
621
622
##### Constraints
623
print "Constraints:"
624
625
for 0<= i < b.nrows():
626
627
indices, values = b.row(i)
628
629
lb, ub = b.row_bounds(i)
630
631
print " ",
632
633
634
# Constraint's name
635
if b.row_name(i):
636
print b.row_name(i)+":",
637
638
# Lower bound
639
if lb is not None:
640
print str(lb)+" <=",
641
642
first = True
643
644
for j,c in sorted(zip(indices, values)):
645
646
if c == 0:
647
continue
648
649
print (("+ " if (not first and c>0) else "") +
650
("" if c == 1 else ("- " if c == -1 else (str(c) + " " if first and c < 0 else ("- " + str(abs(c)) + " " if c < 0 else str(c) + " "))))+varid_name[j]
651
),
652
first = False
653
654
# Upper bound
655
print ("<= "+str(ub) if ub!=None else "")
656
657
658
##### Variables
659
print "Variables:"
660
661
for 0<= i < b.ncols():
662
print " " + varid_name[i] + " is",
663
664
if b.is_variable_integer(i):
665
print "an integer variable",
666
elif b.is_variable_binary(i):
667
print "a boolean variable",
668
else:
669
print "a continuous variable",
670
671
lb, ub = b.col_bounds(i)
672
673
print "(min=" + ( str(lb) if lb != None else "-oo" )+",",
674
print "max=" + ( str(ub) if ub != None else "+oo" )+")"
675
676
677
def write_mps(self,filename,modern=True):
678
r"""
679
Write the linear program as a MPS file.
680
681
This function export the problem as a MPS file.
682
683
INPUT:
684
685
- ``filename`` -- The file in which you want the problem
686
to be written.
687
688
- ``modern`` -- Lets you choose between Fixed MPS and Free MPS
689
690
- ``True`` -- Outputs the problem in Free MPS
691
- ``False`` -- Outputs the problem in Fixed MPS
692
693
EXAMPLE::
694
695
sage: p = MixedIntegerLinearProgram()
696
sage: x = p.new_variable()
697
sage: p.set_objective(x[1] + x[2])
698
sage: p.add_constraint(-3*x[1] + 2*x[2], max=2,name="OneConstraint")
699
sage: p.write_mps(SAGE_TMP+"/lp_problem.mps")
700
701
For information about the MPS file format :
702
http://en.wikipedia.org/wiki/MPS_%28format%29
703
"""
704
705
self._backend.write_mps(filename, modern)
706
707
def write_lp(self,filename):
708
r"""
709
Write the linear program as a LP file.
710
711
This function export the problem as a LP file.
712
713
INPUT:
714
715
- ``filename`` -- The file in which you want the problem
716
to be written.
717
718
EXAMPLE::
719
720
sage: p = MixedIntegerLinearProgram()
721
sage: x = p.new_variable()
722
sage: p.set_objective(x[1] + x[2])
723
sage: p.add_constraint(-3*x[1] + 2*x[2], max=2)
724
sage: p.write_lp(SAGE_TMP+"/lp_problem.lp")
725
726
For more information about the LP file format :
727
http://lpsolve.sourceforge.net/5.5/lp-format.htm
728
"""
729
730
self._backend.write_lp(filename)
731
732
def get_values(self, *lists):
733
r"""
734
Return values found by the previous call to ``solve()``.
735
736
INPUT:
737
738
- Any instance of ``MIPVariable`` (or one of its elements),
739
or lists of them.
740
741
OUTPUT:
742
743
- Each instance of ``MIPVariable`` is replaced by a dictionary
744
containing the numerical values found for each
745
corresponding variable in the instance.
746
- Each element of an instance of a ``MIPVariable`` is replaced
747
by its corresponding numerical value.
748
749
.. NOTE::
750
751
While a variable may be declared as binary or integer, its value as
752
returned by the solver is of type ``float``.
753
754
EXAMPLE::
755
756
sage: p = MixedIntegerLinearProgram()
757
sage: x = p.new_variable()
758
sage: y = p.new_variable(dim=2)
759
sage: p.set_objective(x[3] + 3*y[2][9] + x[5])
760
sage: p.add_constraint(x[3] + y[2][9] + 2*x[5], max=2)
761
sage: p.solve()
762
6.0
763
764
To return the optimal value of ``y[2][9]``::
765
766
sage: p.get_values(y[2][9])
767
2.0
768
769
To get a dictionary identical to ``x`` containing optimal
770
values for the corresponding variables ::
771
772
sage: x_sol = p.get_values(x)
773
sage: x_sol.keys()
774
[3, 5]
775
776
Obviously, it also works with variables of higher dimension::
777
778
sage: y_sol = p.get_values(y)
779
780
We could also have tried ::
781
782
sage: [x_sol, y_sol] = p.get_values(x, y)
783
784
Or::
785
786
sage: [x_sol, y_sol] = p.get_values([x, y])
787
"""
788
val = []
789
for l in lists:
790
if isinstance(l, MIPVariable):
791
if l.depth() == 1:
792
c = {}
793
for (k,v) in l.items():
794
#c[k] = self._values[v] if self._values.has_key(v) else None
795
c[k] = self._backend.get_variable_value(self._variables[v])
796
val.append(c)
797
else:
798
c = {}
799
for (k,v) in l.items():
800
c[k] = self.get_values(v)
801
val.append(c)
802
elif isinstance(l, list):
803
if len(l) == 1:
804
val.append([self.get_values(l[0])])
805
else:
806
c = []
807
[c.append(self.get_values(ll)) for ll in l]
808
val.append(c)
809
elif self._variables.has_key(l):
810
#val.append(self._values[l])
811
val.append(self._backend.get_variable_value(self._variables[l]))
812
if len(lists) == 1:
813
return val[0]
814
else:
815
return val
816
817
def set_objective(self,obj):
818
r"""
819
Sets the objective of the ``MixedIntegerLinearProgram``.
820
821
INPUT:
822
823
- ``obj`` -- A linear function to be optimized.
824
( can also be set to ``None`` or ``0`` when just
825
looking for a feasible solution )
826
827
EXAMPLE:
828
829
Let's solve the following linear program::
830
831
Maximize:
832
x + 5 * y
833
Constraints:
834
x + 0.2 y <= 4
835
1.5 * x + 3 * y <= 4
836
Variables:
837
x is Real (min = 0, max = None)
838
y is Real (min = 0, max = None)
839
840
This linear program can be solved as follows::
841
842
sage: p = MixedIntegerLinearProgram(maximization=True)
843
sage: x = p.new_variable()
844
sage: p.set_objective(x[1] + 5*x[2])
845
sage: p.add_constraint(x[1] + 2/10*x[2], max=4)
846
sage: p.add_constraint(1.5*x[1]+3*x[2], max=4)
847
sage: round(p.solve(),5)
848
6.66667
849
sage: p.set_objective(None)
850
sage: _ = p.solve()
851
"""
852
cdef list values = []
853
854
# If the objective is None, or a constant, we want to remember
855
# that the objective function has been defined ( the user did not
856
# forget it ). In some LP problems, you just want a feasible solution
857
# and do not care about any function being optimal.
858
859
cdef int i
860
861
if obj != None:
862
f = obj.dict()
863
else:
864
f = {-1 : 0}
865
866
cdef double d = f.pop(-1,0.0)
867
868
for i in range(self._backend.ncols()):
869
values.append(f.get(i,0.0))
870
871
self._backend.set_objective(values, d)
872
873
def add_constraint(self, linear_function, max=None, min=None, name=None):
874
r"""
875
Adds a constraint to the ``MixedIntegerLinearProgram``.
876
877
INPUT:
878
879
- ``linear_function`` -- Two different types of arguments are possible:
880
- A linear function. In this case, arguments ``min`` or ``max``
881
have to be specified.
882
- A linear constraint of the form ``A <= B``, ``A >= B``,
883
``A <= B <= C``, ``A >= B >= C`` or ``A == B``. In this
884
case, arguments ``min`` and ``max`` will be ignored.
885
- ``max`` -- An upper bound on the constraint (set to ``None``
886
by default). This must be a numerical value.
887
- ``min`` -- A lower bound on the constraint. This must be a
888
numerical value.
889
- ``name`` -- A name for the constraint.
890
891
To set a lower and/or upper bound on the variables use the methods
892
``set_min`` and/or ``set_max`` of ``MixedIntegerLinearProgram``.
893
894
EXAMPLE:
895
896
Consider the following linear program::
897
898
Maximize:
899
x + 5 * y
900
Constraints:
901
x + 0.2 y <= 4
902
1.5 * x + 3 * y <= 4
903
Variables:
904
x is Real (min = 0, max = None)
905
y is Real (min = 0, max = None)
906
907
It can be solved as follows::
908
909
sage: p = MixedIntegerLinearProgram(maximization=True)
910
sage: x = p.new_variable()
911
sage: p.set_objective(x[1] + 5*x[2])
912
sage: p.add_constraint(x[1] + 0.2*x[2], max=4)
913
sage: p.add_constraint(1.5*x[1] + 3*x[2], max=4)
914
sage: round(p.solve(),6)
915
6.666667
916
917
There are two different ways to add the constraint
918
``x[5] + 3*x[7] <= x[6] + 3`` to a ``MixedIntegerLinearProgram``.
919
920
The first one consists in giving ``add_constraint`` this
921
very expression::
922
923
sage: p.add_constraint( x[5] + 3*x[7] <= x[6] + 3 )
924
925
The second (slightly more efficient) one is to use the
926
arguments ``min`` or ``max``, which can only be numerical
927
values::
928
929
sage: p.add_constraint( x[5] + 3*x[7] - x[6], max = 3 )
930
931
One can also define double-bounds or equality using symbols
932
``<=``, ``>=`` and ``==``::
933
934
sage: p.add_constraint( x[5] + 3*x[7] == x[6] + 3 )
935
sage: p.add_constraint( x[5] + 3*x[7] <= x[6] + 3 <= x[8] + 27 )
936
937
The previous program can be rewritten::
938
939
sage: p = MixedIntegerLinearProgram(maximization=True)
940
sage: x = p.new_variable()
941
sage: p.set_objective(x[1] + 5*x[2])
942
sage: p.add_constraint(x[1] + 0.2*x[2] <= 4)
943
sage: p.add_constraint(1.5*x[1] + 3*x[2] <= 4)
944
sage: round(p.solve(), 5)
945
6.66667
946
947
TESTS:
948
949
Complex constraints::
950
951
sage: p = MixedIntegerLinearProgram(solver = "GLPK")
952
sage: b = p.new_variable()
953
sage: p.add_constraint( b[8] - b[15] <= 3*b[8] + 9)
954
sage: p.show()
955
Maximization:
956
<BLANKLINE>
957
Constraints:
958
-2.0 x_0 - x_1 <= 9.0
959
Variables:
960
x_0 is a continuous variable (min=0.0, max=+oo)
961
x_1 is a continuous variable (min=0.0, max=+oo)
962
963
Empty constraint::
964
965
sage: p=MixedIntegerLinearProgram()
966
sage: p.add_constraint(sum([]),min=2)
967
968
Min/Max are numerical ::
969
970
sage: v = p.new_variable()
971
sage: p.add_constraint(v[3] + v[5], min = v[6])
972
Traceback (most recent call last):
973
...
974
ValueError: min and max arguments are required to be numerical
975
sage: p.add_constraint(v[3] + v[5], max = v[6])
976
Traceback (most recent call last):
977
...
978
ValueError: min and max arguments are required to be numerical
979
980
Do not add redundant elements (notice only one copy of each constraint is added)::
981
982
sage: lp = MixedIntegerLinearProgram(solver = "GLPK", check_redundant=True)
983
sage: for each in xrange(10): lp.add_constraint(lp[0]-lp[1],min=1)
984
sage: lp.show()
985
Maximization:
986
<BLANKLINE>
987
Constraints:
988
1.0 <= x_0 - x_1
989
Variables:
990
x_0 is a continuous variable (min=0.0, max=+oo)
991
x_1 is a continuous variable (min=0.0, max=+oo)
992
993
We check for constant multiples of constraints as well::
994
995
sage: for each in xrange(10): lp.add_constraint(2*lp[0]-2*lp[1],min=2)
996
sage: lp.show()
997
Maximization:
998
<BLANKLINE>
999
Constraints:
1000
1.0 <= x_0 - x_1
1001
Variables:
1002
x_0 is a continuous variable (min=0.0, max=+oo)
1003
x_1 is a continuous variable (min=0.0, max=+oo)
1004
1005
But if the constant multiple is negative, we should add it anyway (once)::
1006
1007
sage: for each in xrange(10): lp.add_constraint(-2*lp[0]+2*lp[1],min=-2)
1008
sage: lp.show()
1009
Maximization:
1010
<BLANKLINE>
1011
Constraints:
1012
1.0 <= x_0 - x_1
1013
x_0 - x_1 <= 1.0
1014
Variables:
1015
x_0 is a continuous variable (min=0.0, max=+oo)
1016
x_1 is a continuous variable (min=0.0, max=+oo)
1017
"""
1018
if linear_function is None or linear_function is 0:
1019
return None
1020
1021
# Raising an exception when min/max are not as expected
1022
from sage.rings.all import RR
1023
if ((min is not None and min not in RR)
1024
or (max is not None and max not in RR)):
1025
1026
raise ValueError("min and max arguments are required to be numerical")
1027
1028
if isinstance(linear_function, LinearFunction):
1029
1030
f = linear_function.dict()
1031
1032
constant_coefficient = f.get(-1,0)
1033
1034
# We do not want to ignore the constant coefficient
1035
max = (max - constant_coefficient) if max != None else None
1036
min = (min - constant_coefficient) if min != None else None
1037
1038
indices = []
1039
values = []
1040
1041
if self._check_redundant:
1042
b = self._backend
1043
from __builtin__ import min as min_function
1044
i = min_function([v for (v,coeff) in f.iteritems() if coeff != 0])
1045
c = f[i]
1046
C = [(v,coeff/c) for (v,coeff) in f.iteritems() if v != -1]
1047
if c > 0:
1048
min = min/c if min != None else None
1049
max = max/c if max != None else None
1050
else:
1051
tempmin = max/c if max != None else None
1052
tempmax = min/c if min != None else None
1053
min, max = tempmin, tempmax
1054
if (tuple(C),min,max) in self._constraints:
1055
return None
1056
else:
1057
self._constraints.append((tuple(C),min,max))
1058
else:
1059
C = [(v,coeff) for (v,coeff) in f.iteritems() if v != -1]
1060
1061
if min == None and max == None:
1062
raise ValueError("Both max and min are set to None ? Weird!")
1063
1064
self._backend.add_linear_constraint(C, min, max, name)
1065
1066
elif isinstance(linear_function,LinearConstraint):
1067
functions = linear_function.constraints
1068
1069
if linear_function.equality:
1070
self.add_constraint(functions[0] - functions[1], min=0, max=0, name=name)
1071
1072
elif len(functions) == 2:
1073
self.add_constraint(functions[0] - functions[1], max=0, name=name)
1074
1075
else:
1076
self.add_constraint(functions[0] - functions[1], max=0, name=name)
1077
self.add_constraint(functions[1] - functions[2], max=0, name=name)
1078
1079
def remove_constraint(self, int i):
1080
r"""
1081
Removes a constraint from self.
1082
1083
INPUT:
1084
1085
- ``i`` -- Index of the constraint to remove.
1086
1087
EXAMPLE::
1088
1089
sage: p = MixedIntegerLinearProgram()
1090
sage: x, y = p[0], p[1]
1091
sage: p.add_constraint(x + y, max = 10)
1092
sage: p.add_constraint(x - y, max = 0)
1093
sage: p.add_constraint(x, max = 4)
1094
sage: p.show()
1095
Maximization:
1096
<BLANKLINE>
1097
Constraints:
1098
x_0 + x_1 <= 10.0
1099
x_0 - x_1 <= 0.0
1100
x_0 <= 4.0
1101
...
1102
sage: p.remove_constraint(1)
1103
sage: p.show()
1104
Maximization:
1105
<BLANKLINE>
1106
Constraints:
1107
x_0 + x_1 <= 10.0
1108
x_0 <= 4.0
1109
...
1110
sage: p.number_of_constraints()
1111
2
1112
"""
1113
if self._check_redundant: self._constraints.pop(i)
1114
self._backend.remove_constraint(i)
1115
1116
def remove_constraints(self, constraints):
1117
r"""
1118
Remove several constraints.
1119
1120
INPUT:
1121
1122
- ``constraints`` -- an iterable containing the indices of the rows to remove.
1123
1124
EXAMPLE::
1125
1126
sage: p = MixedIntegerLinearProgram()
1127
sage: x, y = p[0], p[1]
1128
sage: p.add_constraint(x + y, max = 10)
1129
sage: p.add_constraint(x - y, max = 0)
1130
sage: p.add_constraint(x, max = 4)
1131
sage: p.show()
1132
Maximization:
1133
<BLANKLINE>
1134
Constraints:
1135
x_0 + x_1 <= 10.0
1136
x_0 - x_1 <= 0.0
1137
x_0 <= 4.0
1138
...
1139
sage: p.remove_constraints([0, 1])
1140
sage: p.show()
1141
Maximization:
1142
<BLANKLINE>
1143
Constraints:
1144
x_0 <= 4.0
1145
...
1146
sage: p.number_of_constraints()
1147
1
1148
1149
When checking for redundant constraints, make sure you remove only
1150
the constraints that were actually added. Problems could arise if
1151
you have a function that builds lps non-interactively, but it fails
1152
to check whether adding a constraint actually increases the number of
1153
constraints. The function might later try to remove constraints that
1154
are not actually there::
1155
1156
sage: p = MixedIntegerLinearProgram(check_redundant=True)
1157
sage: x, y = p[0], p[1]
1158
sage: p.add_constraint(x + y, max = 10)
1159
sage: for each in xrange(10): p.add_constraint(x - y, max = 10)
1160
sage: p.add_constraint(x, max = 4)
1161
sage: p.number_of_constraints()
1162
3
1163
sage: p.remove_constraints(range(1,9))
1164
Traceback (most recent call last):
1165
...
1166
IndexError: pop index out of range
1167
sage: p.remove_constraint(1)
1168
sage: p.number_of_constraints()
1169
2
1170
1171
We should now be able to add the old constraint back in::
1172
1173
sage: for each in xrange(10): p.add_constraint(x - y, max = 10)
1174
sage: p.number_of_constraints()
1175
3
1176
"""
1177
if self._check_redundant:
1178
for i in sorted(constraints,reverse=True):
1179
self._constraints.pop(i)
1180
self._backend.remove_constraints(constraints)
1181
1182
def set_binary(self, ee):
1183
r"""
1184
Sets a variable or a ``MIPVariable`` as binary.
1185
1186
INPUT:
1187
1188
- ``ee`` -- An instance of ``MIPVariable`` or one of
1189
its elements.
1190
1191
EXAMPLE::
1192
1193
sage: p = MixedIntegerLinearProgram()
1194
sage: x = p.new_variable()
1195
1196
With the following instruction, all the variables
1197
from x will be binary::
1198
1199
sage: p.set_binary(x)
1200
sage: p.set_objective(x[0] + x[1])
1201
sage: p.add_constraint(-3*x[0] + 2*x[1], max=2)
1202
1203
It is still possible, though, to set one of these
1204
variables as real while keeping the others as they are::
1205
1206
sage: p.set_real(x[3])
1207
"""
1208
cdef MIPVariable e
1209
e = <MIPVariable> ee
1210
1211
if isinstance(e, MIPVariable):
1212
e._vtype = self.__BINARY
1213
if e.depth() == 1:
1214
for v in e.values():
1215
self._backend.set_variable_type(self._variables[v],self.__BINARY)
1216
else:
1217
for v in e.keys():
1218
self.set_binary(e[v])
1219
elif self._variables.has_key(e):
1220
self._backend.set_variable_type(self._variables[e],self.__BINARY)
1221
else:
1222
raise ValueError("e must be an instance of MIPVariable or one of its elements.")
1223
1224
def is_binary(self, e):
1225
r"""
1226
Tests whether the variable ``e`` is binary. Variables are real by
1227
default.
1228
1229
INPUT:
1230
1231
- ``e`` -- A variable (not a ``MIPVariable``, but one of its elements.)
1232
1233
OUTPUT:
1234
1235
``True`` if the variable ``e`` is binary; ``False`` otherwise.
1236
1237
EXAMPLE::
1238
1239
sage: p = MixedIntegerLinearProgram()
1240
sage: v = p.new_variable()
1241
sage: p.set_objective(v[1])
1242
sage: p.is_binary(v[1])
1243
False
1244
sage: p.set_binary(v[1])
1245
sage: p.is_binary(v[1])
1246
True
1247
"""
1248
1249
return self._backend.is_variable_binary(self._variables[e])
1250
1251
def set_integer(self, ee):
1252
r"""
1253
Sets a variable or a ``MIPVariable`` as integer.
1254
1255
INPUT:
1256
1257
- ``ee`` -- An instance of ``MIPVariable`` or one of
1258
its elements.
1259
1260
EXAMPLE::
1261
1262
sage: p = MixedIntegerLinearProgram()
1263
sage: x = p.new_variable()
1264
1265
With the following instruction, all the variables
1266
from x will be integers::
1267
1268
sage: p.set_integer(x)
1269
sage: p.set_objective(x[0] + x[1])
1270
sage: p.add_constraint(-3*x[0] + 2*x[1], max=2)
1271
1272
It is still possible, though, to set one of these
1273
variables as real while keeping the others as they are::
1274
1275
sage: p.set_real(x[3])
1276
"""
1277
cdef MIPVariable e
1278
e = <MIPVariable> ee
1279
1280
if isinstance(e, MIPVariable):
1281
e._vtype = self.__INTEGER
1282
if e.depth() == 1:
1283
for v in e.values():
1284
self._backend.set_variable_type(self._variables[v],self.__INTEGER)
1285
else:
1286
for v in e.keys():
1287
self.set_integer(e[v])
1288
elif self._variables.has_key(e):
1289
self._backend.set_variable_type(self._variables[e],self.__INTEGER)
1290
else:
1291
raise ValueError("e must be an instance of MIPVariable or one of its elements.")
1292
1293
def is_integer(self, e):
1294
r"""
1295
Tests whether the variable is an integer. Variables are real by
1296
default.
1297
1298
INPUT:
1299
1300
- ``e`` -- A variable (not a ``MIPVariable``, but one of its elements.)
1301
1302
OUTPUT:
1303
1304
``True`` if the variable ``e`` is an integer; ``False`` otherwise.
1305
1306
EXAMPLE::
1307
1308
sage: p = MixedIntegerLinearProgram()
1309
sage: v = p.new_variable()
1310
sage: p.set_objective(v[1])
1311
sage: p.is_integer(v[1])
1312
False
1313
sage: p.set_integer(v[1])
1314
sage: p.is_integer(v[1])
1315
True
1316
"""
1317
1318
return self._backend.is_variable_integer(self._variables[e])
1319
1320
def set_real(self,ee):
1321
r"""
1322
Sets a variable or a ``MIPVariable`` as real.
1323
1324
INPUT:
1325
1326
- ``ee`` -- An instance of ``MIPVariable`` or one of
1327
its elements.
1328
1329
EXAMPLE::
1330
1331
sage: p = MixedIntegerLinearProgram()
1332
sage: x = p.new_variable()
1333
1334
With the following instruction, all the variables
1335
from x will be real (they are by default, though)::
1336
1337
sage: p.set_real(x)
1338
sage: p.set_objective(x[0] + x[1])
1339
sage: p.add_constraint(-3*x[0] + 2*x[1], max=2)
1340
1341
It is still possible, though, to set one of these
1342
variables as binary while keeping the others as they are::
1343
1344
sage: p.set_binary(x[3])
1345
"""
1346
1347
cdef MIPVariable e
1348
e = <MIPVariable> ee
1349
1350
if isinstance(e, MIPVariable):
1351
e._vtype = self.__REAL
1352
if e.depth() == 1:
1353
for v in e.values():
1354
self._backend.set_variable_type(self._variables[v],self.__REAL)
1355
else:
1356
for v in e.keys():
1357
self.set_real(e[v])
1358
elif self._variables.has_key(e):
1359
self._backend.set_variable_type(self._variables[e],self.__REAL)
1360
else:
1361
raise ValueError("e must be an instance of MIPVariable or one of its elements.")
1362
1363
def is_real(self, e):
1364
r"""
1365
Tests whether the variable is real. Variables are real by default.
1366
1367
INPUT:
1368
1369
- ``e`` -- A variable (not a ``MIPVariable``, but one of its elements.)
1370
1371
OUTPUT:
1372
1373
``True`` if the variable is real; ``False`` otherwise.
1374
1375
EXAMPLE::
1376
1377
sage: p = MixedIntegerLinearProgram()
1378
sage: v = p.new_variable()
1379
sage: p.set_objective(v[1])
1380
sage: p.is_real(v[1])
1381
True
1382
sage: p.set_binary(v[1])
1383
sage: p.is_real(v[1])
1384
False
1385
sage: p.set_real(v[1])
1386
sage: p.is_real(v[1])
1387
True
1388
"""
1389
1390
return self._backend.is_variable_continuous(self._variables[e])
1391
1392
def solve(self, solver=None, log=None, objective_only=False):
1393
r"""
1394
Solves the ``MixedIntegerLinearProgram``.
1395
1396
INPUT:
1397
1398
- ``solver`` -- DEPRECATED -- the solver now has to be set
1399
when calling the class' constructor
1400
1401
- ``log`` -- integer (default: ``None``) The verbosity level. Indicates
1402
whether progress should be printed during computation. The solver is
1403
initialized to report no progress.
1404
1405
- ``objective_only`` -- Boolean variable.
1406
1407
- When set to ``True``, only the objective function is returned.
1408
- When set to ``False`` (default), the optimal numerical values
1409
are stored (takes computational time).
1410
1411
OUTPUT:
1412
1413
The optimal value taken by the objective function.
1414
1415
EXAMPLES:
1416
1417
Consider the following linear program::
1418
1419
Maximize:
1420
x + 5 * y
1421
Constraints:
1422
x + 0.2 y <= 4
1423
1.5 * x + 3 * y <= 4
1424
Variables:
1425
x is Real (min = 0, max = None)
1426
y is Real (min = 0, max = None)
1427
1428
This linear program can be solved as follows::
1429
1430
sage: p = MixedIntegerLinearProgram(maximization=True)
1431
sage: x = p.new_variable()
1432
sage: p.set_objective(x[1] + 5*x[2])
1433
sage: p.add_constraint(x[1] + 0.2*x[2], max=4)
1434
sage: p.add_constraint(1.5*x[1] + 3*x[2], max=4)
1435
sage: round(p.solve(),6)
1436
6.666667
1437
sage: x = p.get_values(x)
1438
sage: round(x[1],6)
1439
0.0
1440
sage: round(x[2],6)
1441
1.333333
1442
1443
Computation of a maximum stable set in Petersen's graph::
1444
1445
sage: g = graphs.PetersenGraph()
1446
sage: p = MixedIntegerLinearProgram(maximization=True)
1447
sage: b = p.new_variable()
1448
sage: p.set_objective(sum([b[v] for v in g]))
1449
sage: for (u,v) in g.edges(labels=None):
1450
... p.add_constraint(b[u] + b[v], max=1)
1451
sage: p.set_binary(b)
1452
sage: p.solve(objective_only=True)
1453
4.0
1454
1455
Constraints in the objective function are respected:
1456
1457
sage: p = MixedIntegerLinearProgram()
1458
sage: x, y = p[0], p[1]
1459
sage: p.add_constraint(2*x + 3*y, max = 6)
1460
sage: p.add_constraint(3*x + 2*y, max = 6)
1461
sage: p.set_objective(x + y + 7)
1462
sage: p.set_integer(x); p.set_integer(y)
1463
sage: p.solve()
1464
9.0
1465
"""
1466
1467
if solver != None:
1468
raise ValueError("Solver argument deprecated. This parameter now has to be set when calling the class' constructor")
1469
1470
if log != None: self._backend.set_verbosity(log)
1471
1472
self._backend.solve()
1473
1474
return self._backend.get_objective_value()
1475
1476
def set_min(self, v, min):
1477
r"""
1478
Sets the minimum value of a variable.
1479
1480
INPUT:
1481
1482
- ``v`` -- a variable (not a ``MIPVariable``, but one of its
1483
elements).
1484
- ``min`` -- the minimum value the variable can take.
1485
When ``min=None``, the variable has no lower bound.
1486
1487
EXAMPLE::
1488
1489
sage: p = MixedIntegerLinearProgram()
1490
sage: v = p.new_variable()
1491
sage: p.set_objective(v[1])
1492
sage: p.get_min(v[1])
1493
0.0
1494
sage: p.set_min(v[1],6)
1495
sage: p.get_min(v[1])
1496
6.0
1497
sage: p.set_min(v[1], None)
1498
sage: p.get_min(v[1])
1499
1500
"""
1501
self._backend.variable_lower_bound(self._variables[v], min)
1502
1503
def set_max(self, v, max):
1504
r"""
1505
Sets the maximum value of a variable.
1506
1507
INPUT
1508
1509
- ``v`` -- a variable (not a ``MIPVariable``, but one of its
1510
elements).
1511
- ``max`` -- the maximum value the variable can take.
1512
When ``max=None``, the variable has no upper bound.
1513
1514
EXAMPLE::
1515
1516
sage: p = MixedIntegerLinearProgram()
1517
sage: v = p.new_variable()
1518
sage: p.set_objective(v[1])
1519
sage: p.get_max(v[1])
1520
sage: p.set_max(v[1],6)
1521
sage: p.get_max(v[1])
1522
6.0
1523
"""
1524
1525
self._backend.variable_upper_bound(self._variables[v], max)
1526
1527
def get_min(self, v):
1528
r"""
1529
Returns the minimum value of a variable.
1530
1531
INPUT:
1532
1533
- ``v`` -- a variable (not a ``MIPVariable``, but one of its elements).
1534
1535
OUTPUT:
1536
1537
Minimum value of the variable, or ``None`` if
1538
the variable has no lower bound.
1539
1540
EXAMPLE::
1541
1542
sage: p = MixedIntegerLinearProgram()
1543
sage: v = p.new_variable()
1544
sage: p.set_objective(v[1])
1545
sage: p.get_min(v[1])
1546
0.0
1547
sage: p.set_min(v[1],6)
1548
sage: p.get_min(v[1])
1549
6.0
1550
sage: p.set_min(v[1], None)
1551
sage: p.get_min(v[1])
1552
"""
1553
1554
return self._backend.variable_lower_bound(self._variables[v])
1555
1556
def get_max(self, v):
1557
r"""
1558
Returns the maximum value of a variable.
1559
1560
INPUT:
1561
1562
- ``v`` -- a variable (not a ``MIPVariable``, but one of its elements).
1563
1564
OUTPUT:
1565
1566
Maximum value of the variable, or ``None`` if
1567
the variable has no upper bound.
1568
1569
EXAMPLE::
1570
1571
sage: p = MixedIntegerLinearProgram()
1572
sage: v = p.new_variable()
1573
sage: p.set_objective(v[1])
1574
sage: p.get_max(v[1])
1575
sage: p.set_max(v[1],6)
1576
sage: p.get_max(v[1])
1577
6.0
1578
"""
1579
1580
return self._backend.variable_upper_bound(self._variables[v])
1581
1582
def solver_parameter(self, name, value = None):
1583
"""
1584
Return or define a solver parameter
1585
1586
The solver parameters are by essence solver-specific, which
1587
means their meaning heavily depends on the solver used.
1588
1589
(If you do not know which solver you are using, then you are using GLPK)
1590
1591
Aliases:
1592
1593
Very common parameters have aliases making them
1594
solver-independent. For example, the following::
1595
1596
sage: p = MixedIntegerLinearProgram(solver = "GLPK")
1597
sage: p.solver_parameter("timelimit", 60)
1598
1599
Sets the solver to stop its computations after 60 seconds, and
1600
works both with GLPK and CPLEX.
1601
1602
- ``"timelimit"`` -- defines the maximum time spent on a
1603
computation. Measured in seconds.
1604
1605
Solver-specific parameters:
1606
1607
- GLPK : We have implemented very close to comprehensive coverage of
1608
the GLPK solver parameters for the simplex and integer
1609
optimization methods. For details, see the documentation of
1610
:meth:`GLPKBackend.solver_parameter
1611
<sage.numerical.backends.glpk_backend.GLPKBackend.solver_parameter>`.
1612
1613
- CPLEX's parameters are identified by a string. Their
1614
list is available `on ILOG's website
1615
<http://publib.boulder.ibm.com/infocenter/odmeinfo/v3r4/index.jsp?topic=/ilog.odms.ide.odme.help/Content/Optimization/Documentation/ODME/_pubskel/ODME_pubskels/startall_ODME34_Eclipse1590.html>`_.
1616
1617
The command ::
1618
1619
sage: p = MixedIntegerLinearProgram(solver = "CPLEX") # optional - CPLEX
1620
sage: p.solver_parameter("CPX_PARAM_TILIM", 60) # optional - CPLEX
1621
1622
works as intended.
1623
1624
INPUT:
1625
1626
- ``name`` (string) -- the parameter
1627
1628
- ``value`` -- the parameter's value if it is to be defined,
1629
or ``None`` (default) to obtain its current value.
1630
1631
EXAMPLE::
1632
1633
sage: p = MixedIntegerLinearProgram(solver = "GLPK")
1634
sage: p.solver_parameter("timelimit", 60)
1635
sage: p.solver_parameter("timelimit")
1636
60.0
1637
"""
1638
if value is None:
1639
return self._backend.solver_parameter(name)
1640
else:
1641
self._backend.solver_parameter(name, value)
1642
1643
class MIPSolverException(Exception):
1644
r"""
1645
Exception raised when the solver fails.
1646
"""
1647
1648
def __init__(self, value):
1649
r"""
1650
Constructor for ``MIPSolverException``.
1651
1652
``MIPSolverException`` is the exception raised when the solver fails.
1653
1654
EXAMPLE::
1655
1656
sage: from sage.numerical.mip import MIPSolverException
1657
sage: MIPSolverException("Error")
1658
MIPSolverException()
1659
1660
TESTS:
1661
1662
No continuous solution::
1663
1664
sage: p=MixedIntegerLinearProgram(solver="GLPK")
1665
sage: v=p.new_variable()
1666
sage: p.add_constraint(v[0],max=5.5)
1667
sage: p.add_constraint(v[0],min=7.6)
1668
sage: p.set_objective(v[0])
1669
1670
Tests of GLPK's Exceptions::
1671
1672
sage: p.solve()
1673
Traceback (most recent call last):
1674
...
1675
MIPSolverException: 'GLPK : Solution is undefined'
1676
1677
No integer solution::
1678
1679
sage: p=MixedIntegerLinearProgram(solver="GLPK")
1680
sage: v=p.new_variable()
1681
sage: p.add_constraint(v[0],max=5.6)
1682
sage: p.add_constraint(v[0],min=5.2)
1683
sage: p.set_objective(v[0])
1684
sage: p.set_integer(v)
1685
1686
Tests of GLPK's Exceptions::
1687
1688
sage: p.solve()
1689
Traceback (most recent call last):
1690
...
1691
MIPSolverException: 'GLPK : Solution is undefined'
1692
"""
1693
self.value = value
1694
1695
def __str__(self):
1696
r"""
1697
Returns the value of the instance of ``MIPSolverException``.
1698
1699
EXAMPLE::
1700
1701
sage: from sage.numerical.mip import MIPSolverException
1702
sage: e = MIPSolverException("Error")
1703
sage: print e
1704
'Error'
1705
"""
1706
return repr(self.value)
1707
1708
cdef class MIPVariable:
1709
r"""
1710
``MIPVariable`` is a variable used by the class
1711
``MixedIntegerLinearProgram``.
1712
"""
1713
1714
def __cinit__(self, p, vtype, dim=1, name=""):
1715
r"""
1716
Constructor for ``MIPVariable``.
1717
1718
INPUT:
1719
1720
- ``p`` -- the instance of ``MixedIntegerLinearProgram`` to which the
1721
variable is to be linked.
1722
1723
- ``vtype`` (integer) -- Defines the type of the variables
1724
(default is ``REAL``).
1725
1726
- ``dim`` -- the integer defining the definition of the variable.
1727
1728
- ``name`` -- A name for the ``MIPVariable``.
1729
1730
For more informations, see the method
1731
``MixedIntegerLinearProgram.new_variable``.
1732
1733
EXAMPLE::
1734
1735
sage: p=MixedIntegerLinearProgram()
1736
sage: v=p.new_variable()
1737
"""
1738
self._dim = dim
1739
self._dict = {}
1740
self._p = p
1741
self._vtype = vtype
1742
1743
self._hasname = (len(name) >0)
1744
1745
# create a temporary char *
1746
cdef char *name_c = name
1747
# and copy it over
1748
self._name = <char*>sage_malloc(len(name)+1)
1749
strcpy(self._name, name_c)
1750
1751
def __dealloc__(self):
1752
if self._name:
1753
sage_free(self._name)
1754
1755
def __getitem__(self, i):
1756
r"""
1757
Returns the symbolic variable corresponding to the key.
1758
1759
Returns the element asked, otherwise creates it.
1760
(When depth>1, recursively creates the variables).
1761
1762
EXAMPLE::
1763
1764
sage: p = MixedIntegerLinearProgram()
1765
sage: v = p.new_variable()
1766
sage: p.set_objective(v[0] + v[1])
1767
sage: v[0]
1768
x_0
1769
"""
1770
cdef MIPVariable s = self
1771
1772
cdef int j
1773
1774
if self._dict.has_key(i):
1775
return self._dict[i]
1776
elif self._dim == 1:
1777
1778
j = self._p._backend.add_variable(0.0, None, False, True, False, 0.0,
1779
(str(self._name) + "[" + str(i) + "]")
1780
if self._hasname else None)
1781
1782
v = LinearFunction({j : 1})
1783
self._p._variables[v] = j
1784
self._p._backend.set_variable_type(j,self._vtype)
1785
self._dict[i] = v
1786
1787
return v
1788
1789
else:
1790
self._dict[i] = MIPVariable(
1791
self._p,
1792
self._vtype,
1793
dim=self._dim-1,
1794
name = ("" if not self._hasname
1795
else (str(self._name) + "[" + str(i) + "]")))
1796
1797
return self._dict[i]
1798
1799
def __repr__(self):
1800
r"""
1801
Returns a representation of self.
1802
1803
EXAMPLE::
1804
1805
sage: p=MixedIntegerLinearProgram()
1806
sage: v=p.new_variable(dim=3)
1807
sage: v
1808
MIPVariable of dimension 3.
1809
sage: v[2][5][9]
1810
x_0
1811
sage: v
1812
MIPVariable of dimension 3.
1813
"""
1814
return "MIPVariable of dimension " + str(self._dim) + "."
1815
1816
def keys(self):
1817
r"""
1818
Returns the keys already defined in the dictionary.
1819
1820
EXAMPLE::
1821
1822
sage: p = MixedIntegerLinearProgram()
1823
sage: v = p.new_variable()
1824
sage: p.set_objective(v[0] + v[1])
1825
sage: v.keys()
1826
[0, 1]
1827
"""
1828
return self._dict.keys()
1829
1830
def items(self):
1831
r"""
1832
Returns the pairs (keys,value) contained in the dictionary.
1833
1834
EXAMPLE::
1835
1836
sage: p = MixedIntegerLinearProgram()
1837
sage: v = p.new_variable()
1838
sage: p.set_objective(v[0] + v[1])
1839
sage: v.items()
1840
[(0, x_0), (1, x_1)]
1841
"""
1842
return self._dict.items()
1843
1844
def depth(self):
1845
r"""
1846
Returns the current variable's depth.
1847
1848
EXAMPLE::
1849
1850
sage: p = MixedIntegerLinearProgram()
1851
sage: v = p.new_variable()
1852
sage: p.set_objective(v[0] + v[1])
1853
sage: v.depth()
1854
1
1855
"""
1856
return self._dim
1857
1858
def values(self):
1859
r"""
1860
Returns the symbolic variables associated to the current dictionary.
1861
1862
EXAMPLE::
1863
1864
sage: p = MixedIntegerLinearProgram()
1865
sage: v = p.new_variable()
1866
sage: p.set_objective(v[0] + v[1])
1867
sage: v.values()
1868
[x_0, x_1]
1869
"""
1870
return self._dict.values()
1871
1872
1873
class LinearFunction:
1874
r"""
1875
An elementary algebra to represent symbolic linear functions.
1876
"""
1877
1878
def __init__(self,f):
1879
r"""
1880
Constructor taking a dictionary or a numerical value as its argument.
1881
1882
A linear function is represented as a dictionary. The
1883
values are the coefficient of the variable represented
1884
by the keys ( which are integers ). The key ``-1``
1885
corresponds to the constant term.
1886
1887
EXAMPLES:
1888
1889
With a dictionary::
1890
1891
sage: from sage.numerical.mip import LinearFunction
1892
sage: LinearFunction({0 : 1, 3 : -8})
1893
x_0 -8 x_3
1894
1895
Using the constructor with a numerical value::
1896
1897
sage: from sage.numerical.mip import LinearFunction
1898
sage: LinearFunction(25)
1899
25
1900
"""
1901
if isinstance(f, dict):
1902
self._f = f
1903
else:
1904
self._f = {-1:f}
1905
1906
def dict(self):
1907
r"""
1908
Returns the dictionary corresponding to the Linear Function.
1909
1910
A linear function is represented as a dictionary. The
1911
value are the coefficient of the variable represented
1912
by the keys ( which are integers ). The key ``-1``
1913
corresponds to the constant term.
1914
1915
EXAMPLE::
1916
1917
sage: from sage.numerical.mip import LinearFunction
1918
sage: lf = LinearFunction({0 : 1, 3 : -8})
1919
sage: lf.dict()
1920
{0: 1, 3: -8}
1921
"""
1922
return self._f
1923
1924
def __add__(self,b):
1925
r"""
1926
Defining the + operator
1927
1928
EXAMPLE::
1929
1930
sage: from sage.numerical.mip import LinearFunction
1931
sage: LinearFunction({0 : 1, 3 : -8}) + LinearFunction({2 : 5, 3 : 2}) - 16
1932
-16 +x_0 +5 x_2 -6 x_3
1933
"""
1934
if isinstance(b, LinearFunction):
1935
e = deepcopy(self._f)
1936
for (id,coeff) in b.dict().iteritems():
1937
e[id] = self._f.get(id,0) + coeff
1938
return LinearFunction(e)
1939
else:
1940
el = deepcopy(self)
1941
el.dict()[-1] = el.dict().get(-1,0) + b
1942
return el
1943
1944
def __neg__(self):
1945
r"""
1946
Defining the - operator (opposite).
1947
1948
EXAMPLE::
1949
1950
sage: from sage.numerical.mip import LinearFunction
1951
sage: -LinearFunction({0 : 1, 3 : -8})
1952
-1 x_0 +8 x_3
1953
"""
1954
return LinearFunction(dict([(id,-coeff) for (id, coeff) in self._f.iteritems()]))
1955
1956
def __sub__(self,b):
1957
r"""
1958
Defining the - operator (substraction).
1959
1960
EXAMPLE::
1961
1962
sage: from sage.numerical.mip import LinearFunction
1963
sage: LinearFunction({2 : 5, 3 : 2}) - 3
1964
-3 +5 x_2 +2 x_3
1965
sage: LinearFunction({0 : 1, 3 : -8}) - LinearFunction({2 : 5, 3 : 2}) - 16
1966
-16 +x_0 -5 x_2 -10 x_3
1967
"""
1968
if isinstance(b, LinearFunction):
1969
e = deepcopy(self._f)
1970
for (id,coeff) in b.dict().iteritems():
1971
e[id] = self._f.get(id,0) - coeff
1972
return LinearFunction(e)
1973
else:
1974
el = deepcopy(self)
1975
el.dict()[-1] = self._f.get(-1,0) - b
1976
return el
1977
1978
def __radd__(self,b):
1979
r"""
1980
Defining the + operator (right side).
1981
1982
EXAMPLE::
1983
1984
sage: from sage.numerical.mip import LinearFunction
1985
sage: 3 + LinearFunction({2 : 5, 3 : 2})
1986
3 +5 x_2 +2 x_3
1987
"""
1988
if isinstance(self,LinearFunction):
1989
return self.__add__(b)
1990
else:
1991
return b.__add__(self)
1992
1993
def __rsub__(self,b):
1994
r"""
1995
Defining the - operator (right side).
1996
1997
EXAMPLE::
1998
1999
sage: from sage.numerical.mip import LinearFunction
2000
sage: 3 - LinearFunction({2 : 5, 3 : 2})
2001
3 -5 x_2 -2 x_3
2002
"""
2003
if isinstance(self,LinearFunction):
2004
return (-self).__add__(b)
2005
else:
2006
return b.__sub__(self)
2007
2008
def __mul__(self,b):
2009
r"""
2010
Defining the * operator.
2011
2012
EXAMPLE::
2013
2014
sage: from sage.numerical.mip import LinearFunction
2015
sage: LinearFunction({2 : 5, 3 : 2}) * 3
2016
15 x_2 +6 x_3
2017
"""
2018
return LinearFunction(dict([(id,b*coeff) for (id, coeff) in self._f.iteritems()]))
2019
2020
def __rmul__(self,b):
2021
r"""
2022
Defining the * operator (right side).
2023
2024
EXAMPLE::
2025
2026
sage: from sage.numerical.mip import LinearFunction
2027
sage: 3 * LinearFunction({2 : 5, 3 : 2})
2028
15 x_2 +6 x_3
2029
"""
2030
return self.__mul__(b)
2031
2032
def __repr__(self):
2033
r"""
2034
Returns a string version of the linear function.
2035
2036
EXAMPLE::
2037
2038
sage: from sage.numerical.mip import LinearFunction
2039
sage: LinearFunction({2 : 5, 3 : 2})
2040
5 x_2 +2 x_3
2041
"""
2042
cdef dict d = deepcopy(self._f)
2043
cdef bint first = True
2044
t = ""
2045
2046
if d.has_key(-1):
2047
coeff = d.pop(-1)
2048
if coeff!=0:
2049
t = str(coeff)
2050
first = False
2051
2052
cdef list l = sorted(d.items())
2053
for id,coeff in l:
2054
if coeff!=0:
2055
if not first:
2056
t += " "
2057
t += ("+" if (not first and coeff >= 0) else "") + (str(coeff) + " " if coeff != 1 else "") + "x_" + str(id)
2058
first = False
2059
return t
2060
2061
def __le__(self,other):
2062
r"""
2063
Defines the <= operator
2064
2065
EXAMPLE::
2066
2067
sage: from sage.numerical.mip import LinearFunction
2068
sage: LinearFunction({2 : 5, 3 : 2}) <= LinearFunction({2 : 3, 9 : 2})
2069
5 x_2 +2 x_3 <= 3 x_2 +2 x_9
2070
"""
2071
return LinearConstraint(self).__le__(other)
2072
2073
def __lt__(self,other):
2074
r"""
2075
Defines the < operator
2076
2077
EXAMPLE::
2078
2079
sage: from sage.numerical.mip import LinearFunction
2080
sage: LinearFunction({2 : 5, 3 : 2}) < LinearFunction({2 : 3, 9 : 2})
2081
Traceback (most recent call last):
2082
...
2083
ValueError: The strict operators are not defined. Use <= and >= instead.
2084
"""
2085
return LinearConstraint(self).__lt__(other)
2086
2087
def __gt__(self,other):
2088
r"""
2089
Defines the > operator
2090
2091
EXAMPLE::
2092
2093
sage: from sage.numerical.mip import LinearFunction
2094
sage: LinearFunction({2 : 5, 3 : 2}) > LinearFunction({2 : 3, 9 : 2})
2095
Traceback (most recent call last):
2096
...
2097
ValueError: The strict operators are not defined. Use <= and >= instead.
2098
"""
2099
return LinearConstraint(self).__gt__(other)
2100
2101
2102
def __ge__(self,other):
2103
r"""
2104
Defines the >= operator
2105
2106
EXAMPLE::
2107
2108
sage: from sage.numerical.mip import LinearFunction
2109
sage: LinearFunction({2 : 5, 3 : 2}) >= LinearFunction({2 : 3, 9 : 2})
2110
3 x_2 +2 x_9 <= 5 x_2 +2 x_3
2111
"""
2112
return LinearConstraint(self).__ge__(other)
2113
2114
def __hash__(self):
2115
r"""
2116
Defines a ``__hash__`` function
2117
2118
EXAMPLE::
2119
2120
sage: from sage.numerical.mip import LinearFunction
2121
sage: d = {}
2122
sage: d[LinearFunction({2 : 5, 3 : 2})] = 3
2123
"""
2124
return id(self)
2125
2126
def __eq__(self,other):
2127
r"""
2128
Defines the == operator
2129
2130
EXAMPLE::
2131
2132
sage: from sage.numerical.mip import LinearFunction
2133
sage: LinearFunction({2 : 5, 3 : 2}) == LinearFunction({2 : 3, 9 : 2})
2134
5 x_2 +2 x_3 = 3 x_2 +2 x_9
2135
"""
2136
return LinearConstraint(self).__eq__(other)
2137
2138
def Sum(L):
2139
r"""
2140
Efficiently computes the sum of a sequence of
2141
``LinearFunction`` elements
2142
2143
INPUT:
2144
2145
- ``L`` a list of ``LinearFunction`` instances.
2146
2147
.. NOTE::
2148
2149
The use of the regular ``sum`` function is not recommended as it is much less efficient than this one
2150
2151
EXAMPLES::
2152
2153
sage: p = MixedIntegerLinearProgram()
2154
sage: v = p.new_variable()
2155
2156
The following command::
2157
2158
sage: from sage.numerical.mip import Sum
2159
sage: s = Sum([v[i] for i in xrange(90)])
2160
2161
is much more efficient than::
2162
2163
sage: s = sum([v[i] for i in xrange(90)])
2164
2165
"""
2166
2167
d = {}
2168
for v in L:
2169
for (id,coeff) in v._f.iteritems():
2170
d[id] = coeff + d.get(id,0)
2171
2172
return LinearFunction(d)
2173
2174
2175
2176
class LinearConstraint:
2177
"""
2178
A class to represent formal Linear Constraints.
2179
2180
A Linear Constraint being an inequality between
2181
two linear functions, this class lets the user
2182
write ``LinearFunction1 <= LinearFunction2``
2183
to define the corresponding constraint, which
2184
can potentially involve several layers of such
2185
inequalities (``(A <= B <= C``), or even equalities
2186
like ``A == B``.
2187
2188
This class has no reason to be instanciated by the
2189
user, and is meant to be used by instances of
2190
MixedIntegerLinearProgram.
2191
2192
INPUT:
2193
2194
- ``c`` -- A ``LinearFunction``
2195
2196
EXAMPLE::
2197
2198
sage: p = MixedIntegerLinearProgram()
2199
sage: b = p.new_variable()
2200
sage: b[2]+2*b[3] <= b[8]-5
2201
x_0 +2 x_1 <= -5 +x_2
2202
"""
2203
2204
def __init__(self, c):
2205
r"""
2206
Constructor for ``LinearConstraint``
2207
2208
INPUT:
2209
2210
- ``c`` -- A linear function (see ``LinearFunction``).
2211
2212
EXAMPLE::
2213
2214
sage: p = MixedIntegerLinearProgram()
2215
sage: b = p.new_variable()
2216
sage: b[2]+2*b[3] <= b[8]-5
2217
x_0 +2 x_1 <= -5 +x_2
2218
"""
2219
self.equality = False
2220
self.constraints = []
2221
if isinstance(c, LinearFunction):
2222
self.constraints.append(c)
2223
else:
2224
self.constraints.append(LinearFunction(c))
2225
2226
def __repr__(self):
2227
r"""
2228
Returns a string representation of the constraint.
2229
2230
EXAMPLE::
2231
2232
sage: p = MixedIntegerLinearProgram()
2233
sage: b = p.new_variable()
2234
sage: print b[3] <= b[8] + 9
2235
x_0 <= 9 +x_1
2236
"""
2237
if self.equality:
2238
return str(self.constraints[0]) + " = " + str(self.constraints[1])
2239
else:
2240
first = True
2241
s = ""
2242
for c in self.constraints:
2243
s += (" <= " if not first else "") + c.__repr__()
2244
first = False
2245
return s
2246
2247
def __eq__(self,other):
2248
r"""
2249
Defines the == operator
2250
2251
EXAMPLE::
2252
2253
sage: p = MixedIntegerLinearProgram()
2254
sage: b = p.new_variable()
2255
sage: print b[3] == b[8] + 9
2256
x_0 = 9 +x_1
2257
"""
2258
if not isinstance(other, LinearConstraint):
2259
other = LinearConstraint(other)
2260
2261
if len(self.constraints) == 1 and len(other.constraints) == 1:
2262
self.constraints.extend(other.constraints)
2263
self.equality = True
2264
return self
2265
else:
2266
raise ValueError("Impossible to mix equality and inequality in the same equation")
2267
2268
def __le__(self,other):
2269
r"""
2270
Defines the <= operator
2271
2272
EXAMPLE::
2273
2274
sage: p = MixedIntegerLinearProgram()
2275
sage: b = p.new_variable()
2276
sage: print b[3] <= b[8] + 9
2277
x_0 <= 9 +x_1
2278
"""
2279
2280
if not isinstance(other, LinearConstraint):
2281
other = LinearConstraint(other)
2282
2283
if self.equality or other.equality:
2284
raise ValueError("Impossible to mix equality and inequality in the same equation")
2285
2286
self.constraints.extend(other.constraints)
2287
return self
2288
2289
def __lt__(self, other):
2290
r"""
2291
Prevents the use of the stricts operators ``<`` and ``>``
2292
2293
EXAMPLE::
2294
2295
sage: p = MixedIntegerLinearProgram()
2296
sage: b = p.new_variable()
2297
sage: print b[3] < b[8] + 9
2298
Traceback (most recent call last):
2299
...
2300
ValueError: The strict operators are not defined. Use <= and >= instead.
2301
sage: print b[3] > b[8] + 9
2302
Traceback (most recent call last):
2303
...
2304
ValueError: The strict operators are not defined. Use <= and >= instead.
2305
"""
2306
raise ValueError("The strict operators are not defined. Use <= and >= instead.")
2307
2308
__gt__ = __lt__
2309
2310
def __ge__(self,other):
2311
r"""
2312
Defines the >= operator
2313
2314
EXAMPLE::
2315
2316
sage: p = MixedIntegerLinearProgram()
2317
sage: b = p.new_variable()
2318
sage: print b[3] >= b[8] + 9
2319
9 +x_1 <= x_0
2320
"""
2321
if not isinstance(other, LinearConstraint):
2322
other = LinearConstraint(other)
2323
2324
if self.equality or other.equality:
2325
raise ValueError("Impossible to mix equality and inequality in the same equation")
2326
2327
self.constraints = other.constraints + self.constraints
2328
return self
2329
2330
2331