Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/numerical/backends/coin_backend.pyx
4079 views
1
"""
2
COIN Backend
3
4
AUTHORS:
5
6
- Nathann Cohen (2010-10): initial implementation
7
8
- John Perry (2012-03): major modifications to the interface in order to update
9
the Coin package to version 2.7.5
10
"""
11
12
##############################################################################
13
# Copyright (C) 2010 Nathann Cohen <[email protected]>
14
# Distributed under the terms of the GNU General Public License (GPL)
15
# The full text of the GPL is available at:
16
# http://www.gnu.org/licenses/
17
##############################################################################
18
19
20
from sage.numerical.mip import MIPSolverException
21
from copy import copy
22
23
cdef class CoinBackend(GenericBackend):
24
25
def __cinit__(self, maximization = True):
26
"""
27
Cython constructor
28
29
EXAMPLE::
30
31
sage: from sage.numerical.backends.generic_backend import get_solver
32
sage: p = get_solver(solver = "Coin") # optional - Coin
33
34
"""
35
36
# Coin devs seem to favor OsiClpSolverInterface
37
self.si = new OsiClpSolverInterface()
38
self.model = new CbcModel(self.si[0])
39
self.prob_name = None
40
self.row_names = []
41
self.col_names = []
42
43
if maximization:
44
self.set_sense(+1)
45
else:
46
self.set_sense(-1)
47
48
self.obj_constant_term = 0.0
49
50
def __dealloc__(self):
51
r"""
52
Destructor function
53
"""
54
del self.si
55
56
cpdef int add_variable(self, lower_bound=0.0, upper_bound=None, binary=False, continuous=False, integer=False, obj=0.0, name=None) except -1:
57
r"""
58
Add a variable.
59
60
This amounts to adding a new column to the matrix. By default,
61
the variable is both positive and real.
62
63
INPUT:
64
65
- ``lower_bound`` - the lower bound of the variable (default: 0)
66
67
- ``upper_bound`` - the upper bound of the variable (default: ``None``)
68
69
- ``binary`` - ``True`` if the variable is binary (default: ``False``).
70
71
- ``continuous`` - ``True`` if the variable is binary (default: ``True``).
72
73
- ``integer`` - ``True`` if the variable is binary (default: ``False``).
74
75
- ``obj`` - (optional) coefficient of this variable in the objective function (default: 0.0)
76
77
- ``name`` - an optional name for the newly added variable (default: ``None``).
78
79
OUTPUT: The index of the newly created variable
80
81
EXAMPLE::
82
83
sage: from sage.numerical.backends.generic_backend import get_solver
84
sage: p = get_solver(solver = "Coin") # optional - Coin
85
sage: p.ncols() # optional - Coin
86
0
87
sage: p.add_variable() # optional - Coin
88
0
89
sage: p.ncols() # optional - Coin
90
1
91
sage: p.add_variable(binary=True) # optional - Coin
92
1
93
sage: p.add_variable(lower_bound=-2.0, integer=True) # optional - Coin
94
2
95
sage: p.add_variable(continuous=True, integer=True) # optional - Coin
96
Traceback (most recent call last):
97
...
98
ValueError: ...
99
sage: p.add_variable(name='x',obj=1.0) # optional - Coin
100
3
101
sage: p.col_name(3) # optional - Coin
102
'x'
103
sage: p.objective_coefficient(3) # optional - Coin
104
1.0
105
"""
106
107
# for some reason, Cython is not accepting the line below, which appeare
108
#cdef int vtype = int(bool(binary)) + int(bool(continuous)) + int(bool(integer))
109
cdef int vtype = int(binary) + int(continuous) + int(integer)
110
if vtype == 0:
111
continuous = True
112
elif vtype != 1:
113
raise ValueError("Exactly one parameter of 'binary', 'integer' and 'continuous' must be 'True'.")
114
115
self.si.addCol(0, NULL, NULL, 0, self.si.getInfinity(), 0)
116
117
cdef int n
118
n = self.si.getNumCols() - 1
119
120
if lower_bound != 0.0:
121
self.variable_lower_bound(n, lower_bound)
122
if upper_bound is not None:
123
self.variable_upper_bound(n, upper_bound)
124
125
if binary:
126
self.set_variable_type(n,0)
127
elif integer:
128
self.set_variable_type(n,1)
129
130
if name:
131
self.col_names.append(name)
132
else:
133
self.col_names.append("")
134
135
if obj:
136
self.si.setObjCoeff(n, obj)
137
138
return n
139
140
cpdef int add_variables(self, int number, lower_bound=0.0, upper_bound=None, binary=False, continuous=False, integer=False, obj=0.0, names=None) except -1:
141
"""
142
Add ``number`` new variables.
143
144
This amounts to adding new columns to the matrix. By default,
145
the variables are both positive and real.
146
147
INPUT:
148
149
- ``n`` - the number of new variables (must be > 0)
150
151
- ``lower_bound`` - the lower bound of the variable (default: 0)
152
153
- ``upper_bound`` - the upper bound of the variable (default: ``None``)
154
155
- ``binary`` - ``True`` if the variable is binary (default: ``False``).
156
157
- ``continuous`` - ``True`` if the variable is binary (default: ``True``).
158
159
- ``integer`` - ``True`` if the variable is binary (default: ``False``).
160
161
- ``obj`` - (optional) coefficient of all variables in the objective function (default: 0.0)
162
163
- ``names`` - optional list of names (default: ``None``)
164
165
OUTPUT: The index of the variable created last.
166
167
EXAMPLE::
168
169
sage: from sage.numerical.backends.generic_backend import get_solver
170
sage: p = get_solver(solver = "Coin") # optional - Coin
171
sage: p.ncols() # optional - Coin
172
0
173
sage: p.add_variables(5) # optional - Coin
174
4
175
sage: p.ncols() # optional - Coin
176
5
177
sage: p.add_variables(2, lower_bound=-2.0, integer=True, names=['a','b']) # optional - Coin
178
6
179
sage: p.col_name(5) # optional - Coin
180
'a'
181
"""
182
#cdef int vtype = int(bool(binary)) + int(bool(continuous)) + int(bool(integer))
183
cdef int vtype = int(binary) + int(continuous) + int(integer)
184
if vtype == 0:
185
continuous = True
186
elif vtype != 1:
187
raise ValueError("Exactly one parameter of 'binary', 'integer' and 'continuous' must be 'True'.")
188
189
cdef int n
190
n = self.si.getNumCols()
191
192
cdef int i
193
194
for 0<= i < number:
195
196
self.si.addCol(0, NULL, NULL, 0, self.si.getInfinity(), 0)
197
198
if lower_bound != 0.0:
199
self.variable_lower_bound(n + i, lower_bound)
200
if upper_bound is not None:
201
self.variable_upper_bound(n + i, upper_bound)
202
203
if binary:
204
self.set_variable_type(n + i,0)
205
elif integer:
206
self.set_variable_type(n + i,1)
207
208
if obj:
209
self.si.setObjCoeff(n + i, obj)
210
211
if names != None:
212
for name in names:
213
self.col_names.append(name)
214
else:
215
self.col_names.extend(['' for i in range(number)])
216
217
return n + number -1
218
219
cpdef set_variable_type(self, int variable, int vtype):
220
r"""
221
Sets the type of a variable
222
223
INPUT:
224
225
- ``variable`` (integer) -- the variable's id
226
227
- ``vtype`` (integer) :
228
229
* 1 Integer
230
* 0 Binary
231
* -1 Real
232
233
EXAMPLE::
234
235
sage: from sage.numerical.backends.generic_backend import get_solver
236
sage: p = get_solver(solver = "Coin") # optional - Coin
237
sage: p.ncols() # optional - Coin
238
0
239
sage: p.add_variable() # optional - Coin
240
0
241
sage: p.set_variable_type(0,1) # optional - Coin
242
sage: p.is_variable_integer(0) # optional - Coin
243
True
244
"""
245
246
if vtype == 1:
247
self.si.setInteger(variable)
248
elif vtype == 0:
249
self.si.setColLower(variable, 0)
250
self.si.setInteger(variable)
251
self.si.setColUpper(variable, 1)
252
else:
253
self.si.setContinuous(variable)
254
255
cpdef set_sense(self, int sense):
256
r"""
257
Sets the direction (maximization/minimization).
258
259
INPUT:
260
261
- ``sense`` (integer) :
262
263
* +1 => Maximization
264
* -1 => Minimization
265
266
EXAMPLE::
267
268
sage: from sage.numerical.backends.generic_backend import get_solver
269
sage: p = get_solver(solver = "Coin") # optional - Coin
270
sage: p.is_maximization() # optional - Coin
271
True
272
sage: p.set_sense(-1) # optional - Coin
273
sage: p.is_maximization() # optional - Coin
274
False
275
"""
276
self.si.setObjSense(-sense)
277
278
cpdef objective_coefficient(self, int variable, coeff=None):
279
"""
280
Set or get the coefficient of a variable in the objective function
281
282
INPUT:
283
284
- ``variable`` (integer) -- the variable's id
285
286
- ``coeff`` (double) -- its coefficient or ``None`` for
287
reading (default: ``None``)
288
289
EXAMPLE::
290
291
sage: from sage.numerical.backends.generic_backend import get_solver
292
sage: p = get_solver(solver = "Coin") # optional -- Coin
293
sage: p.add_variable() # optional -- Coin
294
0
295
sage: p.objective_coefficient(0) # optional -- Coin
296
0.0
297
sage: p.objective_coefficient(0,2) # optional -- Coin
298
sage: p.objective_coefficient(0) # optional -- Coin
299
2.0
300
"""
301
if coeff is not None:
302
self.si.setObjCoeff(variable, coeff)
303
else:
304
return self.si.getObjCoefficients()[variable]
305
306
cpdef set_objective(self, list coeff, double d = 0.0):
307
r"""
308
Sets the objective function.
309
310
INPUT:
311
312
- ``coeff`` -- a list of real values, whose ith element is the
313
coefficient of the ith variable in the objective function.
314
315
- ``d`` (double) -- the constant term in the linear function (set to `0` by default)
316
317
EXAMPLE::
318
319
sage: from sage.numerical.backends.generic_backend import get_solver
320
sage: p = get_solver(solver = "Coin") # optional - Coin
321
sage: p.add_variables(5) # optional - Coin
322
4
323
sage: p.set_objective([1, 1, 2, 1, 3]) # optional - Coin
324
sage: map(lambda x :p.objective_coefficient(x), range(5)) # optional - Coin
325
[1.0, 1.0, 2.0, 1.0, 3.0]
326
327
Constants in the objective function are respected::
328
329
sage: p = MixedIntegerLinearProgram(solver='Coin') # optional - Coin
330
sage: x,y = p[0], p[1] # optional - Coin
331
sage: p.add_constraint(2*x + 3*y, max = 6) # optional - Coin
332
sage: p.add_constraint(3*x + 2*y, max = 6) # optional - Coin
333
sage: p.set_objective(x + y + 7) # optional - Coin
334
sage: p.set_integer(x); p.set_integer(y) # optional - Coin
335
sage: p.solve() # optional - Coin
336
9.0
337
"""
338
339
cdef int i
340
341
for i,v in enumerate(coeff):
342
self.si.setObjCoeff(i, v)
343
344
self.obj_constant_term = d
345
346
cpdef set_verbosity(self, int level):
347
r"""
348
Sets the log (verbosity) level
349
350
INPUT:
351
352
- ``level`` (integer) -- From 0 (no verbosity) to 3.
353
354
EXAMPLE::
355
356
sage: from sage.numerical.backends.generic_backend import get_solver
357
sage: p = get_solver(solver = "Coin") # optional - Coin
358
sage: p.set_verbosity(2) # optional - Coin
359
360
"""
361
362
self.model.setLogLevel(level)
363
364
cpdef remove_constraint(self, int i):
365
r"""
366
Remove a constraint from self.
367
368
INPUT:
369
370
- ``i`` -- index of the constraint to remove
371
372
EXAMPLE::
373
374
sage: p = MixedIntegerLinearProgram(solver='Coin') # optional - Coin
375
sage: x,y = p[0], p[1] # optional - Coin
376
sage: p.add_constraint(2*x + 3*y, max = 6) # optional - Coin
377
sage: p.add_constraint(3*x + 2*y, max = 6) # optional - Coin
378
sage: p.set_objective(x + y + 7) # optional - Coin
379
sage: p.set_integer(x); p.set_integer(y) # optional - Coin
380
sage: p.solve() # optional - Coin
381
9.0
382
sage: p.remove_constraint(0) # optional - Coin
383
sage: p.solve() # optional - Coin
384
10.0
385
sage: p.get_values([x,y]) # optional - Coin
386
[0.0, 3.0]
387
388
TESTS:
389
390
Removing fancy constraints does not make Sage crash::
391
392
sage: MixedIntegerLinearProgram(solver='Coin').remove_constraint(-2) # optional - Coin
393
Traceback (most recent call last):
394
...
395
ValueError: The constraint's index i must satisfy 0 <= i < number_of_constraints
396
"""
397
cdef int rows [1]
398
399
if i < 0 or i >= self.si.getNumRows():
400
raise ValueError("The constraint's index i must satisfy 0 <= i < number_of_constraints")
401
rows[0] = i
402
self.si.deleteRows(1,rows)
403
404
cpdef remove_constraints(self, constraints):
405
r"""
406
Remove several constraints.
407
408
INPUT:
409
410
- ``constraints`` -- an interable containing the indices of the rows to remove
411
412
EXAMPLE::
413
414
sage: p = MixedIntegerLinearProgram(solver='Coin') # optional - Coin
415
sage: x,y = p[0], p[1] # optional - Coin
416
sage: p.add_constraint(2*x + 3*y, max = 6) # optional - Coin
417
sage: p.add_constraint(3*x + 2*y, max = 6) # optional - Coin
418
sage: p.set_objective(x + y + 7) # optional - Coin
419
sage: p.set_integer(x); p.set_integer(y) # optional - Coin
420
sage: p.solve() # optional - Coin
421
9.0
422
sage: p.get_values(x) # optional - Coin
423
2.0...
424
sage: p.get_values(y) # optional - Coin
425
0.0...
426
sage: p.remove_constraints([0]) # optional - Coin
427
sage: p.solve() # optional - Coin
428
10.0
429
sage: p.get_values([x,y]) # optional - Coin
430
[0.0, 3.0]
431
432
TESTS:
433
434
Removing fancy constraints do not make Sage crash::
435
436
sage: MixedIntegerLinearProgram(solver='Coin').remove_constraints([0, -2]) # optional - Coin
437
Traceback (most recent call last):
438
...
439
ValueError: The constraint's index i must satisfy 0 <= i < number_of_constraints
440
"""
441
cdef int i, c
442
cdef int m = len(constraints)
443
cdef int * rows = <int *>sage_malloc(m * sizeof(int *))
444
cdef int nrows = self.si.getNumRows()
445
446
for i in xrange(m):
447
448
c = constraints[i]
449
if c < 0 or c >= nrows:
450
sage_free(rows)
451
raise ValueError("The constraint's index i must satisfy 0 <= i < number_of_constraints")
452
453
rows[i] = c
454
455
self.si.deleteRows(m,rows)
456
sage_free(rows)
457
458
cpdef add_linear_constraints(self, int number, lower_bound, upper_bound, names = None):
459
"""
460
Add ``'number`` linear constraints.
461
462
INPUT:
463
464
- ``number`` (integer) -- the number of constraints to add.
465
466
- ``lower_bound`` - a lower bound, either a real value or ``None``
467
468
- ``upper_bound`` - an upper bound, either a real value or ``None``
469
470
- ``names`` - an optional list of names (default: ``None``)
471
472
EXAMPLE::
473
474
sage: from sage.numerical.backends.generic_backend import get_solver
475
sage: p = get_solver(solver = "Coin") # optional - Coin
476
sage: p.add_variables(5) # optional - Coin
477
4
478
sage: p.add_linear_constraints(5, None, 2) # optional - Coin
479
sage: p.row(4) # optional - Coin
480
([], [])
481
sage: p.row_bounds(4) # optional - Coin
482
(None, 2.0)
483
sage: p.add_linear_constraints(2, None, 2, names=['foo','bar']) # optional - Coin
484
sage: p.row_name(6) # optional - Coin
485
'bar'
486
"""
487
488
cdef int i
489
for 0<= i<number:
490
self.add_linear_constraint([],lower_bound, upper_bound, name = (names[i] if names else None))
491
492
493
cpdef add_linear_constraint(self, coefficients, lower_bound, upper_bound, name = None):
494
"""
495
Add a linear constraint.
496
497
INPUT:
498
499
- ``coefficients`` an iterable with ``(c,v)`` pairs where ``c``
500
is a variable index (integer) and ``v`` is a value (real
501
value).
502
503
- ``lower_bound`` - a lower bound, either a real value or ``None``
504
505
- ``upper_bound`` - an upper bound, either a real value or ``None``
506
507
- ``name`` - an optional name for this row (default: ``None``)
508
509
EXAMPLE::
510
511
sage: from sage.numerical.backends.generic_backend import get_solver
512
sage: p = get_solver(solver = "Coin") # optional - Coin
513
sage: p.add_variables(5) # optional - Coin
514
4
515
sage: p.add_linear_constraint( zip(range(5), range(5)), 2.0, 2.0) # optional - Coin
516
sage: p.row(0) # optional - Coin
517
([0, 1, 2, 3, 4], [0.0, 1.0, 2.0, 3.0, 4.0])
518
sage: p.row_bounds(0) # optional - Coin
519
(2.0, 2.0)
520
sage: p.add_linear_constraint( zip(range(5), range(5)), 1.0, 1.0, name='foo') # optional - Coin
521
sage: p.row_name(1) # optional - Coin
522
'foo'
523
"""
524
if lower_bound is None and upper_bound is None:
525
raise ValueError("At least one of 'upper_bound' or 'lower_bound' must be set.")
526
527
cdef int i
528
cdef double c
529
cdef CoinPackedVector* row
530
row = new_CoinPackedVector();
531
532
533
for i,c in coefficients:
534
row.insert(i, c)
535
536
self.si.addRow (row[0],
537
lower_bound if lower_bound != None else -self.si.getInfinity(),
538
upper_bound if upper_bound != None else +self.si.getInfinity())
539
if name != None:
540
self.row_names.append(name)
541
else:
542
self.row_names.append("")
543
544
cpdef row(self, int index):
545
r"""
546
Returns a row
547
548
INPUT:
549
550
- ``index`` (integer) -- the constraint's id.
551
552
OUTPUT:
553
554
A pair ``(indices, coeffs)`` where ``indices`` lists the
555
entries whose coefficient is nonzero, and to which ``coeffs``
556
associates their coefficient on the model of the
557
``add_linear_constraint`` method.
558
559
EXAMPLE::
560
561
sage: from sage.numerical.backends.generic_backend import get_solver
562
sage: p = get_solver(solver = "Coin") # optional - Coin
563
sage: p.add_variables(5) # optional - Coin
564
4
565
sage: p.add_linear_constraint(zip(range(5), range(5)), 2, 2) # optional - Coin
566
sage: p.row(0) # optional - Coin
567
([0, 1, 2, 3, 4], [0.0, 1.0, 2.0, 3.0, 4.0])
568
sage: p.row_bounds(0) # optional - Coin
569
(2.0, 2.0)
570
"""
571
572
cdef list indices = []
573
cdef list values = []
574
cdef int * c_indices
575
cdef int i
576
cdef double * c_values
577
cdef CoinPackedMatrix * M = <CoinPackedMatrix *> self.si.getMatrixByRow()
578
cdef CoinShallowPackedVector V = <CoinShallowPackedVector> M.getVector(index)
579
cdef int n = V.getNumElements()
580
581
c_indices = <int*> V.getIndices()
582
c_values = <double*> V.getElements()
583
584
for 0<= i < n:
585
indices.append(c_indices[i])
586
values.append(c_values[i])
587
588
return (indices, values)
589
590
cpdef row_bounds(self, int i):
591
r"""
592
Returns the bounds of a specific constraint.
593
594
INPUT:
595
596
- ``index`` (integer) -- the constraint's id.
597
598
OUTPUT:
599
600
A pair ``(lower_bound, upper_bound)``. Each of them can be set
601
to ``None`` if the constraint is not bounded in the
602
corresponding direction, and is a real value otherwise.
603
604
EXAMPLE::
605
606
sage: from sage.numerical.backends.generic_backend import get_solver
607
sage: p = get_solver(solver = "Coin") # optional - Coin
608
sage: p.add_variables(5) # optional - Coin
609
4
610
sage: p.add_linear_constraint(zip(range(5), range(5)), 2, 2) # optional - Coin
611
sage: p.row(0) # optional - Coin
612
([0, 1, 2, 3, 4], [0.0, 1.0, 2.0, 3.0, 4.0])
613
sage: p.row_bounds(0) # optional - Coin
614
(2.0, 2.0)
615
"""
616
617
cdef double * ub
618
cdef double * lb
619
620
ub = <double*> self.si.getRowUpper()
621
lb = <double*> self.si.getRowLower()
622
623
return (lb[i] if lb[i] != - self.si.getInfinity() else None,
624
ub[i] if ub[i] != + self.si.getInfinity() else None)
625
626
cpdef col_bounds(self, int i):
627
r"""
628
Returns the bounds of a specific variable.
629
630
INPUT:
631
632
- ``index`` (integer) -- the variable's id.
633
634
OUTPUT:
635
636
A pair ``(lower_bound, upper_bound)``. Each of them can be set
637
to ``None`` if the variable is not bounded in the
638
corresponding direction, and is a real value otherwise.
639
640
EXAMPLE::
641
642
sage: from sage.numerical.backends.generic_backend import get_solver
643
sage: p = get_solver(solver = "Coin") # optional - Coin
644
sage: p.add_variable() # optional - Coin
645
0
646
sage: p.col_bounds(0) # optional - Coin
647
(0.0, None)
648
sage: p.variable_upper_bound(0, 5) # optional - Coin
649
sage: p.col_bounds(0) # optional - Coin
650
(0.0, 5.0)
651
"""
652
653
cdef double * ub
654
cdef double * lb
655
656
ub = <double*> self.si.getColUpper()
657
lb = <double*> self.si.getColLower()
658
659
return (lb[i] if lb[i] != - self.si.getInfinity() else None,
660
ub[i] if ub[i] != + self.si.getInfinity() else None)
661
662
cpdef add_col(self, list indices, list coeffs):
663
r"""
664
Adds a column.
665
666
INPUT:
667
668
- ``indices`` (list of integers) -- this list constains the
669
indices of the constraints in which the variable's
670
coefficient is nonzero
671
672
- ``coeffs`` (list of real values) -- associates a coefficient
673
to the variable in each of the constraints in which it
674
appears. Namely, the ith entry of ``coeffs`` corresponds to
675
the coefficient of the variable in the constraint
676
represented by the ith entry in ``indices``.
677
678
.. NOTE::
679
680
``indices`` and ``coeffs`` are expected to be of the same
681
length.
682
683
EXAMPLE::
684
685
sage: from sage.numerical.backends.generic_backend import get_solver
686
sage: p = get_solver(solver = "Coin") # optional - Coin
687
sage: p.ncols() # optional - Coin
688
0
689
sage: p.nrows() # optional - Coin
690
0
691
sage: p.add_linear_constraints(5, 0, None) # optional - Coin
692
sage: p.add_col(range(5), range(5)) # optional - Coin
693
sage: p.nrows() # optional - Coin
694
5
695
"""
696
697
cdef int n = len(indices)
698
cdef int * c_indices = <int*>sage_malloc(n*sizeof(int))
699
cdef double * c_values = <double*>sage_malloc(n*sizeof(double))
700
cdef int i
701
702
for 0<= i< n:
703
c_indices[i] = indices[i]
704
c_values[i] = coeffs[i]
705
706
self.si.addCol (1, c_indices, c_values, 0, self.si.getInfinity(), 0)
707
708
cpdef int solve(self) except -1:
709
r"""
710
Solves the problem.
711
712
.. NOTE::
713
714
This method raises ``MIPSolverException`` exceptions when
715
the solution can not be computed for any reason (none
716
exists, or the LP solver was not able to find it, etc...)
717
718
EXAMPLE::
719
720
sage: from sage.numerical.backends.generic_backend import get_solver
721
sage: p = get_solver(solver = "Coin") # optional - Coin
722
sage: p.add_linear_constraints(5, 0, None) # optional - Coin
723
sage: p.add_col(range(5), [1,2,3,4,5]) # optional - Coin
724
sage: p.solve() # optional - Coin
725
0
726
727
TESTS::
728
729
sage: from sage.numerical.backends.generic_backend import get_solver
730
sage: p = get_solver(solver = "Coin") # optional - Coin
731
sage: p.add_variable() # optional - Coin
732
0
733
sage: p.add_linear_constraint([(0, 1)], None, 4) # optional - Coin
734
sage: p.add_linear_constraint([(0, 1)], 6, None) # optional - Coin
735
sage: p.objective_coefficient(0,1) # optional - Coin
736
sage: p.solve() # optional - Coin
737
Traceback (most recent call last):
738
...
739
MIPSolverException: ...
740
"""
741
742
# set up the model
743
cdef OsiSolverInterface * si = self.si
744
745
cdef CbcModel * model
746
model = new CbcModel(si[0])
747
model.setLogLevel(self.model.logLevel())
748
749
# multithreading
750
import multiprocessing
751
model.setNumberThreads(multiprocessing.cpu_count())
752
753
model.branchAndBound()
754
755
if model.solver().isAbandoned():
756
raise MIPSolverException("CBC : The solver has abandoned!")
757
758
elif model.solver().isProvenPrimalInfeasible() or model.solver().isProvenDualInfeasible():
759
raise MIPSolverException("CBC : The problem or its dual has been proven infeasible!")
760
761
elif (model.solver().isPrimalObjectiveLimitReached() or model.solver().isDualObjectiveLimitReached()):
762
raise MIPSolverException("CBC : The objective limit has been reached for the problem or its dual!")
763
764
elif model.solver().isIterationLimitReached():
765
raise MIPSolverException("CBC : The iteration limit has been reached!")
766
767
elif not model.solver().isProvenOptimal():
768
raise MIPSolverException("CBC : Unknown error")
769
770
del self.model
771
self.model = model
772
773
cpdef double get_objective_value(self):
774
r"""
775
Returns the value of the objective function.
776
777
.. NOTE::
778
779
Has no meaning unless ``solve`` has been called before.
780
781
EXAMPLE::
782
783
sage: from sage.numerical.backends.generic_backend import get_solver
784
sage: p = get_solver(solver = "Coin") # optional - Coin
785
sage: p.add_variables(2) # optional - Coin
786
1
787
sage: p.add_linear_constraint([(0, 1), (1, 2)], None, 3) # optional - Coin
788
sage: p.set_objective([2, 5]) # optional - Coin
789
sage: p.solve() # optional - Coin
790
0
791
sage: p.get_objective_value() # optional - Coin
792
7.5
793
sage: p.get_variable_value(0) # optional - Coin
794
0.0
795
sage: p.get_variable_value(1) # optional - Coin
796
1.5
797
"""
798
return self.model.solver().getObjValue() + self.obj_constant_term
799
800
cpdef double get_variable_value(self, int variable):
801
r"""
802
Returns the value of a variable given by the solver.
803
804
.. NOTE::
805
806
Has no meaning unless ``solve`` has been called before.
807
808
EXAMPLE::
809
810
sage: from sage.numerical.backends.generic_backend import get_solver
811
sage: p = get_solver(solver = "Coin") # optional - Coin
812
sage: p.add_variables(2) # optional - Coin
813
1
814
sage: p.add_linear_constraint([(0, 1), (1, 2)], None, 3) # optional - Coin
815
sage: p.set_objective([2, 5]) # optional - Coin
816
sage: p.solve() # optional - Coin
817
0
818
sage: p.get_objective_value() # optional - Coin
819
7.5
820
sage: p.get_variable_value(0) # optional - Coin
821
0.0
822
sage: p.get_variable_value(1) # optional - Coin
823
1.5
824
"""
825
826
cdef double * solution
827
solution = <double*> self.model.solver().getColSolution()
828
return solution[variable]
829
830
cpdef int ncols(self):
831
r"""
832
Returns the number of columns/variables.
833
834
EXAMPLE::
835
836
sage: from sage.numerical.backends.generic_backend import get_solver
837
sage: p = get_solver(solver = "Coin") # optional - Coin
838
sage: p.ncols() # optional - Coin
839
0
840
sage: p.add_variables(2) # optional - Coin
841
1
842
sage: p.ncols() # optional - Coin
843
2
844
"""
845
846
return self.si.getNumCols()
847
848
cpdef int nrows(self):
849
r"""
850
Returns the number of rows/constraints.
851
852
EXAMPLE::
853
854
sage: from sage.numerical.backends.generic_backend import get_solver
855
sage: p = get_solver(solver = "Coin") # optional - Coin
856
sage: p.nrows() # optional - Coin
857
0
858
sage: p.add_linear_constraints(2, 2, None) # optional - Coin
859
sage: p.nrows() # optional - Coin
860
2
861
"""
862
return self.si.getNumRows()
863
864
865
cpdef bint is_variable_binary(self, int index):
866
r"""
867
Tests whether the given variable is of binary type.
868
869
INPUT:
870
871
- ``index`` (integer) -- the variable's id
872
873
EXAMPLE::
874
875
sage: from sage.numerical.backends.generic_backend import get_solver
876
sage: p = get_solver(solver = "Coin") # optional - Coin
877
sage: p.ncols() # optional - Coin
878
0
879
sage: p.add_variable() # optional - Coin
880
0
881
sage: p.set_variable_type(0,0) # optional - Coin
882
sage: p.is_variable_binary(0) # optional - Coin
883
True
884
885
"""
886
887
return (0 == self.si.isContinuous(index) and
888
self.variable_lower_bound(index) == 0 and
889
self.variable_upper_bound(index) == 1)
890
891
cpdef bint is_variable_integer(self, int index):
892
r"""
893
Tests whether the given variable is of integer type.
894
895
INPUT:
896
897
- ``index`` (integer) -- the variable's id
898
899
EXAMPLE::
900
901
sage: from sage.numerical.backends.generic_backend import get_solver
902
sage: p = get_solver(solver = "Coin") # optional - Coin
903
sage: p.ncols() # optional - Coin
904
0
905
sage: p.add_variable() # optional - Coin
906
0
907
sage: p.set_variable_type(0,1) # optional - Coin
908
sage: p.is_variable_integer(0) # optional - Coin
909
True
910
"""
911
return (0 == self.si.isContinuous(index) and
912
(self.variable_lower_bound(index) != 0 or
913
self.variable_upper_bound(index) != 1))
914
915
cpdef bint is_variable_continuous(self, int index):
916
r"""
917
Tests whether the given variable is of continuous/real type.
918
919
INPUT:
920
921
- ``index`` (integer) -- the variable's id
922
923
EXAMPLE::
924
925
sage: from sage.numerical.backends.generic_backend import get_solver
926
sage: p = get_solver(solver = "Coin") # optional - Coin
927
sage: p.ncols() # optional - Coin
928
0
929
sage: p.add_variable() # optional - Coin
930
0
931
sage: p.is_variable_continuous(0) # optional - Coin
932
True
933
sage: p.set_variable_type(0,1) # optional - Coin
934
sage: p.is_variable_continuous(0) # optional - Coin
935
False
936
937
"""
938
return 1 == self.si.isContinuous(index)
939
940
941
cpdef bint is_maximization(self):
942
r"""
943
Tests whether the problem is a maximization
944
945
EXAMPLE::
946
947
sage: from sage.numerical.backends.generic_backend import get_solver
948
sage: p = get_solver(solver = "Coin") # optional - Coin
949
sage: p.is_maximization() # optional - Coin
950
True
951
sage: p.set_sense(-1) # optional - Coin
952
sage: p.is_maximization() # optional - Coin
953
False
954
"""
955
956
return self.si.getObjSense() == -1
957
958
cpdef variable_upper_bound(self, int index, value = False):
959
r"""
960
Returns or defines the upper bound on a variable
961
962
INPUT:
963
964
- ``index`` (integer) -- the variable's id
965
966
- ``value`` -- real value, or ``None`` to mean that the
967
variable has not upper bound. When set to ``False``
968
(default), the method returns the current value.
969
970
EXAMPLE::
971
972
sage: from sage.numerical.backends.generic_backend import get_solver
973
sage: p = get_solver(solver = "Coin") # optional - Coin
974
sage: p.add_variable() # optional - Coin
975
0
976
sage: p.col_bounds(0) # optional - Coin
977
(0.0, None)
978
sage: p.variable_upper_bound(0, 5) # optional - Coin
979
sage: p.col_bounds(0) # optional - Coin
980
(0.0, 5.0)
981
"""
982
cdef double * ub
983
984
if value == False:
985
ub = <double*> self.si.getColUpper()
986
return ub[index] if ub[index] != + self.si.getInfinity() else None
987
else:
988
self.si.setColUpper(index, value if value is not None else +self.si.getInfinity())
989
990
cpdef variable_lower_bound(self, int index, value = False):
991
r"""
992
Returns or defines the lower bound on a variable
993
994
INPUT:
995
996
- ``index`` (integer) -- the variable's id
997
998
- ``value`` -- real value, or ``None`` to mean that the
999
variable has not lower bound. When set to ``False``
1000
(default), the method returns the current value.
1001
1002
EXAMPLE::
1003
1004
sage: from sage.numerical.backends.generic_backend import get_solver
1005
sage: p = get_solver(solver = "Coin") # optional - Coin
1006
sage: p.add_variable() # optional - Coin
1007
0
1008
sage: p.col_bounds(0) # optional - Coin
1009
(0.0, None)
1010
sage: p.variable_lower_bound(0, 5) # optional - Coin
1011
sage: p.col_bounds(0) # optional - Coin
1012
(5.0, None)
1013
"""
1014
cdef double * lb
1015
1016
if value == False:
1017
lb = <double*> self.si.getColLower()
1018
return lb[index] if lb[index] != - self.si.getInfinity() else None
1019
else:
1020
self.si.setColLower(index, value if value is not None else -self.si.getInfinity())
1021
1022
cpdef write_mps(self, char * filename, int modern):
1023
r"""
1024
Writes the problem to a .mps file
1025
1026
INPUT:
1027
1028
- ``filename`` (string)
1029
1030
EXAMPLE::
1031
1032
sage: from sage.numerical.backends.generic_backend import get_solver
1033
sage: p = get_solver(solver = "Coin") # optional - Coin
1034
sage: p.add_variables(2) # optional - Coin
1035
1
1036
sage: p.add_linear_constraint([(0, 1), (1, 2)], None, 3) # optional - Coin
1037
sage: p.set_objective([2, 5]) # optional - Coin
1038
sage: p.write_mps(SAGE_TMP+"/lp_problem.mps", 0) # optional - Coin
1039
"""
1040
1041
cdef char * mps = "mps"
1042
self.si.writeMps(filename, mps, -1 if self.is_maximization() else 1)
1043
1044
cpdef write_lp(self, char * filename):
1045
r"""
1046
Writes the problem to a .lp file
1047
1048
INPUT:
1049
1050
- ``filename`` (string)
1051
1052
EXAMPLE::
1053
1054
sage: from sage.numerical.backends.generic_backend import get_solver
1055
sage: p = get_solver(solver = "Coin") # optional - Coin
1056
sage: p.add_variables(2) # optional - Coin
1057
1
1058
sage: p.add_linear_constraint([(0, 1), (1, 2)], None, 3) # optional - Coin
1059
sage: p.set_objective([2, 5]) # optional - Coin
1060
sage: p.write_lp(SAGE_TMP+"/lp_problem.lp") # optional - Coin
1061
"""
1062
1063
cdef char * lp = "lp"
1064
self.si.writeLp(filename, lp, 0.00001, 10, 5, -1 if self.is_maximization() else 1, 1)
1065
1066
cpdef problem_name(self, char * name = NULL):
1067
r"""
1068
Returns or defines the problem's name
1069
1070
INPUT:
1071
1072
- ``name`` (``char *``) -- the problem's name. When set to
1073
``NULL`` (default), the method returns the problem's name.
1074
1075
EXAMPLE::
1076
1077
sage: from sage.numerical.backends.generic_backend import get_solver
1078
sage: p = get_solver(solver = "Coin") # optional - Coin
1079
sage: p.problem_name("There once was a french fry") # optional - Coin
1080
sage: print p.problem_name() # optional - Coin
1081
There once was a french fry
1082
"""
1083
if name == NULL:
1084
if self.prob_name != None:
1085
return self.prob_name
1086
else:
1087
return ""
1088
else:
1089
self.prob_name = name
1090
1091
1092
cpdef row_name(self, int index):
1093
r"""
1094
Returns the ``index`` th row name
1095
1096
INPUT:
1097
1098
- ``index`` (integer) -- the row's id
1099
1100
EXAMPLE::
1101
1102
sage: from sage.numerical.backends.generic_backend import get_solver
1103
sage: p = get_solver(solver = "Coin") # optional - Coin
1104
sage: p.add_linear_constraints(1, 2, None, names=['Empty constraint 1']) # optional - Coin
1105
sage: print p.row_name(0) # optional - Coin
1106
Empty constraint 1
1107
"""
1108
if self.row_names != None:
1109
return self.row_names[index]
1110
else:
1111
return ""
1112
1113
cpdef col_name(self, int index):
1114
r"""
1115
Returns the ``index`` th col name
1116
1117
INPUT:
1118
1119
- ``index`` (integer) -- the col's id
1120
1121
EXAMPLE::
1122
1123
sage: from sage.numerical.backends.generic_backend import get_solver
1124
sage: p = get_solver(solver = "Coin") # optional - Coin
1125
sage: p.add_variable(name='I am a variable') # optional - Coin
1126
0
1127
sage: print p.col_name(0) # optional - Coin
1128
I am a variable
1129
"""
1130
if self.col_names != None:
1131
return self.col_names[index]
1132
else:
1133
return ""
1134
1135
cpdef CoinBackend copy(self):
1136
"""
1137
Returns a copy of self.
1138
1139
EXAMPLE::
1140
1141
sage: from sage.numerical.backends.generic_backend import get_solver
1142
sage: p = MixedIntegerLinearProgram(solver = "Coin") # optional - Coin
1143
sage: b = p.new_variable() # optional - Coin
1144
sage: p.add_constraint(b[1] + b[2] <= 6) # optional - Coin
1145
sage: p.set_objective(b[1] + b[2]) # optional - Coin
1146
sage: copy(p).solve() # optional - Coin
1147
6.0
1148
"""
1149
# create new backend
1150
cdef CoinBackend p = CoinBackend(maximization = (1 if self.is_maximization() else -1))
1151
1152
# replace solver with copy of self's solver
1153
del p.si
1154
p.si = self.si.clone(1)
1155
p.row_names = copy(self.row_names)
1156
p.col_names = copy(self.col_names)
1157
p.obj_constant_term = self.obj_constant_term
1158
# Maybe I should copy this, not sure -- seems complicated, though
1159
p.prob_name = self.prob_name
1160
1161
return p
1162
1163