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