passagemath-glpk 10.6.46__cp314-cp314t-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of passagemath-glpk might be problematic. Click here for more details.
- passagemath_glpk/__init__.py +3 -0
- passagemath_glpk-10.6.46.dist-info/DELVEWHEEL +2 -0
- passagemath_glpk-10.6.46.dist-info/METADATA +103 -0
- passagemath_glpk-10.6.46.dist-info/RECORD +31 -0
- passagemath_glpk-10.6.46.dist-info/WHEEL +5 -0
- passagemath_glpk-10.6.46.dist-info/top_level.txt +3 -0
- passagemath_glpk.libs/libgcc_s_seh-1-8ac28fe21ad5783af7630f2f0c8f554d.dll +0 -0
- passagemath_glpk.libs/libglpk-40-a2b083517ab17d71a64901ccf3d73f9d.dll +0 -0
- passagemath_glpk.libs/libgmp-10-23decc023ff052bc2d748f1d5bb87892.dll +0 -0
- passagemath_glpk.libs/libwinpthread-1-e271f374468d584905afcdf7da96a6ad.dll +0 -0
- sage/all__sagemath_glpk.py +11 -0
- sage/libs/all__sagemath_glpk.py +1 -0
- sage/libs/glpk/__init__.py +1 -0
- sage/libs/glpk/constants.pxd +94 -0
- sage/libs/glpk/env.pxd +14 -0
- sage/libs/glpk/graph.pxd +43 -0
- sage/libs/glpk/lp.pxd +95 -0
- sage/libs/glpk/types.pxd +87 -0
- sage/numerical/all__sagemath_glpk.py +1 -0
- sage/numerical/backends/all__sagemath_glpk.py +1 -0
- sage/numerical/backends/glpk_backend.cp314t-win_amd64.pyd +0 -0
- sage/numerical/backends/glpk_backend.pxd +41 -0
- sage/numerical/backends/glpk_backend.pyx +3359 -0
- sage/numerical/backends/glpk_backend_test.py +13 -0
- sage/numerical/backends/glpk_exact_backend.cp314t-win_amd64.pyd +0 -0
- sage/numerical/backends/glpk_exact_backend.pxd +17 -0
- sage/numerical/backends/glpk_exact_backend.pyx +190 -0
- sage/numerical/backends/glpk_exact_backend_test.py +12 -0
- sage/numerical/backends/glpk_graph_backend.cp314t-win_amd64.pyd +0 -0
- sage/numerical/backends/glpk_graph_backend.pxd +56 -0
- sage/numerical/backends/glpk_graph_backend.pyx +1346 -0
|
@@ -0,0 +1,3359 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-glpk
|
|
2
|
+
"""
|
|
3
|
+
GLPK Backend
|
|
4
|
+
|
|
5
|
+
AUTHORS:
|
|
6
|
+
|
|
7
|
+
- Nathann Cohen (2010-10): initial implementation
|
|
8
|
+
- John Perry (2012-01): glp_simplex preprocessing
|
|
9
|
+
- John Perry and Raniere Gaia Silva (2012-03): solver parameters
|
|
10
|
+
- Christian Kuper (2012-10): Additions for sensitivity analysis
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
# ****************************************************************************
|
|
14
|
+
# Copyright (C) 2010 Nathann Cohen <nathann.cohen@gmail.com>
|
|
15
|
+
#
|
|
16
|
+
# This program is free software: you can redistribute it and/or modify
|
|
17
|
+
# it under the terms of the GNU General Public License as published by
|
|
18
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
19
|
+
# (at your option) any later version.
|
|
20
|
+
# https://www.gnu.org/licenses/
|
|
21
|
+
# ****************************************************************************
|
|
22
|
+
|
|
23
|
+
from libc.float cimport DBL_MAX
|
|
24
|
+
from libc.limits cimport INT_MAX
|
|
25
|
+
from cysignals.memory cimport sig_malloc, sig_free
|
|
26
|
+
from cysignals.signals cimport sig_on, sig_off
|
|
27
|
+
from memory_allocator cimport MemoryAllocator
|
|
28
|
+
|
|
29
|
+
from sage.cpython.string cimport char_to_str, str_to_bytes
|
|
30
|
+
from sage.cpython.string import FS_ENCODING
|
|
31
|
+
from sage.numerical.mip import MIPSolverException
|
|
32
|
+
from sage.libs.glpk.constants cimport *
|
|
33
|
+
from sage.libs.glpk.lp cimport *
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
cdef class GLPKBackend(GenericBackend):
|
|
37
|
+
"""
|
|
38
|
+
MIP Backend that uses the GLPK solver.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
def __cinit__(self, maximization=True):
|
|
42
|
+
"""
|
|
43
|
+
Constructor.
|
|
44
|
+
|
|
45
|
+
EXAMPLES::
|
|
46
|
+
|
|
47
|
+
sage: p = MixedIntegerLinearProgram(solver='GLPK')
|
|
48
|
+
"""
|
|
49
|
+
self.lp = glp_create_prob()
|
|
50
|
+
self.simplex_or_intopt = glp_simplex_then_intopt
|
|
51
|
+
self.smcp = <glp_smcp* > sig_malloc(sizeof(glp_smcp))
|
|
52
|
+
glp_init_smcp(self.smcp)
|
|
53
|
+
self.iocp = <glp_iocp* > sig_malloc(sizeof(glp_iocp))
|
|
54
|
+
glp_init_iocp(self.iocp)
|
|
55
|
+
|
|
56
|
+
self.iocp.cb_func = glp_callback # callback function
|
|
57
|
+
self.iocp.cb_info = <void *> &(self.search_tree_data) # callback data
|
|
58
|
+
self.iocp.presolve = GLP_ON
|
|
59
|
+
self.set_verbosity(0)
|
|
60
|
+
self.obj_constant_term = 0.0
|
|
61
|
+
|
|
62
|
+
if maximization:
|
|
63
|
+
self.set_sense(+1)
|
|
64
|
+
else:
|
|
65
|
+
self.set_sense(-1)
|
|
66
|
+
|
|
67
|
+
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:
|
|
68
|
+
"""
|
|
69
|
+
Add a variable.
|
|
70
|
+
|
|
71
|
+
This amounts to adding a new column to the matrix. By default,
|
|
72
|
+
the variable is both positive, real and the coefficient in the
|
|
73
|
+
objective function is 0.0.
|
|
74
|
+
|
|
75
|
+
INPUT:
|
|
76
|
+
|
|
77
|
+
- ``lower_bound`` -- the lower bound of the variable (default: 0)
|
|
78
|
+
|
|
79
|
+
- ``upper_bound`` -- the upper bound of the variable (default: ``None``)
|
|
80
|
+
|
|
81
|
+
- ``binary`` -- ``True`` if the variable is binary (default: ``False``)
|
|
82
|
+
|
|
83
|
+
- ``continuous`` -- ``True`` if the variable is continuous (default: ``True``)
|
|
84
|
+
|
|
85
|
+
- ``integer`` -- ``True`` if the variable is integral (default: ``False``)
|
|
86
|
+
|
|
87
|
+
- ``obj`` -- (optional) coefficient of this variable in the objective function (default: 0.0)
|
|
88
|
+
|
|
89
|
+
- ``name`` -- an optional name for the newly added variable (default: ``None``)
|
|
90
|
+
|
|
91
|
+
OUTPUT: the index of the newly created variable
|
|
92
|
+
|
|
93
|
+
EXAMPLES::
|
|
94
|
+
|
|
95
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
96
|
+
sage: p = get_solver(solver = "GLPK")
|
|
97
|
+
sage: p.ncols()
|
|
98
|
+
0
|
|
99
|
+
sage: p.add_variable()
|
|
100
|
+
0
|
|
101
|
+
sage: p.ncols()
|
|
102
|
+
1
|
|
103
|
+
sage: p.add_variable(binary=True)
|
|
104
|
+
1
|
|
105
|
+
sage: p.add_variable(lower_bound=-2.0, integer=True)
|
|
106
|
+
2
|
|
107
|
+
sage: p.add_variable(continuous=True, integer=True)
|
|
108
|
+
Traceback (most recent call last):
|
|
109
|
+
...
|
|
110
|
+
ValueError: ...
|
|
111
|
+
sage: p.add_variable(name='x', obj=1.0)
|
|
112
|
+
3
|
|
113
|
+
sage: p.col_name(3)
|
|
114
|
+
'x'
|
|
115
|
+
sage: p.objective_coefficient(3)
|
|
116
|
+
1.0
|
|
117
|
+
"""
|
|
118
|
+
cdef int vtype = int(bool(binary)) + int(bool(continuous)) + int(bool(integer))
|
|
119
|
+
if vtype == 0:
|
|
120
|
+
continuous = True
|
|
121
|
+
elif vtype != 1:
|
|
122
|
+
raise ValueError("Exactly one parameter of 'binary', 'integer' and 'continuous' must be 'True'.")
|
|
123
|
+
|
|
124
|
+
glp_add_cols(self.lp, 1)
|
|
125
|
+
cdef int n_var = glp_get_num_cols(self.lp)
|
|
126
|
+
|
|
127
|
+
self.variable_lower_bound(n_var - 1, lower_bound)
|
|
128
|
+
self.variable_upper_bound(n_var - 1, upper_bound)
|
|
129
|
+
|
|
130
|
+
if continuous:
|
|
131
|
+
glp_set_col_kind(self.lp, n_var, GLP_CV)
|
|
132
|
+
elif binary:
|
|
133
|
+
glp_set_col_kind(self.lp, n_var, GLP_BV)
|
|
134
|
+
elif integer:
|
|
135
|
+
glp_set_col_kind(self.lp, n_var, GLP_IV)
|
|
136
|
+
|
|
137
|
+
if name is not None:
|
|
138
|
+
glp_set_col_name(self.lp, n_var, str_to_bytes(name))
|
|
139
|
+
|
|
140
|
+
if obj:
|
|
141
|
+
self.objective_coefficient(n_var - 1, obj)
|
|
142
|
+
|
|
143
|
+
return n_var - 1
|
|
144
|
+
|
|
145
|
+
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:
|
|
146
|
+
"""
|
|
147
|
+
Add ``number`` new variables.
|
|
148
|
+
|
|
149
|
+
This amounts to adding new columns to the matrix. By default,
|
|
150
|
+
the variables are both positive, real and their coefficient in
|
|
151
|
+
the objective function is 0.0.
|
|
152
|
+
|
|
153
|
+
INPUT:
|
|
154
|
+
|
|
155
|
+
- ``n`` -- the number of new variables (must be > 0)
|
|
156
|
+
|
|
157
|
+
- ``lower_bound`` -- the lower bound of the variable (default: 0)
|
|
158
|
+
|
|
159
|
+
- ``upper_bound`` -- the upper bound of the variable (default: ``None``)
|
|
160
|
+
|
|
161
|
+
- ``binary`` -- ``True`` if the variable is binary (default: ``False``)
|
|
162
|
+
|
|
163
|
+
- ``continuous`` -- ``True`` if the variable is binary (default: ``True``)
|
|
164
|
+
|
|
165
|
+
- ``integer`` -- ``True`` if the variable is binary (default: ``False``)
|
|
166
|
+
|
|
167
|
+
- ``obj`` -- coefficient of all variables in the objective function (default: 0.0)
|
|
168
|
+
|
|
169
|
+
- ``names`` -- list of names (default: ``None``)
|
|
170
|
+
|
|
171
|
+
OUTPUT: the index of the variable created last
|
|
172
|
+
|
|
173
|
+
EXAMPLES::
|
|
174
|
+
|
|
175
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
176
|
+
sage: p = get_solver(solver = "GLPK")
|
|
177
|
+
sage: p.ncols()
|
|
178
|
+
0
|
|
179
|
+
sage: p.add_variables(5)
|
|
180
|
+
4
|
|
181
|
+
sage: p.ncols()
|
|
182
|
+
5
|
|
183
|
+
sage: p.add_variables(2, lower_bound=-2.0, integer=True, obj=42.0, names=['a','b'])
|
|
184
|
+
6
|
|
185
|
+
|
|
186
|
+
TESTS:
|
|
187
|
+
|
|
188
|
+
Check that arguments are used::
|
|
189
|
+
|
|
190
|
+
sage: p.col_bounds(5) # tol 1e-8
|
|
191
|
+
(-2.0, None)
|
|
192
|
+
sage: p.is_variable_integer(5)
|
|
193
|
+
True
|
|
194
|
+
sage: p.col_name(5)
|
|
195
|
+
'a'
|
|
196
|
+
sage: p.objective_coefficient(5)
|
|
197
|
+
42.0
|
|
198
|
+
"""
|
|
199
|
+
cdef int vtype = int(bool(binary)) + int(bool(continuous)) + int(bool(integer))
|
|
200
|
+
if vtype == 0:
|
|
201
|
+
continuous = True
|
|
202
|
+
elif vtype != 1:
|
|
203
|
+
raise ValueError("Exactly one parameter of 'binary', 'integer' and 'continuous' must be 'True'.")
|
|
204
|
+
|
|
205
|
+
glp_add_cols(self.lp, number)
|
|
206
|
+
|
|
207
|
+
cdef int n_var
|
|
208
|
+
n_var = glp_get_num_cols(self.lp)
|
|
209
|
+
|
|
210
|
+
cdef int i
|
|
211
|
+
|
|
212
|
+
for 0<= i < number:
|
|
213
|
+
self.variable_lower_bound(n_var - i - 1, lower_bound)
|
|
214
|
+
self.variable_upper_bound(n_var - i - 1, upper_bound)
|
|
215
|
+
if continuous:
|
|
216
|
+
glp_set_col_kind(self.lp, n_var - i, GLP_CV)
|
|
217
|
+
elif binary:
|
|
218
|
+
glp_set_col_kind(self.lp, n_var - i, GLP_BV)
|
|
219
|
+
elif integer:
|
|
220
|
+
glp_set_col_kind(self.lp, n_var - i, GLP_IV)
|
|
221
|
+
|
|
222
|
+
if obj:
|
|
223
|
+
self.objective_coefficient(n_var - i - 1, obj)
|
|
224
|
+
|
|
225
|
+
if names is not None:
|
|
226
|
+
glp_set_col_name(self.lp, n_var - i,
|
|
227
|
+
str_to_bytes(names[number - i - 1]))
|
|
228
|
+
|
|
229
|
+
return n_var - 1
|
|
230
|
+
|
|
231
|
+
cpdef set_variable_type(self, int variable, int vtype):
|
|
232
|
+
"""
|
|
233
|
+
Set the type of a variable.
|
|
234
|
+
|
|
235
|
+
INPUT:
|
|
236
|
+
|
|
237
|
+
- ``variable`` -- integer; the variable's id
|
|
238
|
+
|
|
239
|
+
- ``vtype`` -- integer:
|
|
240
|
+
|
|
241
|
+
* 1 Integer
|
|
242
|
+
* 0 Binary
|
|
243
|
+
* -1 Real
|
|
244
|
+
|
|
245
|
+
EXAMPLES::
|
|
246
|
+
|
|
247
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
248
|
+
sage: p = get_solver(solver = "GLPK")
|
|
249
|
+
sage: p.ncols()
|
|
250
|
+
0
|
|
251
|
+
sage: p.add_variable()
|
|
252
|
+
0
|
|
253
|
+
sage: p.set_variable_type(0,1)
|
|
254
|
+
sage: p.is_variable_integer(0)
|
|
255
|
+
True
|
|
256
|
+
|
|
257
|
+
TESTS:
|
|
258
|
+
|
|
259
|
+
We sanity check the input that will be passed to GLPK::
|
|
260
|
+
|
|
261
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
262
|
+
sage: p = get_solver(solver='GLPK')
|
|
263
|
+
sage: p.set_variable_type(2,0)
|
|
264
|
+
Traceback (most recent call last):
|
|
265
|
+
...
|
|
266
|
+
ValueError: invalid variable index 2
|
|
267
|
+
"""
|
|
268
|
+
if variable < 0 or variable > (self.ncols() - 1):
|
|
269
|
+
raise ValueError("invalid variable index %d" % variable)
|
|
270
|
+
|
|
271
|
+
if vtype==1:
|
|
272
|
+
glp_set_col_kind(self.lp, variable+1, GLP_IV)
|
|
273
|
+
|
|
274
|
+
elif vtype==0:
|
|
275
|
+
glp_set_col_kind(self.lp, variable+1, GLP_BV)
|
|
276
|
+
|
|
277
|
+
else:
|
|
278
|
+
glp_set_col_kind(self.lp, variable+1, GLP_CV)
|
|
279
|
+
|
|
280
|
+
cpdef set_sense(self, int sense):
|
|
281
|
+
"""
|
|
282
|
+
Set the direction (maximization/minimization).
|
|
283
|
+
|
|
284
|
+
INPUT:
|
|
285
|
+
|
|
286
|
+
- ``sense`` -- integer:
|
|
287
|
+
|
|
288
|
+
* +1 => Maximization
|
|
289
|
+
* -1 => Minimization
|
|
290
|
+
|
|
291
|
+
EXAMPLES::
|
|
292
|
+
|
|
293
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
294
|
+
sage: p = get_solver(solver = "GLPK")
|
|
295
|
+
sage: p.is_maximization()
|
|
296
|
+
True
|
|
297
|
+
sage: p.set_sense(-1)
|
|
298
|
+
sage: p.is_maximization()
|
|
299
|
+
False
|
|
300
|
+
"""
|
|
301
|
+
if sense == 1:
|
|
302
|
+
glp_set_obj_dir(self.lp, GLP_MAX)
|
|
303
|
+
else:
|
|
304
|
+
glp_set_obj_dir(self.lp, GLP_MIN)
|
|
305
|
+
|
|
306
|
+
cpdef objective_coefficient(self, int variable, coeff=None):
|
|
307
|
+
"""
|
|
308
|
+
Set or get the coefficient of a variable in the objective function.
|
|
309
|
+
|
|
310
|
+
INPUT:
|
|
311
|
+
|
|
312
|
+
- ``variable`` -- integer; the variable's id
|
|
313
|
+
|
|
314
|
+
- ``coeff`` -- double; its coefficient or ``None`` for
|
|
315
|
+
reading (default: ``None``)
|
|
316
|
+
|
|
317
|
+
EXAMPLES::
|
|
318
|
+
|
|
319
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
320
|
+
sage: p = get_solver(solver = "GLPK")
|
|
321
|
+
sage: p.add_variable()
|
|
322
|
+
0
|
|
323
|
+
sage: p.objective_coefficient(0)
|
|
324
|
+
0.0
|
|
325
|
+
sage: p.objective_coefficient(0,2)
|
|
326
|
+
sage: p.objective_coefficient(0)
|
|
327
|
+
2.0
|
|
328
|
+
|
|
329
|
+
TESTS:
|
|
330
|
+
|
|
331
|
+
We sanity check the input that will be passed to GLPK::
|
|
332
|
+
|
|
333
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
334
|
+
sage: p = get_solver(solver='GLPK')
|
|
335
|
+
sage: p.objective_coefficient(2)
|
|
336
|
+
Traceback (most recent call last):
|
|
337
|
+
...
|
|
338
|
+
ValueError: invalid variable index 2
|
|
339
|
+
"""
|
|
340
|
+
if variable < 0 or variable > (self.ncols() - 1):
|
|
341
|
+
raise ValueError("invalid variable index %d" % variable)
|
|
342
|
+
|
|
343
|
+
if coeff is None:
|
|
344
|
+
return glp_get_obj_coef(self.lp, variable + 1)
|
|
345
|
+
else:
|
|
346
|
+
glp_set_obj_coef(self.lp, variable + 1, coeff)
|
|
347
|
+
|
|
348
|
+
cpdef problem_name(self, name=None):
|
|
349
|
+
"""
|
|
350
|
+
Return or define the problem's name.
|
|
351
|
+
|
|
352
|
+
INPUT:
|
|
353
|
+
|
|
354
|
+
- ``name`` -- string; the problem's name. When set to
|
|
355
|
+
``None`` (default), the method returns the problem's name.
|
|
356
|
+
|
|
357
|
+
EXAMPLES::
|
|
358
|
+
|
|
359
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
360
|
+
sage: p = get_solver(solver = "GLPK")
|
|
361
|
+
sage: p.problem_name("There once was a french fry")
|
|
362
|
+
sage: print(p.problem_name())
|
|
363
|
+
There once was a french fry
|
|
364
|
+
"""
|
|
365
|
+
cdef char * n
|
|
366
|
+
|
|
367
|
+
if name is None:
|
|
368
|
+
n = <char *> glp_get_prob_name(self.lp)
|
|
369
|
+
if n == NULL:
|
|
370
|
+
return ""
|
|
371
|
+
else:
|
|
372
|
+
return char_to_str(n)
|
|
373
|
+
|
|
374
|
+
else:
|
|
375
|
+
name = str_to_bytes(name)
|
|
376
|
+
if len(name) > 255:
|
|
377
|
+
raise ValueError("Problem name for GLPK must not be longer than 255 characters.")
|
|
378
|
+
glp_set_prob_name(self.lp, name)
|
|
379
|
+
|
|
380
|
+
cpdef set_objective(self, list coeff, d=0.0):
|
|
381
|
+
"""
|
|
382
|
+
Set the objective function.
|
|
383
|
+
|
|
384
|
+
INPUT:
|
|
385
|
+
|
|
386
|
+
- ``coeff`` -- list of real values, whose i-th element is the
|
|
387
|
+
coefficient of the i-th variable in the objective function
|
|
388
|
+
|
|
389
|
+
- ``d`` -- double; the constant term in the linear function (set to `0`
|
|
390
|
+
by default)
|
|
391
|
+
|
|
392
|
+
EXAMPLES::
|
|
393
|
+
|
|
394
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
395
|
+
sage: p = get_solver(solver = "GLPK")
|
|
396
|
+
sage: p.add_variables(5)
|
|
397
|
+
4
|
|
398
|
+
sage: p.set_objective([1, 1, 2, 1, 3])
|
|
399
|
+
sage: [p.objective_coefficient(x) for x in range(5)]
|
|
400
|
+
[1.0, 1.0, 2.0, 1.0, 3.0]
|
|
401
|
+
"""
|
|
402
|
+
cdef int i
|
|
403
|
+
|
|
404
|
+
for i,v in enumerate(coeff):
|
|
405
|
+
glp_set_obj_coef(self.lp, i+1, v)
|
|
406
|
+
|
|
407
|
+
glp_set_obj_coef(self.lp, 0, d)
|
|
408
|
+
|
|
409
|
+
self.obj_constant_term = d
|
|
410
|
+
|
|
411
|
+
cpdef set_verbosity(self, int level):
|
|
412
|
+
"""
|
|
413
|
+
Set the verbosity level.
|
|
414
|
+
|
|
415
|
+
INPUT:
|
|
416
|
+
|
|
417
|
+
- ``level`` -- integer; from 0 (no verbosity) to 3
|
|
418
|
+
|
|
419
|
+
EXAMPLES::
|
|
420
|
+
|
|
421
|
+
sage: p.<x> = MixedIntegerLinearProgram(solver='GLPK')
|
|
422
|
+
sage: p.add_constraint(10 * x[0] <= 1)
|
|
423
|
+
sage: p.add_constraint(5 * x[1] <= 1)
|
|
424
|
+
sage: p.set_objective(x[0] + x[1])
|
|
425
|
+
sage: p.solve()
|
|
426
|
+
0.30000000000000004
|
|
427
|
+
sage: p.get_backend().set_verbosity(3)
|
|
428
|
+
sage: p.solver_parameter("simplex_or_intopt", "intopt_only")
|
|
429
|
+
sage: p.solve()
|
|
430
|
+
GLPK Integer Optimizer...
|
|
431
|
+
2 rows, 2 columns, 2 non-zeros
|
|
432
|
+
0 integer variables, none of which are binary
|
|
433
|
+
Preprocessing...
|
|
434
|
+
Objective value = 3.000000000e-01
|
|
435
|
+
INTEGER OPTIMAL SOLUTION FOUND BY MIP PREPROCESSOR
|
|
436
|
+
0.30000000000000004
|
|
437
|
+
|
|
438
|
+
::
|
|
439
|
+
|
|
440
|
+
sage: p.<x> = MixedIntegerLinearProgram(solver='GLPK/exact')
|
|
441
|
+
sage: p.add_constraint(10 * x[0] <= 1)
|
|
442
|
+
sage: p.add_constraint(5 * x[1] <= 1)
|
|
443
|
+
sage: p.set_objective(x[0] + x[1])
|
|
444
|
+
sage: p.solve() # tol 1e-14
|
|
445
|
+
0.3
|
|
446
|
+
sage: p.get_backend().set_verbosity(2)
|
|
447
|
+
sage: p.solve() # tol 1e-14
|
|
448
|
+
* 2: objval = 0.3 (0)
|
|
449
|
+
* 2: objval = 0.3 (0)
|
|
450
|
+
0.3
|
|
451
|
+
sage: p.get_backend().set_verbosity(3)
|
|
452
|
+
sage: p.solve() # tol 1e-14
|
|
453
|
+
glp_exact: 2 rows, 2 columns, 2 non-zeros
|
|
454
|
+
...
|
|
455
|
+
* 2: objval = 0.3 (0)
|
|
456
|
+
* 2: objval = 0.3 (0)
|
|
457
|
+
OPTIMAL SOLUTION FOUND
|
|
458
|
+
0.3
|
|
459
|
+
"""
|
|
460
|
+
if level == 0:
|
|
461
|
+
self.iocp.msg_lev = GLP_MSG_OFF
|
|
462
|
+
self.smcp.msg_lev = GLP_MSG_OFF
|
|
463
|
+
elif level == 1:
|
|
464
|
+
self.iocp.msg_lev = GLP_MSG_ERR
|
|
465
|
+
self.smcp.msg_lev = GLP_MSG_ERR
|
|
466
|
+
elif level == 2:
|
|
467
|
+
self.iocp.msg_lev = GLP_MSG_ON
|
|
468
|
+
self.smcp.msg_lev = GLP_MSG_ON
|
|
469
|
+
else:
|
|
470
|
+
self.iocp.msg_lev = GLP_MSG_ALL
|
|
471
|
+
self.smcp.msg_lev = GLP_MSG_ALL
|
|
472
|
+
|
|
473
|
+
cpdef remove_constraint(self, int i):
|
|
474
|
+
r"""
|
|
475
|
+
Remove a constraint from ``self``.
|
|
476
|
+
|
|
477
|
+
INPUT:
|
|
478
|
+
|
|
479
|
+
- ``i`` -- index of the constraint to remove
|
|
480
|
+
|
|
481
|
+
EXAMPLES::
|
|
482
|
+
|
|
483
|
+
sage: p = MixedIntegerLinearProgram(solver='GLPK')
|
|
484
|
+
sage: x, y = p['x'], p['y']
|
|
485
|
+
sage: p.add_constraint(2*x + 3*y <= 6)
|
|
486
|
+
sage: p.add_constraint(3*x + 2*y <= 6)
|
|
487
|
+
sage: p.add_constraint(x >= 0)
|
|
488
|
+
sage: p.set_objective(x + y + 7)
|
|
489
|
+
sage: p.set_integer(x); p.set_integer(y)
|
|
490
|
+
sage: p.solve()
|
|
491
|
+
9.0
|
|
492
|
+
sage: p.remove_constraint(0)
|
|
493
|
+
sage: p.solve()
|
|
494
|
+
10.0
|
|
495
|
+
|
|
496
|
+
Removing fancy constraints does not make Sage crash::
|
|
497
|
+
|
|
498
|
+
sage: MixedIntegerLinearProgram(solver = "GLPK").remove_constraint(-2)
|
|
499
|
+
Traceback (most recent call last):
|
|
500
|
+
...
|
|
501
|
+
ValueError: The constraint's index i must satisfy 0 <= i < number_of_constraints
|
|
502
|
+
"""
|
|
503
|
+
cdef int rows[2]
|
|
504
|
+
|
|
505
|
+
if i < 0 or i >= glp_get_num_rows(self.lp):
|
|
506
|
+
raise ValueError("The constraint's index i must satisfy 0 <= i < number_of_constraints")
|
|
507
|
+
|
|
508
|
+
rows[1] = i + 1
|
|
509
|
+
glp_del_rows(self.lp, 1, rows)
|
|
510
|
+
glp_std_basis(self.lp)
|
|
511
|
+
|
|
512
|
+
cpdef remove_constraints(self, constraints):
|
|
513
|
+
r"""
|
|
514
|
+
Remove several constraints.
|
|
515
|
+
|
|
516
|
+
INPUT:
|
|
517
|
+
|
|
518
|
+
- ``constraints`` -- an iterable containing the indices of the rows to remove
|
|
519
|
+
|
|
520
|
+
EXAMPLES::
|
|
521
|
+
|
|
522
|
+
sage: p = MixedIntegerLinearProgram(solver='GLPK')
|
|
523
|
+
sage: x, y = p['x'], p['y']
|
|
524
|
+
sage: p.add_constraint(2*x + 3*y <= 6)
|
|
525
|
+
sage: p.add_constraint(3*x + 2*y <= 6)
|
|
526
|
+
sage: p.add_constraint(x >= 0)
|
|
527
|
+
sage: p.set_objective(x + y + 7)
|
|
528
|
+
sage: p.set_integer(x); p.set_integer(y)
|
|
529
|
+
sage: p.solve()
|
|
530
|
+
9.0
|
|
531
|
+
sage: p.remove_constraints([0])
|
|
532
|
+
sage: p.solve()
|
|
533
|
+
10.0
|
|
534
|
+
sage: p.get_values([x,y])
|
|
535
|
+
[0.0, 3.0]
|
|
536
|
+
|
|
537
|
+
TESTS:
|
|
538
|
+
|
|
539
|
+
Removing fancy constraints does not make Sage crash::
|
|
540
|
+
|
|
541
|
+
sage: MixedIntegerLinearProgram(solver="GLPK").remove_constraints([0, -2])
|
|
542
|
+
Traceback (most recent call last):
|
|
543
|
+
...
|
|
544
|
+
ValueError: The constraint's index i must satisfy 0 <= i < number_of_constraints
|
|
545
|
+
"""
|
|
546
|
+
cdef int i, c
|
|
547
|
+
cdef int m = len(constraints)
|
|
548
|
+
cdef int * rows = <int *>sig_malloc((m + 1) * sizeof(int *))
|
|
549
|
+
cdef int nrows = glp_get_num_rows(self.lp)
|
|
550
|
+
|
|
551
|
+
for i in range(m):
|
|
552
|
+
|
|
553
|
+
c = constraints[i]
|
|
554
|
+
if c < 0 or c >= nrows:
|
|
555
|
+
sig_free(rows)
|
|
556
|
+
raise ValueError("The constraint's index i must satisfy 0 <= i < number_of_constraints")
|
|
557
|
+
|
|
558
|
+
rows[i+1] = c + 1
|
|
559
|
+
|
|
560
|
+
glp_del_rows(self.lp, m, rows)
|
|
561
|
+
sig_free(rows)
|
|
562
|
+
glp_std_basis(self.lp)
|
|
563
|
+
|
|
564
|
+
cpdef add_linear_constraint(self, coefficients, lower_bound, upper_bound, name=None):
|
|
565
|
+
"""
|
|
566
|
+
Add a linear constraint.
|
|
567
|
+
|
|
568
|
+
INPUT:
|
|
569
|
+
|
|
570
|
+
- ``coefficients`` an iterable with ``(c,v)`` pairs where ``c``
|
|
571
|
+
is a variable index (integer) and ``v`` is a value (real
|
|
572
|
+
value).
|
|
573
|
+
|
|
574
|
+
- ``lower_bound`` -- a lower bound, either a real value or ``None``
|
|
575
|
+
|
|
576
|
+
- ``upper_bound`` -- an upper bound, either a real value or ``None``
|
|
577
|
+
|
|
578
|
+
- ``name`` -- an optional name for this row (default: ``None``)
|
|
579
|
+
|
|
580
|
+
EXAMPLES::
|
|
581
|
+
|
|
582
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
583
|
+
sage: p = get_solver(solver = "GLPK")
|
|
584
|
+
sage: p.add_variables(5)
|
|
585
|
+
4
|
|
586
|
+
sage: p.add_linear_constraint(zip(range(5), range(5)), 2.0, 2.0)
|
|
587
|
+
sage: p.row(0)
|
|
588
|
+
([4, 3, 2, 1], [4.0, 3.0, 2.0, 1.0])
|
|
589
|
+
sage: p.row_bounds(0)
|
|
590
|
+
(2.0, 2.0)
|
|
591
|
+
sage: p.add_linear_constraint(zip(range(5), range(5)), 1.0, 1.0, name='foo')
|
|
592
|
+
sage: p.row_name(1)
|
|
593
|
+
'foo'
|
|
594
|
+
|
|
595
|
+
TESTS:
|
|
596
|
+
|
|
597
|
+
This used to crash Sage, but was fixed in :issue:`19525`::
|
|
598
|
+
|
|
599
|
+
sage: p = MixedIntegerLinearProgram(solver='glpk')
|
|
600
|
+
sage: q = MixedIntegerLinearProgram(solver='glpk')
|
|
601
|
+
sage: q.add_constraint(p.new_variable()[0] <= 1)
|
|
602
|
+
Traceback (most recent call last):
|
|
603
|
+
...
|
|
604
|
+
ValueError: invalid variable index 0
|
|
605
|
+
"""
|
|
606
|
+
if lower_bound is None and upper_bound is None:
|
|
607
|
+
raise ValueError("At least one of 'upper_bound' or 'lower_bound' must be set.")
|
|
608
|
+
|
|
609
|
+
# We're going to iterate through this more than once.
|
|
610
|
+
coefficients = list(coefficients)
|
|
611
|
+
|
|
612
|
+
for (index, _) in coefficients:
|
|
613
|
+
if index < 0 or index > (self.ncols() - 1):
|
|
614
|
+
raise ValueError("invalid variable index %d" % index)
|
|
615
|
+
|
|
616
|
+
glp_add_rows(self.lp, 1)
|
|
617
|
+
cdef int n = glp_get_num_rows(self.lp)
|
|
618
|
+
|
|
619
|
+
cdef MemoryAllocator mem = MemoryAllocator()
|
|
620
|
+
cdef int * row_i
|
|
621
|
+
cdef double * row_values
|
|
622
|
+
|
|
623
|
+
cdef int n_coeff = len(coefficients)
|
|
624
|
+
row_i = <int*>mem.allocarray(n_coeff + 1, sizeof(int))
|
|
625
|
+
row_values = <double*>mem.allocarray(n_coeff + 1, sizeof(double))
|
|
626
|
+
|
|
627
|
+
cdef Py_ssize_t i = 1
|
|
628
|
+
for c,v in coefficients:
|
|
629
|
+
row_i[i] = c+1
|
|
630
|
+
row_values[i] = v
|
|
631
|
+
i += 1
|
|
632
|
+
|
|
633
|
+
sig_on()
|
|
634
|
+
glp_set_mat_row(self.lp, n, n_coeff, row_i, row_values)
|
|
635
|
+
sig_off()
|
|
636
|
+
|
|
637
|
+
if upper_bound is not None and lower_bound is None:
|
|
638
|
+
glp_set_row_bnds(self.lp, n, GLP_UP, upper_bound, upper_bound)
|
|
639
|
+
elif lower_bound is not None and upper_bound is None:
|
|
640
|
+
glp_set_row_bnds(self.lp, n, GLP_LO, lower_bound, lower_bound)
|
|
641
|
+
elif upper_bound is not None and lower_bound is not None:
|
|
642
|
+
if lower_bound == upper_bound:
|
|
643
|
+
glp_set_row_bnds(self.lp, n, GLP_FX, lower_bound, upper_bound)
|
|
644
|
+
else:
|
|
645
|
+
glp_set_row_bnds(self.lp, n, GLP_DB, lower_bound, upper_bound)
|
|
646
|
+
|
|
647
|
+
if name is not None:
|
|
648
|
+
glp_set_row_name(self.lp, n, str_to_bytes(name))
|
|
649
|
+
|
|
650
|
+
cpdef add_linear_constraints(self, int number, lower_bound, upper_bound, names=None):
|
|
651
|
+
"""
|
|
652
|
+
Add ``'number`` linear constraints.
|
|
653
|
+
|
|
654
|
+
INPUT:
|
|
655
|
+
|
|
656
|
+
- ``number`` -- integer; the number of constraints to add
|
|
657
|
+
|
|
658
|
+
- ``lower_bound`` -- a lower bound, either a real value or ``None``
|
|
659
|
+
|
|
660
|
+
- ``upper_bound`` -- an upper bound, either a real value or ``None``
|
|
661
|
+
|
|
662
|
+
- ``names`` -- an optional list of names (default: ``None``)
|
|
663
|
+
|
|
664
|
+
EXAMPLES::
|
|
665
|
+
|
|
666
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
667
|
+
sage: p = get_solver(solver = "GLPK")
|
|
668
|
+
sage: p.add_variables(5)
|
|
669
|
+
4
|
|
670
|
+
sage: p.add_linear_constraints(5, None, 2)
|
|
671
|
+
sage: p.row(4)
|
|
672
|
+
([], [])
|
|
673
|
+
sage: p.row_bounds(4)
|
|
674
|
+
(None, 2.0)
|
|
675
|
+
sage: p.add_linear_constraints(2, None, 2, names=['foo','bar'])
|
|
676
|
+
"""
|
|
677
|
+
if lower_bound is None and upper_bound is None:
|
|
678
|
+
raise ValueError("At least one of 'upper_bound' or 'lower_bound' must be set.")
|
|
679
|
+
|
|
680
|
+
glp_add_rows(self.lp, number)
|
|
681
|
+
cdef int n = glp_get_num_rows(self.lp)
|
|
682
|
+
|
|
683
|
+
cdef int i
|
|
684
|
+
for 0<= i < number:
|
|
685
|
+
if upper_bound is not None and lower_bound is None:
|
|
686
|
+
glp_set_row_bnds(self.lp, n-i, GLP_UP, upper_bound, upper_bound)
|
|
687
|
+
elif lower_bound is not None and upper_bound is None:
|
|
688
|
+
glp_set_row_bnds(self.lp, n-i, GLP_LO, lower_bound, lower_bound)
|
|
689
|
+
elif upper_bound is not None and lower_bound is not None:
|
|
690
|
+
if lower_bound == upper_bound:
|
|
691
|
+
glp_set_row_bnds(self.lp, n-i, GLP_FX, lower_bound, upper_bound)
|
|
692
|
+
else:
|
|
693
|
+
glp_set_row_bnds(self.lp, n-i, GLP_DB, lower_bound, upper_bound)
|
|
694
|
+
if names is not None:
|
|
695
|
+
glp_set_row_name(self.lp, n-i,
|
|
696
|
+
str_to_bytes(names[number-i-1]))
|
|
697
|
+
|
|
698
|
+
cpdef row(self, int index):
|
|
699
|
+
r"""
|
|
700
|
+
Return a row.
|
|
701
|
+
|
|
702
|
+
INPUT:
|
|
703
|
+
|
|
704
|
+
- ``index`` -- integer; the constraint's id
|
|
705
|
+
|
|
706
|
+
OUTPUT:
|
|
707
|
+
|
|
708
|
+
A pair ``(indices, coeffs)`` where ``indices`` lists the
|
|
709
|
+
entries whose coefficient is nonzero, and to which ``coeffs``
|
|
710
|
+
associates their coefficient on the model of the
|
|
711
|
+
``add_linear_constraint`` method.
|
|
712
|
+
|
|
713
|
+
EXAMPLES::
|
|
714
|
+
|
|
715
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
716
|
+
sage: p = get_solver(solver = "GLPK")
|
|
717
|
+
sage: p.add_variables(5)
|
|
718
|
+
4
|
|
719
|
+
sage: p.add_linear_constraint(list(zip(range(5), range(5))), 2, 2)
|
|
720
|
+
sage: p.row(0)
|
|
721
|
+
([4, 3, 2, 1], [4.0, 3.0, 2.0, 1.0])
|
|
722
|
+
sage: p.row_bounds(0)
|
|
723
|
+
(2.0, 2.0)
|
|
724
|
+
|
|
725
|
+
TESTS:
|
|
726
|
+
|
|
727
|
+
We sanity check the input that will be passed to GLPK::
|
|
728
|
+
|
|
729
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
730
|
+
sage: p = get_solver(solver='GLPK')
|
|
731
|
+
sage: p.row(2)
|
|
732
|
+
Traceback (most recent call last):
|
|
733
|
+
...
|
|
734
|
+
ValueError: invalid row index 2
|
|
735
|
+
"""
|
|
736
|
+
if index < 0 or index > (self.nrows() - 1):
|
|
737
|
+
raise ValueError("invalid row index %d" % index)
|
|
738
|
+
|
|
739
|
+
cdef int n = glp_get_num_cols(self.lp)
|
|
740
|
+
cdef MemoryAllocator mem = MemoryAllocator()
|
|
741
|
+
cdef int * c_indices = <int*>mem.allocarray(n+1, sizeof(int))
|
|
742
|
+
cdef double * c_values = <double*>mem.allocarray(n+1, sizeof(double))
|
|
743
|
+
cdef list indices = []
|
|
744
|
+
cdef list values = []
|
|
745
|
+
cdef int i,j
|
|
746
|
+
|
|
747
|
+
i = glp_get_mat_row(self.lp, index + 1, c_indices, c_values)
|
|
748
|
+
for 0 < j <= i:
|
|
749
|
+
indices.append(c_indices[j]-1)
|
|
750
|
+
values.append(c_values[j])
|
|
751
|
+
|
|
752
|
+
return (indices, values)
|
|
753
|
+
|
|
754
|
+
cpdef row_bounds(self, int index):
|
|
755
|
+
"""
|
|
756
|
+
Return the bounds of a specific constraint.
|
|
757
|
+
|
|
758
|
+
INPUT:
|
|
759
|
+
|
|
760
|
+
- ``index`` -- integer; the constraint's id
|
|
761
|
+
|
|
762
|
+
OUTPUT:
|
|
763
|
+
|
|
764
|
+
A pair ``(lower_bound, upper_bound)``. Each of them can be set
|
|
765
|
+
to ``None`` if the constraint is not bounded in the
|
|
766
|
+
corresponding direction, and is a real value otherwise.
|
|
767
|
+
|
|
768
|
+
EXAMPLES::
|
|
769
|
+
|
|
770
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
771
|
+
sage: p = get_solver(solver = "GLPK")
|
|
772
|
+
sage: p.add_variables(5)
|
|
773
|
+
4
|
|
774
|
+
sage: p.add_linear_constraint(list(zip(range(5), range(5))), 2, 2)
|
|
775
|
+
sage: p.row(0)
|
|
776
|
+
([4, 3, 2, 1], [4.0, 3.0, 2.0, 1.0])
|
|
777
|
+
sage: p.row_bounds(0)
|
|
778
|
+
(2.0, 2.0)
|
|
779
|
+
|
|
780
|
+
TESTS:
|
|
781
|
+
|
|
782
|
+
We sanity check the input that will be passed to GLPK::
|
|
783
|
+
|
|
784
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
785
|
+
sage: p = get_solver(solver='GLPK')
|
|
786
|
+
sage: p.row_bounds(2)
|
|
787
|
+
Traceback (most recent call last):
|
|
788
|
+
...
|
|
789
|
+
ValueError: invalid row index 2
|
|
790
|
+
"""
|
|
791
|
+
cdef double ub
|
|
792
|
+
cdef double lb
|
|
793
|
+
|
|
794
|
+
if index < 0 or index > (self.nrows() - 1):
|
|
795
|
+
raise ValueError("invalid row index %d" % index)
|
|
796
|
+
|
|
797
|
+
ub = glp_get_row_ub(self.lp, index + 1)
|
|
798
|
+
lb = glp_get_row_lb(self.lp, index +1)
|
|
799
|
+
|
|
800
|
+
return (
|
|
801
|
+
(lb if lb != -DBL_MAX else None),
|
|
802
|
+
(ub if ub != +DBL_MAX else None)
|
|
803
|
+
)
|
|
804
|
+
|
|
805
|
+
cpdef col_bounds(self, int index):
|
|
806
|
+
"""
|
|
807
|
+
Return the bounds of a specific variable.
|
|
808
|
+
|
|
809
|
+
INPUT:
|
|
810
|
+
|
|
811
|
+
- ``index`` -- integer; the variable's id
|
|
812
|
+
|
|
813
|
+
OUTPUT:
|
|
814
|
+
|
|
815
|
+
A pair ``(lower_bound, upper_bound)``. Each of them can be set
|
|
816
|
+
to ``None`` if the variable is not bounded in the
|
|
817
|
+
corresponding direction, and is a real value otherwise.
|
|
818
|
+
|
|
819
|
+
EXAMPLES::
|
|
820
|
+
|
|
821
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
822
|
+
sage: p = get_solver(solver = "GLPK")
|
|
823
|
+
sage: p.add_variable()
|
|
824
|
+
0
|
|
825
|
+
sage: p.col_bounds(0)
|
|
826
|
+
(0.0, None)
|
|
827
|
+
sage: p.variable_upper_bound(0, 5)
|
|
828
|
+
sage: p.col_bounds(0)
|
|
829
|
+
(0.0, 5.0)
|
|
830
|
+
|
|
831
|
+
TESTS:
|
|
832
|
+
|
|
833
|
+
We sanity check the input that will be passed to GLPK::
|
|
834
|
+
|
|
835
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
836
|
+
sage: p = get_solver(solver='GLPK')
|
|
837
|
+
sage: p.col_bounds(2)
|
|
838
|
+
Traceback (most recent call last):
|
|
839
|
+
...
|
|
840
|
+
ValueError: invalid column index 2
|
|
841
|
+
"""
|
|
842
|
+
|
|
843
|
+
cdef double ub
|
|
844
|
+
cdef double lb
|
|
845
|
+
|
|
846
|
+
if index < 0 or index > (self.ncols() - 1):
|
|
847
|
+
raise ValueError("invalid column index %d" % index)
|
|
848
|
+
|
|
849
|
+
ub = glp_get_col_ub(self.lp, index +1)
|
|
850
|
+
lb = glp_get_col_lb(self.lp, index +1)
|
|
851
|
+
|
|
852
|
+
return (
|
|
853
|
+
(lb if lb != -DBL_MAX else None),
|
|
854
|
+
(ub if ub != +DBL_MAX else None)
|
|
855
|
+
)
|
|
856
|
+
|
|
857
|
+
cpdef add_col(self, indices, coeffs):
|
|
858
|
+
"""
|
|
859
|
+
Add a column.
|
|
860
|
+
|
|
861
|
+
INPUT:
|
|
862
|
+
|
|
863
|
+
- ``indices`` -- list of integers; this list contains the
|
|
864
|
+
indices of the constraints in which the variable's
|
|
865
|
+
coefficient is nonzero
|
|
866
|
+
|
|
867
|
+
- ``coeffs`` -- list of real values; associates a coefficient
|
|
868
|
+
to the variable in each of the constraints in which it
|
|
869
|
+
appears. Namely, the i-th entry of ``coeffs`` corresponds to
|
|
870
|
+
the coefficient of the variable in the constraint
|
|
871
|
+
represented by the i-th entry in ``indices``.
|
|
872
|
+
|
|
873
|
+
.. NOTE::
|
|
874
|
+
|
|
875
|
+
``indices`` and ``coeffs`` are expected to be of the same
|
|
876
|
+
length.
|
|
877
|
+
|
|
878
|
+
EXAMPLES::
|
|
879
|
+
|
|
880
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
881
|
+
sage: p = get_solver(solver = "GLPK")
|
|
882
|
+
sage: p.ncols()
|
|
883
|
+
0
|
|
884
|
+
sage: p.nrows()
|
|
885
|
+
0
|
|
886
|
+
sage: p.add_linear_constraints(5, 0, None)
|
|
887
|
+
sage: p.add_col(range(5), range(5))
|
|
888
|
+
sage: p.nrows()
|
|
889
|
+
5
|
|
890
|
+
"""
|
|
891
|
+
|
|
892
|
+
glp_add_cols(self.lp, 1)
|
|
893
|
+
cdef int n = glp_get_num_cols(self.lp)
|
|
894
|
+
|
|
895
|
+
cdef int * col_i
|
|
896
|
+
cdef double * col_values
|
|
897
|
+
|
|
898
|
+
col_i = <int *> sig_malloc((len(indices)+1) * sizeof(int))
|
|
899
|
+
col_values = <double *> sig_malloc((len(indices)+1) * sizeof(double))
|
|
900
|
+
|
|
901
|
+
for i,v in enumerate(indices):
|
|
902
|
+
col_i[i+1] = v+1
|
|
903
|
+
for i,v in enumerate(coeffs):
|
|
904
|
+
col_values[i+1] = v
|
|
905
|
+
|
|
906
|
+
glp_set_mat_col(self.lp, n, len(indices), col_i, col_values)
|
|
907
|
+
glp_set_col_bnds(self.lp, n, GLP_LO, 0,0)
|
|
908
|
+
sig_free(col_i)
|
|
909
|
+
sig_free(col_values)
|
|
910
|
+
|
|
911
|
+
cpdef int solve(self) except -1:
|
|
912
|
+
"""
|
|
913
|
+
Solve the problem.
|
|
914
|
+
|
|
915
|
+
Sage uses GLPK's implementation of the branch-and-cut
|
|
916
|
+
algorithm (``glp_intopt``) to solve the mixed-integer linear
|
|
917
|
+
program. This algorithm can be requested explicitly by
|
|
918
|
+
setting the solver parameter "simplex_or_intopt" to
|
|
919
|
+
"intopt_only". By default, the simplex method will be used
|
|
920
|
+
first to detect pathological problems that the integer solver
|
|
921
|
+
cannot handle. If all variables are continuous, the integer
|
|
922
|
+
algorithm reduces to solving the linear program by the simplex
|
|
923
|
+
method.
|
|
924
|
+
|
|
925
|
+
EXAMPLES::
|
|
926
|
+
|
|
927
|
+
sage: lp = MixedIntegerLinearProgram(solver = 'GLPK', maximization = False)
|
|
928
|
+
sage: x, y = lp[0], lp[1]
|
|
929
|
+
sage: lp.add_constraint(-2*x + y <= 1)
|
|
930
|
+
sage: lp.add_constraint(x - y <= 1)
|
|
931
|
+
sage: lp.add_constraint(x + y >= 2)
|
|
932
|
+
sage: lp.set_objective(x + y)
|
|
933
|
+
sage: lp.set_integer(x)
|
|
934
|
+
sage: lp.set_integer(y)
|
|
935
|
+
sage: lp.solve()
|
|
936
|
+
2.0
|
|
937
|
+
sage: lp.get_values([x, y])
|
|
938
|
+
[1.0, 1.0]
|
|
939
|
+
|
|
940
|
+
.. NOTE::
|
|
941
|
+
|
|
942
|
+
This method raises ``MIPSolverException`` exceptions when
|
|
943
|
+
the solution cannot be computed for any reason (none
|
|
944
|
+
exists, or the LP solver was not able to find it, etc...)
|
|
945
|
+
|
|
946
|
+
EXAMPLES::
|
|
947
|
+
|
|
948
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
949
|
+
sage: p = get_solver(solver = "GLPK")
|
|
950
|
+
sage: p.add_linear_constraints(5, 0, None)
|
|
951
|
+
sage: p.add_col(range(5), range(5))
|
|
952
|
+
sage: p.solve()
|
|
953
|
+
0
|
|
954
|
+
sage: p.objective_coefficient(0,1)
|
|
955
|
+
sage: p.solve()
|
|
956
|
+
Traceback (most recent call last):
|
|
957
|
+
...
|
|
958
|
+
MIPSolverException: ...
|
|
959
|
+
|
|
960
|
+
.. WARNING::
|
|
961
|
+
|
|
962
|
+
GLPK's ``glp_intopt`` sometimes fails catastrophically
|
|
963
|
+
when given a system it cannot solve (:issue:`12309`). It
|
|
964
|
+
can loop indefinitely, or just plain segfault. Upstream
|
|
965
|
+
considers this behavior "essentially innate" to the
|
|
966
|
+
current design, and suggests preprocessing with
|
|
967
|
+
``glp_simplex``, which is what SageMath does by default.
|
|
968
|
+
Set the ``simplex_or_intopt`` solver parameter to
|
|
969
|
+
``glp_intopt_only`` at your own risk.
|
|
970
|
+
|
|
971
|
+
EXAMPLES::
|
|
972
|
+
|
|
973
|
+
sage: lp = MixedIntegerLinearProgram(solver = "GLPK")
|
|
974
|
+
sage: v = lp.new_variable(nonnegative=True)
|
|
975
|
+
sage: lp.add_constraint(v[1] +v[2] -2.0 *v[3], max=-1.0)
|
|
976
|
+
sage: lp.add_constraint(v[0] -4.0/3 *v[1] +1.0/3 *v[2], max=-1.0/3)
|
|
977
|
+
sage: lp.add_constraint(v[0] +0.5 *v[1] -0.5 *v[2] +0.25 *v[3], max=-0.25)
|
|
978
|
+
sage: lp.solve()
|
|
979
|
+
0.0
|
|
980
|
+
sage: lp.add_constraint(v[0] +4.0 *v[1] -v[2] +v[3], max=-1.0)
|
|
981
|
+
sage: lp.solve()
|
|
982
|
+
Traceback (most recent call last):
|
|
983
|
+
...
|
|
984
|
+
MIPSolverException: GLPK: Problem has no feasible solution
|
|
985
|
+
|
|
986
|
+
If we switch to "simplex_only", the integrality constraints are ignored,
|
|
987
|
+
and we get an optimal solution to the continuous relaxation.
|
|
988
|
+
|
|
989
|
+
EXAMPLES::
|
|
990
|
+
|
|
991
|
+
sage: lp = MixedIntegerLinearProgram(solver = 'GLPK', maximization = False)
|
|
992
|
+
sage: x, y = lp[0], lp[1]
|
|
993
|
+
sage: lp.add_constraint(-2*x + y <= 1)
|
|
994
|
+
sage: lp.add_constraint(x - y <= 1)
|
|
995
|
+
sage: lp.add_constraint(x + y >= 2)
|
|
996
|
+
sage: lp.set_objective(x + y)
|
|
997
|
+
sage: lp.set_integer(x)
|
|
998
|
+
sage: lp.set_integer(y)
|
|
999
|
+
sage: lp.solver_parameter("simplex_or_intopt", "simplex_only") # use simplex only
|
|
1000
|
+
sage: lp.solve()
|
|
1001
|
+
2.0
|
|
1002
|
+
sage: lp.get_values([x, y])
|
|
1003
|
+
[1.5, 0.5]
|
|
1004
|
+
|
|
1005
|
+
If one solves a linear program and wishes to access dual information
|
|
1006
|
+
(`get_col_dual` etc.) or tableau data (`get_row_stat` etc.),
|
|
1007
|
+
one needs to switch to "simplex_only" before solving.
|
|
1008
|
+
|
|
1009
|
+
GLPK also has an exact rational simplex solver. The only access
|
|
1010
|
+
to data is via double-precision floats, which means that
|
|
1011
|
+
rationals in the input data may be rounded before the exact
|
|
1012
|
+
solver sees them. Thus, it is unreasonable to expect that
|
|
1013
|
+
arbitrary LPs with rational coefficients are solved exactly.
|
|
1014
|
+
Once the LP has been read into the backend, it reconstructs
|
|
1015
|
+
rationals from doubles and does solve exactly over the rationals,
|
|
1016
|
+
but results are returned as as doubles.
|
|
1017
|
+
|
|
1018
|
+
EXAMPLES::
|
|
1019
|
+
|
|
1020
|
+
sage: lp.solver_parameter("simplex_or_intopt", "exact_simplex_only") # use exact simplex only
|
|
1021
|
+
sage: lp.solve()
|
|
1022
|
+
2.0
|
|
1023
|
+
sage: lp.get_values([x, y])
|
|
1024
|
+
[1.5, 0.5]
|
|
1025
|
+
|
|
1026
|
+
If you need the rational solution, you need to retrieve the
|
|
1027
|
+
basis information via ``get_col_stat`` and ``get_row_stat``
|
|
1028
|
+
and calculate the corresponding basic solution. Below we only
|
|
1029
|
+
test that the basis information is indeed available.
|
|
1030
|
+
Calculating the corresponding basic solution is left as an
|
|
1031
|
+
exercise.
|
|
1032
|
+
|
|
1033
|
+
EXAMPLES::
|
|
1034
|
+
|
|
1035
|
+
sage: lp.get_backend().get_row_stat(0)
|
|
1036
|
+
1
|
|
1037
|
+
sage: lp.get_backend().get_col_stat(0)
|
|
1038
|
+
1
|
|
1039
|
+
|
|
1040
|
+
Below we test that integers that can be exactly represented by
|
|
1041
|
+
IEEE 754 double-precision floating point numbers survive the
|
|
1042
|
+
rational reconstruction done by ``glp_exact`` and the subsequent
|
|
1043
|
+
conversion to double-precision floating point numbers.
|
|
1044
|
+
|
|
1045
|
+
EXAMPLES::
|
|
1046
|
+
|
|
1047
|
+
sage: lp = MixedIntegerLinearProgram(solver = 'GLPK', maximization = True)
|
|
1048
|
+
sage: test = 2^53 - 43
|
|
1049
|
+
sage: lp.solver_parameter("simplex_or_intopt", "exact_simplex_only") # use exact simplex only
|
|
1050
|
+
sage: x = lp[0]
|
|
1051
|
+
sage: lp.add_constraint(x <= test)
|
|
1052
|
+
sage: lp.set_objective(x)
|
|
1053
|
+
sage: lp.solve() == test # yes, we want an exact comparison here
|
|
1054
|
+
True
|
|
1055
|
+
sage: lp.get_values(x) == test # yes, we want an exact comparison here
|
|
1056
|
+
True
|
|
1057
|
+
|
|
1058
|
+
Below we test that GLPK backend can detect unboundedness in
|
|
1059
|
+
"simplex_only" mode (:issue:`18838`).
|
|
1060
|
+
|
|
1061
|
+
EXAMPLES::
|
|
1062
|
+
|
|
1063
|
+
sage: lp = MixedIntegerLinearProgram(maximization=True, solver = "GLPK")
|
|
1064
|
+
sage: lp.set_objective(lp[0])
|
|
1065
|
+
sage: lp.solver_parameter("simplex_or_intopt", "simplex_only")
|
|
1066
|
+
sage: lp.solve()
|
|
1067
|
+
Traceback (most recent call last):
|
|
1068
|
+
...
|
|
1069
|
+
MIPSolverException: GLPK: Problem has unbounded solution
|
|
1070
|
+
sage: lp.set_objective(lp[1])
|
|
1071
|
+
sage: lp.solver_parameter("primal_v_dual", "GLP_DUAL")
|
|
1072
|
+
sage: lp.solve()
|
|
1073
|
+
Traceback (most recent call last):
|
|
1074
|
+
...
|
|
1075
|
+
MIPSolverException: GLPK: Problem has unbounded solution
|
|
1076
|
+
sage: lp.solver_parameter("simplex_or_intopt", "simplex_then_intopt")
|
|
1077
|
+
sage: lp.solve()
|
|
1078
|
+
Traceback (most recent call last):
|
|
1079
|
+
...
|
|
1080
|
+
MIPSolverException: GLPK: The LP (relaxation) problem has no dual feasible solution
|
|
1081
|
+
sage: lp.solver_parameter("simplex_or_intopt", "intopt_only")
|
|
1082
|
+
sage: lp.solve()
|
|
1083
|
+
Traceback (most recent call last):
|
|
1084
|
+
...
|
|
1085
|
+
MIPSolverException: GLPK: The LP (relaxation) problem has no dual feasible solution
|
|
1086
|
+
sage: lp.set_max(lp[1],5)
|
|
1087
|
+
sage: lp.solve()
|
|
1088
|
+
5.0
|
|
1089
|
+
|
|
1090
|
+
Solving a LP within the acceptable gap. No exception is raised, even if
|
|
1091
|
+
the result is not optimal. To do this, we try to compute the maximum
|
|
1092
|
+
number of disjoint balls (of diameter 1) in a hypercube::
|
|
1093
|
+
|
|
1094
|
+
sage: # needs sage.graphs
|
|
1095
|
+
sage: g = graphs.CubeGraph(9)
|
|
1096
|
+
sage: p = MixedIntegerLinearProgram(solver='GLPK')
|
|
1097
|
+
sage: p.solver_parameter("mip_gap_tolerance",100)
|
|
1098
|
+
sage: b = p.new_variable(binary=True)
|
|
1099
|
+
sage: p.set_objective(p.sum(b[v] for v in g))
|
|
1100
|
+
sage: for v in g:
|
|
1101
|
+
....: p.add_constraint(b[v]+p.sum(b[u] for u in g.neighbors(v)) <= 1)
|
|
1102
|
+
sage: p.add_constraint(b[v] == 1) # Force an easy non-0 solution
|
|
1103
|
+
sage: p.solve() # rel tol 100
|
|
1104
|
+
1
|
|
1105
|
+
|
|
1106
|
+
Same, now with a time limit::
|
|
1107
|
+
|
|
1108
|
+
sage: # needs sage.graphs
|
|
1109
|
+
sage: p.solver_parameter("mip_gap_tolerance",1)
|
|
1110
|
+
sage: p.solver_parameter("timelimit",3.0)
|
|
1111
|
+
sage: p.solve() # rel tol 100
|
|
1112
|
+
1
|
|
1113
|
+
"""
|
|
1114
|
+
|
|
1115
|
+
cdef int solve_status
|
|
1116
|
+
cdef int solution_status
|
|
1117
|
+
global solve_status_msg
|
|
1118
|
+
global solution_status_msg
|
|
1119
|
+
|
|
1120
|
+
if (self.simplex_or_intopt == glp_simplex_only
|
|
1121
|
+
or self.simplex_or_intopt == glp_simplex_then_intopt
|
|
1122
|
+
or self.simplex_or_intopt == glp_exact_simplex_only):
|
|
1123
|
+
if self.simplex_or_intopt == glp_exact_simplex_only:
|
|
1124
|
+
solve_status = glp_exact(self.lp, self.smcp)
|
|
1125
|
+
else:
|
|
1126
|
+
solve_status = glp_simplex(self.lp, self.smcp)
|
|
1127
|
+
solution_status = glp_get_status(self.lp)
|
|
1128
|
+
|
|
1129
|
+
if ((self.simplex_or_intopt == glp_intopt_only)
|
|
1130
|
+
or (self.simplex_or_intopt == glp_simplex_then_intopt) and (solution_status != GLP_UNDEF) and (solution_status != GLP_NOFEAS)):
|
|
1131
|
+
sig_on()
|
|
1132
|
+
solve_status = glp_intopt(self.lp, self.iocp)
|
|
1133
|
+
solution_status = glp_mip_status(self.lp)
|
|
1134
|
+
sig_off()
|
|
1135
|
+
|
|
1136
|
+
if solution_status == GLP_OPT:
|
|
1137
|
+
pass
|
|
1138
|
+
elif (solution_status == GLP_FEAS) and (solve_status == GLP_ETMLIM or solve_status == GLP_EITLIM
|
|
1139
|
+
or solve_status == GLP_EMIPGAP or solve_status == GLP_EOBJLL or solve_status == GLP_EOBJUL):
|
|
1140
|
+
# no exception when time limit or iteration limit or mip gap tolerances or objective limits reached.
|
|
1141
|
+
pass
|
|
1142
|
+
elif solution_status == GLP_UNDEF:
|
|
1143
|
+
raise MIPSolverException("GLPK: "+solve_status_msg.get(solve_status, "unknown error during call to GLPK : "+str(solve_status)))
|
|
1144
|
+
else:
|
|
1145
|
+
raise MIPSolverException("GLPK: "+solution_status_msg.get(solution_status, "unknown error during call to GLPK : "+str(solution_status)))
|
|
1146
|
+
return 0
|
|
1147
|
+
|
|
1148
|
+
cpdef get_objective_value(self):
|
|
1149
|
+
"""
|
|
1150
|
+
Return the value of the objective function.
|
|
1151
|
+
|
|
1152
|
+
.. NOTE::
|
|
1153
|
+
|
|
1154
|
+
Behaviour is undefined unless ``solve`` has been called before.
|
|
1155
|
+
|
|
1156
|
+
EXAMPLES::
|
|
1157
|
+
|
|
1158
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
1159
|
+
sage: p = get_solver(solver = "GLPK")
|
|
1160
|
+
sage: p.add_variables(2)
|
|
1161
|
+
1
|
|
1162
|
+
sage: p.add_linear_constraint([[0, 1], [1, 2]], None, 3)
|
|
1163
|
+
sage: p.set_objective([2, 5])
|
|
1164
|
+
sage: p.solve()
|
|
1165
|
+
0
|
|
1166
|
+
sage: p.get_objective_value()
|
|
1167
|
+
7.5
|
|
1168
|
+
sage: p.get_variable_value(0) # abs tol 1e-15
|
|
1169
|
+
0.0
|
|
1170
|
+
sage: p.get_variable_value(1)
|
|
1171
|
+
1.5
|
|
1172
|
+
"""
|
|
1173
|
+
if (self.simplex_or_intopt != glp_simplex_only
|
|
1174
|
+
and self.simplex_or_intopt != glp_exact_simplex_only):
|
|
1175
|
+
return glp_mip_obj_val(self.lp)
|
|
1176
|
+
else:
|
|
1177
|
+
return glp_get_obj_val(self.lp)
|
|
1178
|
+
|
|
1179
|
+
cpdef best_known_objective_bound(self):
|
|
1180
|
+
r"""
|
|
1181
|
+
Return the value of the currently best known bound.
|
|
1182
|
+
|
|
1183
|
+
This method returns the current best upper (resp. lower) bound on the
|
|
1184
|
+
optimal value of the objective function in a maximization
|
|
1185
|
+
(resp. minimization) problem. It is equal to the output of
|
|
1186
|
+
:meth:`get_objective_value` if the MILP found an optimal solution, but
|
|
1187
|
+
it can differ if it was interrupted manually or after a time limit (cf
|
|
1188
|
+
:meth:`solver_parameter`).
|
|
1189
|
+
|
|
1190
|
+
.. NOTE::
|
|
1191
|
+
|
|
1192
|
+
Has no meaning unless ``solve`` has been called before.
|
|
1193
|
+
|
|
1194
|
+
EXAMPLES::
|
|
1195
|
+
|
|
1196
|
+
sage: # needs sage.graphs
|
|
1197
|
+
sage: g = graphs.CubeGraph(9)
|
|
1198
|
+
sage: p = MixedIntegerLinearProgram(solver='GLPK')
|
|
1199
|
+
sage: p.solver_parameter("mip_gap_tolerance",100)
|
|
1200
|
+
sage: b = p.new_variable(binary=True)
|
|
1201
|
+
sage: p.set_objective(p.sum(b[v] for v in g))
|
|
1202
|
+
sage: for v in g:
|
|
1203
|
+
....: p.add_constraint(b[v]+p.sum(b[u] for u in g.neighbors(v)) <= 1)
|
|
1204
|
+
sage: p.add_constraint(b[v] == 1) # Force an easy non-0 solution
|
|
1205
|
+
sage: p.solve() # rel tol 100
|
|
1206
|
+
1.0
|
|
1207
|
+
sage: backend = p.get_backend()
|
|
1208
|
+
sage: backend.best_known_objective_bound() # random
|
|
1209
|
+
48.0
|
|
1210
|
+
"""
|
|
1211
|
+
return self.search_tree_data.best_bound
|
|
1212
|
+
|
|
1213
|
+
cpdef get_relative_objective_gap(self):
|
|
1214
|
+
r"""
|
|
1215
|
+
Return the relative objective gap of the best known solution.
|
|
1216
|
+
|
|
1217
|
+
For a minimization problem, this value is computed by
|
|
1218
|
+
`(\texttt{bestinteger} - \texttt{bestobjective}) / (1e-10 +
|
|
1219
|
+
|\texttt{bestobjective}|)`, where ``bestinteger`` is the value returned
|
|
1220
|
+
by :meth:`get_objective_value` and ``bestobjective`` is the value
|
|
1221
|
+
returned by :meth:`best_known_objective_bound`. For a maximization
|
|
1222
|
+
problem, the value is computed by `(\texttt{bestobjective} -
|
|
1223
|
+
\texttt{bestinteger}) / (1e-10 + |\texttt{bestobjective}|)`.
|
|
1224
|
+
|
|
1225
|
+
.. NOTE::
|
|
1226
|
+
|
|
1227
|
+
Has no meaning unless ``solve`` has been called before.
|
|
1228
|
+
|
|
1229
|
+
EXAMPLES::
|
|
1230
|
+
|
|
1231
|
+
sage: # needs sage.graphs
|
|
1232
|
+
sage: g = graphs.CubeGraph(9)
|
|
1233
|
+
sage: p = MixedIntegerLinearProgram(solver='GLPK')
|
|
1234
|
+
sage: p.solver_parameter("mip_gap_tolerance",100)
|
|
1235
|
+
sage: b = p.new_variable(binary=True)
|
|
1236
|
+
sage: p.set_objective(p.sum(b[v] for v in g))
|
|
1237
|
+
sage: for v in g:
|
|
1238
|
+
....: p.add_constraint(b[v]+p.sum(b[u] for u in g.neighbors(v)) <= 1)
|
|
1239
|
+
sage: p.add_constraint(b[v] == 1) # Force an easy non-0 solution
|
|
1240
|
+
sage: p.solve() # rel tol 100
|
|
1241
|
+
1.0
|
|
1242
|
+
sage: backend = p.get_backend()
|
|
1243
|
+
sage: backend.get_relative_objective_gap() # random
|
|
1244
|
+
46.99999999999999
|
|
1245
|
+
|
|
1246
|
+
TESTS:
|
|
1247
|
+
|
|
1248
|
+
Just make sure that the variable *has* been defined, and is not just
|
|
1249
|
+
undefined::
|
|
1250
|
+
|
|
1251
|
+
sage: backend.get_relative_objective_gap() > 1 # needs sage.graphs
|
|
1252
|
+
True
|
|
1253
|
+
"""
|
|
1254
|
+
return self.search_tree_data.mip_gap
|
|
1255
|
+
|
|
1256
|
+
cpdef get_variable_value(self, int variable):
|
|
1257
|
+
"""
|
|
1258
|
+
Return the value of a variable given by the solver.
|
|
1259
|
+
|
|
1260
|
+
.. NOTE::
|
|
1261
|
+
|
|
1262
|
+
Behaviour is undefined unless ``solve`` has been called before.
|
|
1263
|
+
|
|
1264
|
+
EXAMPLES::
|
|
1265
|
+
|
|
1266
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
1267
|
+
sage: p = get_solver(solver = "GLPK")
|
|
1268
|
+
sage: p.add_variables(2)
|
|
1269
|
+
1
|
|
1270
|
+
sage: p.add_linear_constraint([[0, 1], [1, 2]], None, 3)
|
|
1271
|
+
sage: p.set_objective([2, 5])
|
|
1272
|
+
sage: p.solve()
|
|
1273
|
+
0
|
|
1274
|
+
sage: p.get_objective_value()
|
|
1275
|
+
7.5
|
|
1276
|
+
sage: p.get_variable_value(0) # abs tol 1e-15
|
|
1277
|
+
0.0
|
|
1278
|
+
sage: p.get_variable_value(1)
|
|
1279
|
+
1.5
|
|
1280
|
+
|
|
1281
|
+
TESTS:
|
|
1282
|
+
|
|
1283
|
+
We sanity check the input that will be passed to GLPK::
|
|
1284
|
+
|
|
1285
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
1286
|
+
sage: p = get_solver(solver='GLPK')
|
|
1287
|
+
sage: p.get_variable_value(2)
|
|
1288
|
+
Traceback (most recent call last):
|
|
1289
|
+
...
|
|
1290
|
+
ValueError: invalid variable index 2
|
|
1291
|
+
"""
|
|
1292
|
+
if variable < 0 or variable > (self.ncols() - 1):
|
|
1293
|
+
raise ValueError("invalid variable index %d" % variable)
|
|
1294
|
+
|
|
1295
|
+
if (self.simplex_or_intopt != glp_simplex_only
|
|
1296
|
+
and self.simplex_or_intopt != glp_exact_simplex_only):
|
|
1297
|
+
return glp_mip_col_val(self.lp, variable + 1)
|
|
1298
|
+
else:
|
|
1299
|
+
return glp_get_col_prim(self.lp, variable + 1)
|
|
1300
|
+
|
|
1301
|
+
cpdef get_row_prim(self, int i):
|
|
1302
|
+
r"""
|
|
1303
|
+
Return the value of the auxiliary variable associated with i-th row.
|
|
1304
|
+
|
|
1305
|
+
.. NOTE::
|
|
1306
|
+
|
|
1307
|
+
Behaviour is undefined unless ``solve`` has been called before.
|
|
1308
|
+
|
|
1309
|
+
EXAMPLES::
|
|
1310
|
+
|
|
1311
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
1312
|
+
sage: lp = get_solver(solver = "GLPK")
|
|
1313
|
+
sage: lp.add_variables(3)
|
|
1314
|
+
2
|
|
1315
|
+
sage: lp.add_linear_constraint(list(zip([0, 1, 2], [8, 6, 1])), None, 48)
|
|
1316
|
+
sage: lp.add_linear_constraint(list(zip([0, 1, 2], [4, 2, 1.5])), None, 20)
|
|
1317
|
+
sage: lp.add_linear_constraint(list(zip([0, 1, 2], [2, 1.5, 0.5])), None, 8)
|
|
1318
|
+
sage: lp.set_objective([60, 30, 20])
|
|
1319
|
+
sage: import sage.numerical.backends.glpk_backend as backend
|
|
1320
|
+
sage: lp.solver_parameter(backend.glp_simplex_or_intopt, backend.glp_simplex_only)
|
|
1321
|
+
sage: lp.solve()
|
|
1322
|
+
0
|
|
1323
|
+
sage: lp.get_objective_value()
|
|
1324
|
+
280.0
|
|
1325
|
+
sage: lp.get_row_prim(0)
|
|
1326
|
+
24.0
|
|
1327
|
+
sage: lp.get_row_prim(1)
|
|
1328
|
+
20.0
|
|
1329
|
+
sage: lp.get_row_prim(2)
|
|
1330
|
+
8.0
|
|
1331
|
+
|
|
1332
|
+
TESTS:
|
|
1333
|
+
|
|
1334
|
+
We sanity check the input that will be passed to GLPK::
|
|
1335
|
+
|
|
1336
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
1337
|
+
sage: p = get_solver(solver='GLPK')
|
|
1338
|
+
sage: p.get_row_prim(2)
|
|
1339
|
+
Traceback (most recent call last):
|
|
1340
|
+
...
|
|
1341
|
+
ValueError: invalid row index 2
|
|
1342
|
+
"""
|
|
1343
|
+
if i < 0 or i > (self.nrows() - 1):
|
|
1344
|
+
raise ValueError("invalid row index %d" % i)
|
|
1345
|
+
|
|
1346
|
+
return glp_get_row_prim(self.lp, i+1)
|
|
1347
|
+
|
|
1348
|
+
cpdef int ncols(self) noexcept:
|
|
1349
|
+
"""
|
|
1350
|
+
Return the number of columns/variables.
|
|
1351
|
+
|
|
1352
|
+
EXAMPLES::
|
|
1353
|
+
|
|
1354
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
1355
|
+
sage: p = get_solver(solver = "GLPK")
|
|
1356
|
+
sage: p.ncols()
|
|
1357
|
+
0
|
|
1358
|
+
sage: p.add_variables(2)
|
|
1359
|
+
1
|
|
1360
|
+
sage: p.ncols()
|
|
1361
|
+
2
|
|
1362
|
+
"""
|
|
1363
|
+
return glp_get_num_cols(self.lp)
|
|
1364
|
+
|
|
1365
|
+
cpdef int nrows(self) noexcept:
|
|
1366
|
+
"""
|
|
1367
|
+
Return the number of rows/constraints.
|
|
1368
|
+
|
|
1369
|
+
EXAMPLES::
|
|
1370
|
+
|
|
1371
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
1372
|
+
sage: p = get_solver(solver = "GLPK")
|
|
1373
|
+
sage: p.nrows()
|
|
1374
|
+
0
|
|
1375
|
+
sage: p.add_linear_constraints(2, 2, None)
|
|
1376
|
+
sage: p.nrows()
|
|
1377
|
+
2
|
|
1378
|
+
"""
|
|
1379
|
+
|
|
1380
|
+
return glp_get_num_rows(self.lp)
|
|
1381
|
+
|
|
1382
|
+
cpdef col_name(self, int index):
|
|
1383
|
+
"""
|
|
1384
|
+
Return the ``index``-th col name.
|
|
1385
|
+
|
|
1386
|
+
INPUT:
|
|
1387
|
+
|
|
1388
|
+
- ``index`` -- integer; the col's id
|
|
1389
|
+
|
|
1390
|
+
EXAMPLES::
|
|
1391
|
+
|
|
1392
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
1393
|
+
sage: p = get_solver(solver = "GLPK")
|
|
1394
|
+
sage: p.add_variable(name='I am a variable')
|
|
1395
|
+
0
|
|
1396
|
+
sage: p.col_name(0)
|
|
1397
|
+
'I am a variable'
|
|
1398
|
+
|
|
1399
|
+
TESTS:
|
|
1400
|
+
|
|
1401
|
+
We sanity check the input that will be passed to GLPK::
|
|
1402
|
+
|
|
1403
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
1404
|
+
sage: p = get_solver(solver='GLPK')
|
|
1405
|
+
sage: p.col_name(2)
|
|
1406
|
+
Traceback (most recent call last):
|
|
1407
|
+
...
|
|
1408
|
+
ValueError: invalid column index 2
|
|
1409
|
+
"""
|
|
1410
|
+
cdef char * s
|
|
1411
|
+
|
|
1412
|
+
if index < 0 or index > (self.ncols() - 1):
|
|
1413
|
+
raise ValueError("invalid column index %d" % index)
|
|
1414
|
+
|
|
1415
|
+
glp_create_index(self.lp)
|
|
1416
|
+
s = <char*> glp_get_col_name(self.lp, index + 1)
|
|
1417
|
+
|
|
1418
|
+
if s != NULL:
|
|
1419
|
+
return char_to_str(s)
|
|
1420
|
+
else:
|
|
1421
|
+
return ""
|
|
1422
|
+
|
|
1423
|
+
cpdef row_name(self, int index):
|
|
1424
|
+
"""
|
|
1425
|
+
Return the ``index``-th row name.
|
|
1426
|
+
|
|
1427
|
+
INPUT:
|
|
1428
|
+
|
|
1429
|
+
- ``index`` -- integer; the row's id
|
|
1430
|
+
|
|
1431
|
+
EXAMPLES::
|
|
1432
|
+
|
|
1433
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
1434
|
+
sage: p = get_solver(solver = "GLPK")
|
|
1435
|
+
sage: p.add_linear_constraints(1, 2, None, names=['Empty constraint 1'])
|
|
1436
|
+
sage: p.row_name(0)
|
|
1437
|
+
'Empty constraint 1'
|
|
1438
|
+
|
|
1439
|
+
TESTS:
|
|
1440
|
+
|
|
1441
|
+
We sanity check the input that will be passed to GLPK::
|
|
1442
|
+
|
|
1443
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
1444
|
+
sage: p = get_solver(solver='GLPK')
|
|
1445
|
+
sage: p.row_name(2)
|
|
1446
|
+
Traceback (most recent call last):
|
|
1447
|
+
...
|
|
1448
|
+
ValueError: invalid row index 2
|
|
1449
|
+
"""
|
|
1450
|
+
cdef char * s
|
|
1451
|
+
|
|
1452
|
+
if index < 0 or index > (self.nrows() - 1):
|
|
1453
|
+
raise ValueError("invalid row index %d" % index)
|
|
1454
|
+
|
|
1455
|
+
glp_create_index(self.lp)
|
|
1456
|
+
s = <char*> glp_get_row_name(self.lp, index + 1)
|
|
1457
|
+
|
|
1458
|
+
if s != NULL:
|
|
1459
|
+
return char_to_str(s)
|
|
1460
|
+
else:
|
|
1461
|
+
return ""
|
|
1462
|
+
|
|
1463
|
+
cpdef bint is_variable_binary(self, int index) noexcept:
|
|
1464
|
+
"""
|
|
1465
|
+
Test whether the given variable is of binary type.
|
|
1466
|
+
|
|
1467
|
+
INPUT:
|
|
1468
|
+
|
|
1469
|
+
- ``index`` -- integer; the variable's id
|
|
1470
|
+
|
|
1471
|
+
EXAMPLES::
|
|
1472
|
+
|
|
1473
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
1474
|
+
sage: p = get_solver(solver = "GLPK")
|
|
1475
|
+
sage: p.ncols()
|
|
1476
|
+
0
|
|
1477
|
+
sage: p.add_variable()
|
|
1478
|
+
0
|
|
1479
|
+
sage: p.set_variable_type(0,0)
|
|
1480
|
+
sage: p.is_variable_binary(0)
|
|
1481
|
+
True
|
|
1482
|
+
|
|
1483
|
+
TESTS:
|
|
1484
|
+
|
|
1485
|
+
We sanity check the input that will be passed to GLPK::
|
|
1486
|
+
|
|
1487
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
1488
|
+
sage: p = get_solver(solver='GLPK')
|
|
1489
|
+
sage: p.is_variable_binary(2)
|
|
1490
|
+
False
|
|
1491
|
+
"""
|
|
1492
|
+
if index < 0 or index > (self.ncols() - 1):
|
|
1493
|
+
# This is how the other backends behave, and this method is
|
|
1494
|
+
# unable to raise a python exception as currently defined.
|
|
1495
|
+
return False
|
|
1496
|
+
|
|
1497
|
+
return glp_get_col_kind(self.lp, index + 1) == GLP_BV
|
|
1498
|
+
|
|
1499
|
+
cpdef bint is_variable_integer(self, int index) noexcept:
|
|
1500
|
+
"""
|
|
1501
|
+
Test whether the given variable is of integer type.
|
|
1502
|
+
|
|
1503
|
+
INPUT:
|
|
1504
|
+
|
|
1505
|
+
- ``index`` -- integer; the variable's id
|
|
1506
|
+
|
|
1507
|
+
EXAMPLES::
|
|
1508
|
+
|
|
1509
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
1510
|
+
sage: p = get_solver(solver = "GLPK")
|
|
1511
|
+
sage: p.ncols()
|
|
1512
|
+
0
|
|
1513
|
+
sage: p.add_variable()
|
|
1514
|
+
0
|
|
1515
|
+
sage: p.set_variable_type(0,1)
|
|
1516
|
+
sage: p.is_variable_integer(0)
|
|
1517
|
+
True
|
|
1518
|
+
|
|
1519
|
+
TESTS:
|
|
1520
|
+
|
|
1521
|
+
We sanity check the input that will be passed to GLPK::
|
|
1522
|
+
|
|
1523
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
1524
|
+
sage: p = get_solver(solver='GLPK')
|
|
1525
|
+
sage: p.is_variable_integer(2)
|
|
1526
|
+
False
|
|
1527
|
+
"""
|
|
1528
|
+
if index < 0 or index > (self.ncols() - 1):
|
|
1529
|
+
# This is how the other backends behave, and this method is
|
|
1530
|
+
# unable to raise a python exception as currently defined.
|
|
1531
|
+
return False
|
|
1532
|
+
|
|
1533
|
+
return glp_get_col_kind(self.lp, index + 1) == GLP_IV
|
|
1534
|
+
|
|
1535
|
+
cpdef bint is_variable_continuous(self, int index) noexcept:
|
|
1536
|
+
"""
|
|
1537
|
+
Test whether the given variable is of continuous/real type.
|
|
1538
|
+
|
|
1539
|
+
INPUT:
|
|
1540
|
+
|
|
1541
|
+
- ``index`` -- integer; the variable's id
|
|
1542
|
+
|
|
1543
|
+
EXAMPLES::
|
|
1544
|
+
|
|
1545
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
1546
|
+
sage: p = get_solver(solver = "GLPK")
|
|
1547
|
+
sage: p.ncols()
|
|
1548
|
+
0
|
|
1549
|
+
sage: p.add_variable()
|
|
1550
|
+
0
|
|
1551
|
+
sage: p.is_variable_continuous(0)
|
|
1552
|
+
True
|
|
1553
|
+
sage: p.set_variable_type(0,1)
|
|
1554
|
+
sage: p.is_variable_continuous(0)
|
|
1555
|
+
False
|
|
1556
|
+
|
|
1557
|
+
TESTS:
|
|
1558
|
+
|
|
1559
|
+
We sanity check the input that will be passed to GLPK::
|
|
1560
|
+
|
|
1561
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
1562
|
+
sage: p = get_solver(solver='GLPK')
|
|
1563
|
+
sage: p.is_variable_continuous(2)
|
|
1564
|
+
False
|
|
1565
|
+
"""
|
|
1566
|
+
if index < 0 or index > (self.ncols() - 1):
|
|
1567
|
+
# This is how the other backends behave, and this method is
|
|
1568
|
+
# unable to raise a python exception as currently defined.
|
|
1569
|
+
return False
|
|
1570
|
+
|
|
1571
|
+
return glp_get_col_kind(self.lp, index + 1) == GLP_CV
|
|
1572
|
+
|
|
1573
|
+
cpdef bint is_maximization(self) noexcept:
|
|
1574
|
+
"""
|
|
1575
|
+
Test whether the problem is a maximization
|
|
1576
|
+
|
|
1577
|
+
EXAMPLES::
|
|
1578
|
+
|
|
1579
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
1580
|
+
sage: p = get_solver(solver = "GLPK")
|
|
1581
|
+
sage: p.is_maximization()
|
|
1582
|
+
True
|
|
1583
|
+
sage: p.set_sense(-1)
|
|
1584
|
+
sage: p.is_maximization()
|
|
1585
|
+
False
|
|
1586
|
+
"""
|
|
1587
|
+
|
|
1588
|
+
return glp_get_obj_dir(self.lp) == GLP_MAX
|
|
1589
|
+
|
|
1590
|
+
cpdef variable_upper_bound(self, int index, value=False):
|
|
1591
|
+
"""
|
|
1592
|
+
Return or define the upper bound on a variable.
|
|
1593
|
+
|
|
1594
|
+
INPUT:
|
|
1595
|
+
|
|
1596
|
+
- ``index`` -- integer; the variable's id
|
|
1597
|
+
|
|
1598
|
+
- ``value`` -- real value, or ``None`` to mean that the
|
|
1599
|
+
variable has not upper bound. When set to ``False``
|
|
1600
|
+
(default), the method returns the current value.
|
|
1601
|
+
|
|
1602
|
+
EXAMPLES::
|
|
1603
|
+
|
|
1604
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
1605
|
+
sage: p = get_solver(solver = "GLPK")
|
|
1606
|
+
sage: p.add_variable()
|
|
1607
|
+
0
|
|
1608
|
+
sage: p.col_bounds(0)
|
|
1609
|
+
(0.0, None)
|
|
1610
|
+
sage: p.variable_upper_bound(0, 5)
|
|
1611
|
+
sage: p.col_bounds(0)
|
|
1612
|
+
(0.0, 5.0)
|
|
1613
|
+
|
|
1614
|
+
TESTS:
|
|
1615
|
+
|
|
1616
|
+
:issue:`14581`::
|
|
1617
|
+
|
|
1618
|
+
sage: P = MixedIntegerLinearProgram(solver='GLPK')
|
|
1619
|
+
sage: x = P["x"]
|
|
1620
|
+
sage: P.set_max(x, 0)
|
|
1621
|
+
sage: P.get_max(x)
|
|
1622
|
+
0.0
|
|
1623
|
+
|
|
1624
|
+
Check that :issue:`10232` is fixed::
|
|
1625
|
+
|
|
1626
|
+
sage: p = get_solver(solver='GLPK')
|
|
1627
|
+
sage: p.variable_upper_bound(2)
|
|
1628
|
+
Traceback (most recent call last):
|
|
1629
|
+
...
|
|
1630
|
+
ValueError: invalid variable index 2
|
|
1631
|
+
|
|
1632
|
+
sage: p.variable_upper_bound(-1)
|
|
1633
|
+
Traceback (most recent call last):
|
|
1634
|
+
...
|
|
1635
|
+
ValueError: invalid variable index -1
|
|
1636
|
+
|
|
1637
|
+
sage: p.variable_upper_bound(3, 5)
|
|
1638
|
+
Traceback (most recent call last):
|
|
1639
|
+
...
|
|
1640
|
+
ValueError: invalid variable index 3
|
|
1641
|
+
|
|
1642
|
+
sage: p.add_variable()
|
|
1643
|
+
0
|
|
1644
|
+
sage: p.variable_upper_bound(0, 'hey!')
|
|
1645
|
+
Traceback (most recent call last):
|
|
1646
|
+
...
|
|
1647
|
+
TypeError: must be real number, not str
|
|
1648
|
+
"""
|
|
1649
|
+
cdef double x
|
|
1650
|
+
cdef double min
|
|
1651
|
+
cdef double dvalue
|
|
1652
|
+
|
|
1653
|
+
if index < 0 or index > (self.ncols() - 1):
|
|
1654
|
+
raise ValueError("invalid variable index %d" % index)
|
|
1655
|
+
|
|
1656
|
+
if value is False:
|
|
1657
|
+
sig_on()
|
|
1658
|
+
x = glp_get_col_ub(self.lp, index +1)
|
|
1659
|
+
sig_off()
|
|
1660
|
+
if x == DBL_MAX:
|
|
1661
|
+
return None
|
|
1662
|
+
else:
|
|
1663
|
+
return x
|
|
1664
|
+
else:
|
|
1665
|
+
sig_on()
|
|
1666
|
+
min = glp_get_col_lb(self.lp, index + 1)
|
|
1667
|
+
sig_off()
|
|
1668
|
+
|
|
1669
|
+
if value is None:
|
|
1670
|
+
sig_on()
|
|
1671
|
+
if min == -DBL_MAX:
|
|
1672
|
+
glp_set_col_bnds(self.lp, index + 1, GLP_FR, 0, 0)
|
|
1673
|
+
else:
|
|
1674
|
+
glp_set_col_bnds(self.lp, index + 1, GLP_LO, min, 0)
|
|
1675
|
+
sig_off()
|
|
1676
|
+
else:
|
|
1677
|
+
dvalue = <double?> value
|
|
1678
|
+
|
|
1679
|
+
sig_on()
|
|
1680
|
+
if min == -DBL_MAX:
|
|
1681
|
+
glp_set_col_bnds(self.lp, index + 1, GLP_UP, 0, dvalue)
|
|
1682
|
+
|
|
1683
|
+
elif min == dvalue:
|
|
1684
|
+
glp_set_col_bnds(self.lp, index + 1, GLP_FX, dvalue, dvalue)
|
|
1685
|
+
else:
|
|
1686
|
+
glp_set_col_bnds(self.lp, index + 1, GLP_DB, min, dvalue)
|
|
1687
|
+
sig_off()
|
|
1688
|
+
|
|
1689
|
+
cpdef variable_lower_bound(self, int index, value=False):
|
|
1690
|
+
"""
|
|
1691
|
+
Return or define the lower bound on a variable.
|
|
1692
|
+
|
|
1693
|
+
INPUT:
|
|
1694
|
+
|
|
1695
|
+
- ``index`` -- integer; the variable's id
|
|
1696
|
+
|
|
1697
|
+
- ``value`` -- real value, or ``None`` to mean that the
|
|
1698
|
+
variable has not lower bound. When set to ``False``
|
|
1699
|
+
(default), the method returns the current value.
|
|
1700
|
+
|
|
1701
|
+
EXAMPLES::
|
|
1702
|
+
|
|
1703
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
1704
|
+
sage: p = get_solver(solver = "GLPK")
|
|
1705
|
+
sage: p.add_variable()
|
|
1706
|
+
0
|
|
1707
|
+
sage: p.col_bounds(0)
|
|
1708
|
+
(0.0, None)
|
|
1709
|
+
sage: p.variable_lower_bound(0, 5)
|
|
1710
|
+
sage: p.col_bounds(0)
|
|
1711
|
+
(5.0, None)
|
|
1712
|
+
|
|
1713
|
+
TESTS:
|
|
1714
|
+
|
|
1715
|
+
:issue:`14581`::
|
|
1716
|
+
|
|
1717
|
+
sage: P = MixedIntegerLinearProgram(solver='GLPK')
|
|
1718
|
+
sage: x = P["x"]
|
|
1719
|
+
sage: P.set_min(x, 5)
|
|
1720
|
+
sage: P.set_min(x, 0)
|
|
1721
|
+
sage: P.get_min(x)
|
|
1722
|
+
0.0
|
|
1723
|
+
|
|
1724
|
+
Check that :issue:`10232` is fixed::
|
|
1725
|
+
|
|
1726
|
+
sage: p = get_solver(solver='GLPK')
|
|
1727
|
+
sage: p.variable_lower_bound(2)
|
|
1728
|
+
Traceback (most recent call last):
|
|
1729
|
+
...
|
|
1730
|
+
ValueError: invalid variable index 2
|
|
1731
|
+
|
|
1732
|
+
sage: p.variable_lower_bound(-1)
|
|
1733
|
+
Traceback (most recent call last):
|
|
1734
|
+
...
|
|
1735
|
+
ValueError: invalid variable index -1
|
|
1736
|
+
|
|
1737
|
+
sage: p.variable_lower_bound(3, 5)
|
|
1738
|
+
Traceback (most recent call last):
|
|
1739
|
+
...
|
|
1740
|
+
ValueError: invalid variable index 3
|
|
1741
|
+
|
|
1742
|
+
sage: p.add_variable()
|
|
1743
|
+
0
|
|
1744
|
+
sage: p.variable_lower_bound(0, 'hey!')
|
|
1745
|
+
Traceback (most recent call last):
|
|
1746
|
+
...
|
|
1747
|
+
TypeError: must be real number, not str
|
|
1748
|
+
"""
|
|
1749
|
+
cdef double x
|
|
1750
|
+
cdef double max
|
|
1751
|
+
cdef double dvalue
|
|
1752
|
+
|
|
1753
|
+
if index < 0 or index > (self.ncols() - 1):
|
|
1754
|
+
raise ValueError("invalid variable index %d" % index)
|
|
1755
|
+
|
|
1756
|
+
if value is False:
|
|
1757
|
+
sig_on()
|
|
1758
|
+
x = glp_get_col_lb(self.lp, index +1)
|
|
1759
|
+
sig_off()
|
|
1760
|
+
if x == -DBL_MAX:
|
|
1761
|
+
return None
|
|
1762
|
+
else:
|
|
1763
|
+
return x
|
|
1764
|
+
else:
|
|
1765
|
+
sig_on()
|
|
1766
|
+
max = glp_get_col_ub(self.lp, index + 1)
|
|
1767
|
+
sig_off()
|
|
1768
|
+
|
|
1769
|
+
if value is None:
|
|
1770
|
+
sig_on()
|
|
1771
|
+
if max == DBL_MAX:
|
|
1772
|
+
glp_set_col_bnds(self.lp, index + 1, GLP_FR, 0.0, 0.0)
|
|
1773
|
+
else:
|
|
1774
|
+
glp_set_col_bnds(self.lp, index + 1, GLP_UP, 0.0, max)
|
|
1775
|
+
sig_off()
|
|
1776
|
+
|
|
1777
|
+
else:
|
|
1778
|
+
dvalue = <double?> value
|
|
1779
|
+
|
|
1780
|
+
sig_on()
|
|
1781
|
+
if max == DBL_MAX:
|
|
1782
|
+
glp_set_col_bnds(self.lp, index + 1, GLP_LO, value, 0.0)
|
|
1783
|
+
elif max == value:
|
|
1784
|
+
glp_set_col_bnds(self.lp, index + 1, GLP_FX, value, value)
|
|
1785
|
+
else:
|
|
1786
|
+
glp_set_col_bnds(self.lp, index + 1, GLP_DB, value, max)
|
|
1787
|
+
sig_off()
|
|
1788
|
+
|
|
1789
|
+
cpdef write_lp(self, filename):
|
|
1790
|
+
"""
|
|
1791
|
+
Write the problem to a ``.lp`` file.
|
|
1792
|
+
|
|
1793
|
+
INPUT:
|
|
1794
|
+
|
|
1795
|
+
- ``filename`` -- string
|
|
1796
|
+
|
|
1797
|
+
EXAMPLES::
|
|
1798
|
+
|
|
1799
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
1800
|
+
sage: p = get_solver(solver = "GLPK")
|
|
1801
|
+
sage: p.add_variables(2)
|
|
1802
|
+
1
|
|
1803
|
+
sage: p.add_linear_constraint([[0, 1], [1, 2]], None, 3)
|
|
1804
|
+
sage: p.set_objective([2, 5])
|
|
1805
|
+
sage: import tempfile
|
|
1806
|
+
sage: with tempfile.NamedTemporaryFile(suffix='.lp') as f:
|
|
1807
|
+
....: _ = p.write_lp(f.name)
|
|
1808
|
+
....: len(f.readlines())
|
|
1809
|
+
...
|
|
1810
|
+
9 lines were written
|
|
1811
|
+
9
|
|
1812
|
+
"""
|
|
1813
|
+
filename = str_to_bytes(filename, FS_ENCODING, 'surrogateescape')
|
|
1814
|
+
glp_write_lp(self.lp, NULL, filename)
|
|
1815
|
+
|
|
1816
|
+
cpdef write_mps(self, filename, int modern):
|
|
1817
|
+
"""
|
|
1818
|
+
Write the problem to a ``.mps`` file.
|
|
1819
|
+
|
|
1820
|
+
INPUT:
|
|
1821
|
+
|
|
1822
|
+
- ``filename`` -- string
|
|
1823
|
+
|
|
1824
|
+
EXAMPLES::
|
|
1825
|
+
|
|
1826
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
1827
|
+
sage: p = get_solver(solver = "GLPK")
|
|
1828
|
+
sage: p.add_variables(2)
|
|
1829
|
+
1
|
|
1830
|
+
sage: p.add_linear_constraint([[0, 1], [1, 2]], None, 3)
|
|
1831
|
+
sage: p.set_objective([2, 5])
|
|
1832
|
+
sage: import tempfile
|
|
1833
|
+
sage: with tempfile.NamedTemporaryFile(suffix='mps') as f:
|
|
1834
|
+
....: _ = p.write_mps(f.name, 2)
|
|
1835
|
+
....: len(f.readlines())
|
|
1836
|
+
...
|
|
1837
|
+
17 records were written
|
|
1838
|
+
17
|
|
1839
|
+
"""
|
|
1840
|
+
filename = str_to_bytes(filename, FS_ENCODING, 'surrogateescape')
|
|
1841
|
+
glp_write_mps(self.lp, modern, NULL, filename)
|
|
1842
|
+
|
|
1843
|
+
cpdef __copy__(self):
|
|
1844
|
+
"""
|
|
1845
|
+
Return a copy of ``self``.
|
|
1846
|
+
|
|
1847
|
+
EXAMPLES::
|
|
1848
|
+
|
|
1849
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
1850
|
+
sage: p = MixedIntegerLinearProgram(solver = "GLPK")
|
|
1851
|
+
sage: b = p.new_variable()
|
|
1852
|
+
sage: p.add_constraint(b[1] + b[2] <= 6)
|
|
1853
|
+
sage: p.set_objective(b[1] + b[2])
|
|
1854
|
+
sage: copy(p).solve()
|
|
1855
|
+
6.0
|
|
1856
|
+
"""
|
|
1857
|
+
cdef GLPKBackend p = type(self)(maximization = (1 if self.is_maximization() else -1))
|
|
1858
|
+
p.simplex_or_intopt = self.simplex_or_intopt
|
|
1859
|
+
p.iocp.tm_lim = self.iocp.tm_lim
|
|
1860
|
+
glp_copy_prob(p.lp, self.lp, 1)
|
|
1861
|
+
return p
|
|
1862
|
+
|
|
1863
|
+
cpdef solver_parameter(self, name, value=None):
|
|
1864
|
+
"""
|
|
1865
|
+
Return or define a solver parameter.
|
|
1866
|
+
|
|
1867
|
+
INPUT:
|
|
1868
|
+
|
|
1869
|
+
- ``name`` -- string; the parameter
|
|
1870
|
+
|
|
1871
|
+
- ``value`` -- the parameter's value if it is to be defined,
|
|
1872
|
+
or ``None`` (default) to obtain its current value
|
|
1873
|
+
|
|
1874
|
+
You can supply the name of a parameter and its value using either a
|
|
1875
|
+
string or a ``glp_`` constant (which are defined as Cython variables of
|
|
1876
|
+
this module).
|
|
1877
|
+
|
|
1878
|
+
In most cases, you can use the same name for a parameter as that given
|
|
1879
|
+
in the GLPK documentation, which is available by downloading GLPK from
|
|
1880
|
+
<http://www.gnu.org/software/glpk/>. The exceptions relate to parameters
|
|
1881
|
+
common to both methods; these require you to append ``_simplex`` or
|
|
1882
|
+
``_intopt`` to the name to resolve ambiguity, since the interface allows
|
|
1883
|
+
access to both.
|
|
1884
|
+
|
|
1885
|
+
We have also provided more meaningful names, to assist readability.
|
|
1886
|
+
|
|
1887
|
+
Parameter **names** are specified in lower case.
|
|
1888
|
+
To use a constant instead of a string, prepend ``glp_`` to the name.
|
|
1889
|
+
For example, both ``glp_gmi_cuts`` or ``'gmi_cuts'`` control whether
|
|
1890
|
+
to solve using Gomory cuts.
|
|
1891
|
+
|
|
1892
|
+
Parameter **values** are specified as strings in upper case,
|
|
1893
|
+
or as constants in lower case. For example, both ``glp_on`` and ``"GLP_ON"``
|
|
1894
|
+
specify the same thing.
|
|
1895
|
+
|
|
1896
|
+
Naturally, you can use ``True`` and ``False`` in cases where ``glp_on`` and ``glp_off``
|
|
1897
|
+
would be used.
|
|
1898
|
+
|
|
1899
|
+
A list of parameter names, with their possible values:
|
|
1900
|
+
|
|
1901
|
+
**General-purpose parameters:**
|
|
1902
|
+
|
|
1903
|
+
.. list-table::
|
|
1904
|
+
:widths: 30 70
|
|
1905
|
+
|
|
1906
|
+
* - ``timelimit``
|
|
1907
|
+
|
|
1908
|
+
- specify the time limit IN SECONDS. This affects both simplex
|
|
1909
|
+
and intopt.
|
|
1910
|
+
|
|
1911
|
+
* - ``timelimit_simplex`` and ``timelimit_intopt``
|
|
1912
|
+
|
|
1913
|
+
- specify the time limit IN MILLISECONDS. (This is glpk's
|
|
1914
|
+
default.)
|
|
1915
|
+
|
|
1916
|
+
* - ``simplex_or_intopt``
|
|
1917
|
+
|
|
1918
|
+
- specify which solution routines in GLPK to use. Set this
|
|
1919
|
+
to either ``simplex_only``, ``exact_simplex_only``,
|
|
1920
|
+
``intopt_only``, or ``simplex_then_intopt`` (the
|
|
1921
|
+
default). The ``simplex_then_intopt`` option does some
|
|
1922
|
+
extra work, but avoids hangs/crashes in GLPK on problems
|
|
1923
|
+
with no solution; SageMath will try simplex first, then
|
|
1924
|
+
perform integer optimization only if a solution of the LP
|
|
1925
|
+
relaxation exists. If you know that your system is not
|
|
1926
|
+
pathological, one of the other options will be faster.
|
|
1927
|
+
|
|
1928
|
+
* - ``verbosity_intopt`` and ``verbosity_simplex``
|
|
1929
|
+
|
|
1930
|
+
- one of ``GLP_MSG_OFF``, ``GLP_MSG_ERR``, ``GLP_MSG_ON``, or
|
|
1931
|
+
``GLP_MSG_ALL``. The default is ``GLP_MSG_OFF``.
|
|
1932
|
+
|
|
1933
|
+
* - ``output_frequency_intopt`` and ``output_frequency_simplex``
|
|
1934
|
+
|
|
1935
|
+
- the output frequency, in milliseconds. Default is 5000.
|
|
1936
|
+
|
|
1937
|
+
* - ``output_delay_intopt`` and ``output_delay_simplex``
|
|
1938
|
+
|
|
1939
|
+
- the output delay, in milliseconds, regarding the use of the
|
|
1940
|
+
simplex method on the LP relaxation. Default is 10000.
|
|
1941
|
+
|
|
1942
|
+
|
|
1943
|
+
**intopt-specific parameters:**
|
|
1944
|
+
|
|
1945
|
+
.. list-table::
|
|
1946
|
+
:widths: 30 70
|
|
1947
|
+
|
|
1948
|
+
* - ``branching``
|
|
1949
|
+
- - ``GLP_BR_FFV`` first fractional variable
|
|
1950
|
+
- ``GLP_BR_LFV`` last fractional variable
|
|
1951
|
+
- ``GLP_BR_MFV`` most fractional variable
|
|
1952
|
+
- ``GLP_BR_DTH`` Driebeck-Tomlin heuristic (default)
|
|
1953
|
+
- ``GLP_BR_PCH`` hybrid pseudocost heuristic
|
|
1954
|
+
|
|
1955
|
+
* - ``backtracking``
|
|
1956
|
+
- - ``GLP_BT_DFS`` depth first search
|
|
1957
|
+
- ``GLP_BT_BFS`` breadth first search
|
|
1958
|
+
- ``GLP_BT_BLB`` best local bound (default)
|
|
1959
|
+
- ``GLP_BT_BPH`` best projection heuristic
|
|
1960
|
+
|
|
1961
|
+
* - ``preprocessing``
|
|
1962
|
+
- - ``GLP_PP_NONE``
|
|
1963
|
+
- ``GLP_PP_ROOT`` preprocessing only at root level
|
|
1964
|
+
- ``GLP_PP_ALL`` (default)
|
|
1965
|
+
|
|
1966
|
+
|
|
1967
|
+
* - ``feasibility_pump``
|
|
1968
|
+
|
|
1969
|
+
- ``GLP_ON`` or ``GLP_OFF`` (default)
|
|
1970
|
+
|
|
1971
|
+
* - ``gomory_cuts``
|
|
1972
|
+
|
|
1973
|
+
- ``GLP_ON`` or ``GLP_OFF`` (default)
|
|
1974
|
+
|
|
1975
|
+
* - ``mixed_int_rounding_cuts``
|
|
1976
|
+
|
|
1977
|
+
- ``GLP_ON`` or ``GLP_OFF`` (default)
|
|
1978
|
+
|
|
1979
|
+
* - ``mixed_cover_cuts``
|
|
1980
|
+
|
|
1981
|
+
- ``GLP_ON`` or ``GLP_OFF`` (default)
|
|
1982
|
+
|
|
1983
|
+
* - ``clique_cuts``
|
|
1984
|
+
|
|
1985
|
+
- ``GLP_ON`` or ``GLP_OFF`` (default)
|
|
1986
|
+
|
|
1987
|
+
* - ``absolute_tolerance``
|
|
1988
|
+
|
|
1989
|
+
- (double) used to check if optimal solution to LP relaxation is
|
|
1990
|
+
integer feasible. GLPK manual advises, "do not change... without
|
|
1991
|
+
detailed understanding of its purpose."
|
|
1992
|
+
|
|
1993
|
+
* - ``relative_tolerance``
|
|
1994
|
+
|
|
1995
|
+
- (double) used to check if objective value in LP relaxation is not
|
|
1996
|
+
better than best known integer solution. GLPK manual advises, "do
|
|
1997
|
+
not change... without detailed understanding of its purpose."
|
|
1998
|
+
|
|
1999
|
+
* - ``mip_gap_tolerance``
|
|
2000
|
+
|
|
2001
|
+
- (double) relative mip gap tolerance. Default is 0.0.
|
|
2002
|
+
|
|
2003
|
+
* - ``presolve_intopt``
|
|
2004
|
+
|
|
2005
|
+
- ``GLP_ON`` (default) or ``GLP_OFF``
|
|
2006
|
+
|
|
2007
|
+
* - ``binarize``
|
|
2008
|
+
|
|
2009
|
+
- ``GLP_ON`` or ``GLP_OFF`` (default)
|
|
2010
|
+
|
|
2011
|
+
|
|
2012
|
+
**simplex-specific parameters:**
|
|
2013
|
+
|
|
2014
|
+
.. list-table::
|
|
2015
|
+
:widths: 30 70
|
|
2016
|
+
|
|
2017
|
+
* - ``primal_v_dual``
|
|
2018
|
+
|
|
2019
|
+
- - ``GLP_PRIMAL`` (default)
|
|
2020
|
+
- ``GLP_DUAL``
|
|
2021
|
+
- ``GLP_DUALP``
|
|
2022
|
+
|
|
2023
|
+
* - ``pricing``
|
|
2024
|
+
|
|
2025
|
+
- - ``GLP_PT_STD`` standard (textbook)
|
|
2026
|
+
- ``GLP_PT_PSE`` projected steepest edge (default)
|
|
2027
|
+
|
|
2028
|
+
* - ``ratio_test``
|
|
2029
|
+
|
|
2030
|
+
- - ``GLP_RT_STD`` standard (textbook)
|
|
2031
|
+
- ``GLP_RT_HAR`` Harris' two-pass ratio test (default)
|
|
2032
|
+
|
|
2033
|
+
* - ``tolerance_primal``
|
|
2034
|
+
|
|
2035
|
+
- (double) tolerance used to check if basic solution is primal
|
|
2036
|
+
feasible. GLPK manual advises, "do not change... without
|
|
2037
|
+
detailed understanding of its purpose."
|
|
2038
|
+
|
|
2039
|
+
* - ``tolerance_dual``
|
|
2040
|
+
|
|
2041
|
+
- (double) tolerance used to check if basic solution is dual
|
|
2042
|
+
feasible. GLPK manual advises, "do not change... without
|
|
2043
|
+
detailed understanding of its purpose."
|
|
2044
|
+
|
|
2045
|
+
* - ``tolerance_pivot``
|
|
2046
|
+
|
|
2047
|
+
- (double) tolerance used to choose pivot. GLPK manual advises,
|
|
2048
|
+
"do not change... without detailed understanding of its
|
|
2049
|
+
purpose."
|
|
2050
|
+
|
|
2051
|
+
* - ``obj_lower_limit``
|
|
2052
|
+
|
|
2053
|
+
- (double) lower limit of the objective function. The default is
|
|
2054
|
+
``-DBL_MAX``.
|
|
2055
|
+
|
|
2056
|
+
* - ``obj_upper_limit``
|
|
2057
|
+
|
|
2058
|
+
- (double) upper limit of the objective function. The default is
|
|
2059
|
+
``DBL_MAX``.
|
|
2060
|
+
|
|
2061
|
+
* - ``iteration_limit``
|
|
2062
|
+
|
|
2063
|
+
- (int) iteration limit of the simplex algorithm. The default is
|
|
2064
|
+
``INT_MAX``.
|
|
2065
|
+
|
|
2066
|
+
* - ``presolve_simplex``
|
|
2067
|
+
|
|
2068
|
+
- ``GLP_ON`` or ``GLP_OFF`` (default).
|
|
2069
|
+
|
|
2070
|
+
.. NOTE::
|
|
2071
|
+
|
|
2072
|
+
The coverage for GLPK's control parameters for simplex and integer optimization
|
|
2073
|
+
is nearly complete. The only thing lacking is a wrapper for callback routines.
|
|
2074
|
+
|
|
2075
|
+
To date, no attempt has been made to expose the interior point methods.
|
|
2076
|
+
|
|
2077
|
+
EXAMPLES::
|
|
2078
|
+
|
|
2079
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
2080
|
+
sage: p = get_solver(solver = "GLPK")
|
|
2081
|
+
sage: p.solver_parameter("timelimit", 60)
|
|
2082
|
+
sage: p.solver_parameter("timelimit")
|
|
2083
|
+
60.0
|
|
2084
|
+
|
|
2085
|
+
- Don't forget the difference between ``timelimit`` and ``timelimit_intopt``
|
|
2086
|
+
|
|
2087
|
+
::
|
|
2088
|
+
|
|
2089
|
+
sage: p.solver_parameter("timelimit_intopt")
|
|
2090
|
+
60000
|
|
2091
|
+
|
|
2092
|
+
If you don't care for an integer answer, you can ask for an LP
|
|
2093
|
+
relaxation instead. The default solver performs integer optimization,
|
|
2094
|
+
but you can switch to the standard simplex algorithm through the
|
|
2095
|
+
``glp_simplex_or_intopt`` parameter.
|
|
2096
|
+
|
|
2097
|
+
EXAMPLES::
|
|
2098
|
+
|
|
2099
|
+
sage: lp = MixedIntegerLinearProgram(solver = 'GLPK', maximization = False)
|
|
2100
|
+
sage: x, y = lp[0], lp[1]
|
|
2101
|
+
sage: lp.add_constraint(-2*x + y <= 1)
|
|
2102
|
+
sage: lp.add_constraint(x - y <= 1)
|
|
2103
|
+
sage: lp.add_constraint(x + y >= 2)
|
|
2104
|
+
sage: lp.set_integer(x); lp.set_integer(y)
|
|
2105
|
+
sage: lp.set_objective(x + y)
|
|
2106
|
+
sage: lp.solve()
|
|
2107
|
+
2.0
|
|
2108
|
+
sage: lp.get_values([x,y])
|
|
2109
|
+
[1.0, 1.0]
|
|
2110
|
+
sage: import sage.numerical.backends.glpk_backend as backend
|
|
2111
|
+
sage: lp.solver_parameter(backend.glp_simplex_or_intopt, backend.glp_simplex_only)
|
|
2112
|
+
sage: lp.solve()
|
|
2113
|
+
2.0
|
|
2114
|
+
sage: lp.get_values([x,y])
|
|
2115
|
+
[1.5, 0.5]
|
|
2116
|
+
|
|
2117
|
+
You can get GLPK to spout all sorts of information at you.
|
|
2118
|
+
The default is to turn this off, but sometimes (debugging) it's very useful::
|
|
2119
|
+
|
|
2120
|
+
sage: lp.solver_parameter(backend.glp_simplex_or_intopt, backend.glp_simplex_then_intopt)
|
|
2121
|
+
sage: lp.solver_parameter(backend.glp_mir_cuts, backend.glp_on)
|
|
2122
|
+
sage: lp.solver_parameter(backend.glp_msg_lev_intopt, backend.glp_msg_all)
|
|
2123
|
+
sage: lp.solver_parameter(backend.glp_mir_cuts)
|
|
2124
|
+
1
|
|
2125
|
+
|
|
2126
|
+
If you actually try to solve ``lp``, you will get a lot of detailed information.
|
|
2127
|
+
"""
|
|
2128
|
+
|
|
2129
|
+
if isinstance(name, str):
|
|
2130
|
+
if name == "out_frq" or name == "out_dly" or name == "tm_lim" or name == "msg_lev":
|
|
2131
|
+
raise ValueError("To set parameter " + name + " you must specify the solver. Append either _simplex or _intopt.")
|
|
2132
|
+
name = solver_parameter_names_dict[name]
|
|
2133
|
+
|
|
2134
|
+
if isinstance(value, str):
|
|
2135
|
+
value = solver_parameter_values[value]
|
|
2136
|
+
|
|
2137
|
+
if name == timelimit_intopt:
|
|
2138
|
+
if value is None:
|
|
2139
|
+
return self.iocp.tm_lim
|
|
2140
|
+
else:
|
|
2141
|
+
self.iocp.tm_lim = value
|
|
2142
|
+
|
|
2143
|
+
if name == timelimit_seconds:
|
|
2144
|
+
if value is None:
|
|
2145
|
+
return self.iocp.tm_lim / 1000.0
|
|
2146
|
+
else:
|
|
2147
|
+
self.iocp.tm_lim = value * 1000.0
|
|
2148
|
+
self.smcp.tm_lim = value * 1000.0
|
|
2149
|
+
|
|
2150
|
+
elif name == timelimit_simplex:
|
|
2151
|
+
if value is None:
|
|
2152
|
+
return self.smcp.tm_lim
|
|
2153
|
+
else:
|
|
2154
|
+
self.smcp.tm_lim = value
|
|
2155
|
+
|
|
2156
|
+
elif name == simplex_or_intopt:
|
|
2157
|
+
if value is None:
|
|
2158
|
+
return self.simplex_or_intopt
|
|
2159
|
+
if value not in (simplex_only, intopt_only, simplex_then_intopt, exact_simplex_only):
|
|
2160
|
+
raise MIPSolverException("GLPK: invalid value for simplex_or_intopt; see documentation")
|
|
2161
|
+
self.simplex_or_intopt = value
|
|
2162
|
+
|
|
2163
|
+
elif name == msg_lev_simplex:
|
|
2164
|
+
if value is None:
|
|
2165
|
+
return self.smcp.msg_lev
|
|
2166
|
+
else:
|
|
2167
|
+
self.smcp.msg_lev = value
|
|
2168
|
+
|
|
2169
|
+
elif name == msg_lev_intopt:
|
|
2170
|
+
if value is None:
|
|
2171
|
+
return self.iocp.msg_lev
|
|
2172
|
+
else:
|
|
2173
|
+
self.iocp.msg_lev = value
|
|
2174
|
+
|
|
2175
|
+
elif name == br_tech:
|
|
2176
|
+
if value is None:
|
|
2177
|
+
return self.iocp.br_tech
|
|
2178
|
+
else:
|
|
2179
|
+
self.iocp.br_tech = value
|
|
2180
|
+
|
|
2181
|
+
elif name == bt_tech:
|
|
2182
|
+
if value is None:
|
|
2183
|
+
return self.iocp.bt_tech
|
|
2184
|
+
else:
|
|
2185
|
+
self.iocp.bt_tech = value
|
|
2186
|
+
|
|
2187
|
+
elif name == pp_tech:
|
|
2188
|
+
if value is None:
|
|
2189
|
+
return self.iocp.pp_tech
|
|
2190
|
+
else:
|
|
2191
|
+
self.iocp.pp_tech = value
|
|
2192
|
+
|
|
2193
|
+
elif name == fp_heur:
|
|
2194
|
+
if value is None:
|
|
2195
|
+
return self.iocp.fp_heur
|
|
2196
|
+
else:
|
|
2197
|
+
if value:
|
|
2198
|
+
value = GLP_ON
|
|
2199
|
+
else:
|
|
2200
|
+
value = GLP_OFF
|
|
2201
|
+
self.iocp.fp_heur = value
|
|
2202
|
+
|
|
2203
|
+
elif name == gmi_cuts:
|
|
2204
|
+
if value is None:
|
|
2205
|
+
return self.iocp.gmi_cuts
|
|
2206
|
+
else:
|
|
2207
|
+
if value:
|
|
2208
|
+
value = GLP_ON
|
|
2209
|
+
else:
|
|
2210
|
+
value = GLP_OFF
|
|
2211
|
+
self.iocp.gmi_cuts = value
|
|
2212
|
+
|
|
2213
|
+
elif name == mir_cuts:
|
|
2214
|
+
if value is None:
|
|
2215
|
+
return self.iocp.mir_cuts
|
|
2216
|
+
else:
|
|
2217
|
+
if value:
|
|
2218
|
+
value = GLP_ON
|
|
2219
|
+
else:
|
|
2220
|
+
value = GLP_OFF
|
|
2221
|
+
self.iocp.mir_cuts = value
|
|
2222
|
+
|
|
2223
|
+
elif name == cov_cuts:
|
|
2224
|
+
if value is None:
|
|
2225
|
+
return self.iocp.cov_cuts
|
|
2226
|
+
else:
|
|
2227
|
+
if value:
|
|
2228
|
+
value = GLP_ON
|
|
2229
|
+
else:
|
|
2230
|
+
value = GLP_OFF
|
|
2231
|
+
self.iocp.cov_cuts = value
|
|
2232
|
+
|
|
2233
|
+
elif name == clq_cuts:
|
|
2234
|
+
if value is None:
|
|
2235
|
+
return self.iocp.clq_cuts
|
|
2236
|
+
else:
|
|
2237
|
+
if value:
|
|
2238
|
+
value = GLP_ON
|
|
2239
|
+
else:
|
|
2240
|
+
value = GLP_OFF
|
|
2241
|
+
self.iocp.clq_cuts = value
|
|
2242
|
+
|
|
2243
|
+
elif name == tol_int:
|
|
2244
|
+
if value is None:
|
|
2245
|
+
return self.iocp.tol_int
|
|
2246
|
+
else:
|
|
2247
|
+
self.iocp.tol_int = value
|
|
2248
|
+
|
|
2249
|
+
elif name == tol_obj:
|
|
2250
|
+
if value is None:
|
|
2251
|
+
return self.iocp.tol_obj
|
|
2252
|
+
else:
|
|
2253
|
+
self.iocp.tol_obj = value
|
|
2254
|
+
|
|
2255
|
+
elif name == mip_gap:
|
|
2256
|
+
if value is None:
|
|
2257
|
+
return self.iocp.mip_gap
|
|
2258
|
+
else:
|
|
2259
|
+
self.iocp.mip_gap = value
|
|
2260
|
+
|
|
2261
|
+
elif name == tm_lim_intopt:
|
|
2262
|
+
if value is None:
|
|
2263
|
+
return self.iocp.tm_lim
|
|
2264
|
+
else:
|
|
2265
|
+
self.iocp.tm_lim = value
|
|
2266
|
+
|
|
2267
|
+
elif name == out_frq_intopt:
|
|
2268
|
+
if value is None:
|
|
2269
|
+
return self.iocp.out_frq
|
|
2270
|
+
else:
|
|
2271
|
+
self.iocp.out_frq = value
|
|
2272
|
+
|
|
2273
|
+
elif name == out_dly_intopt:
|
|
2274
|
+
if value is None:
|
|
2275
|
+
return self.iocp.out_dly
|
|
2276
|
+
else:
|
|
2277
|
+
self.iocp.out_dly = value
|
|
2278
|
+
|
|
2279
|
+
elif name == presolve_intopt:
|
|
2280
|
+
if value is None:
|
|
2281
|
+
return self.iocp.presolve
|
|
2282
|
+
else:
|
|
2283
|
+
if value:
|
|
2284
|
+
value = GLP_ON
|
|
2285
|
+
else:
|
|
2286
|
+
value = GLP_OFF
|
|
2287
|
+
self.iocp.presolve = value
|
|
2288
|
+
|
|
2289
|
+
elif name == binarize:
|
|
2290
|
+
if value is None:
|
|
2291
|
+
return self.iocp.binarize
|
|
2292
|
+
else:
|
|
2293
|
+
if value:
|
|
2294
|
+
value = GLP_ON
|
|
2295
|
+
else:
|
|
2296
|
+
value = GLP_OFF
|
|
2297
|
+
self.iocp.binarize = value
|
|
2298
|
+
|
|
2299
|
+
elif name == msg_lev_simplex:
|
|
2300
|
+
if value is None:
|
|
2301
|
+
return self.smcp.msg_lev
|
|
2302
|
+
else:
|
|
2303
|
+
self.smcp.msg_lev = value
|
|
2304
|
+
|
|
2305
|
+
elif name == meth:
|
|
2306
|
+
if value is None:
|
|
2307
|
+
return self.smcp.meth
|
|
2308
|
+
else:
|
|
2309
|
+
self.smcp.meth = value
|
|
2310
|
+
|
|
2311
|
+
elif name == pricing:
|
|
2312
|
+
if value is None:
|
|
2313
|
+
return self.smcp.pricing
|
|
2314
|
+
else:
|
|
2315
|
+
self.smcp.pricing = value
|
|
2316
|
+
|
|
2317
|
+
elif name == r_test:
|
|
2318
|
+
if value is None:
|
|
2319
|
+
return self.smcp.r_test
|
|
2320
|
+
else:
|
|
2321
|
+
self.smcp.r_test = value
|
|
2322
|
+
|
|
2323
|
+
elif name == tol_bnd:
|
|
2324
|
+
if value is None:
|
|
2325
|
+
return self.smcp.tol_bnd
|
|
2326
|
+
else:
|
|
2327
|
+
self.smcp.tol_bnd = value
|
|
2328
|
+
|
|
2329
|
+
elif name == tol_dj:
|
|
2330
|
+
if value is None:
|
|
2331
|
+
return self.smcp.tol_dj
|
|
2332
|
+
else:
|
|
2333
|
+
self.smcp.tol_dj = value
|
|
2334
|
+
|
|
2335
|
+
elif name == tol_piv:
|
|
2336
|
+
if value is None:
|
|
2337
|
+
return self.smcp.tol_piv
|
|
2338
|
+
else:
|
|
2339
|
+
self.smcp.tol_piv = value
|
|
2340
|
+
|
|
2341
|
+
elif name == obj_ll:
|
|
2342
|
+
if value is None:
|
|
2343
|
+
return self.smcp.obj_ll
|
|
2344
|
+
else:
|
|
2345
|
+
self.smcp.obj_ll = value
|
|
2346
|
+
|
|
2347
|
+
elif name == obj_ul:
|
|
2348
|
+
if value is None:
|
|
2349
|
+
return self.smcp.obj_ul
|
|
2350
|
+
else:
|
|
2351
|
+
self.smcp.obj_ul = value
|
|
2352
|
+
|
|
2353
|
+
elif name == it_lim:
|
|
2354
|
+
if value is None:
|
|
2355
|
+
return self.smcp.it_lim
|
|
2356
|
+
else:
|
|
2357
|
+
self.smcp.it_lim = value
|
|
2358
|
+
|
|
2359
|
+
elif name == tm_lim_simplex:
|
|
2360
|
+
if value is None:
|
|
2361
|
+
return self.smcp.tm_lim
|
|
2362
|
+
else:
|
|
2363
|
+
self.smcp.tm_lim = value
|
|
2364
|
+
|
|
2365
|
+
elif name == out_frq_simplex:
|
|
2366
|
+
if value is None:
|
|
2367
|
+
return self.smcp.out_frq
|
|
2368
|
+
else:
|
|
2369
|
+
self.smcp.out_frq = value
|
|
2370
|
+
|
|
2371
|
+
elif name == out_dly_simplex:
|
|
2372
|
+
if value is None:
|
|
2373
|
+
return self.smcp.out_dly
|
|
2374
|
+
else:
|
|
2375
|
+
self.smcp.out_dly = value
|
|
2376
|
+
|
|
2377
|
+
elif name == presolve_simplex:
|
|
2378
|
+
if value is None:
|
|
2379
|
+
return self.smcp.presolve
|
|
2380
|
+
else:
|
|
2381
|
+
if value:
|
|
2382
|
+
value = GLP_ON
|
|
2383
|
+
else:
|
|
2384
|
+
value = GLP_OFF
|
|
2385
|
+
self.smcp.presolve = value
|
|
2386
|
+
else:
|
|
2387
|
+
raise ValueError("This parameter is not available.")
|
|
2388
|
+
|
|
2389
|
+
cpdef bint is_variable_basic(self, int index) noexcept:
|
|
2390
|
+
"""
|
|
2391
|
+
Test whether the given variable is basic.
|
|
2392
|
+
|
|
2393
|
+
This assumes that the problem has been solved with the simplex method
|
|
2394
|
+
and a basis is available. Otherwise an exception will be raised.
|
|
2395
|
+
|
|
2396
|
+
INPUT:
|
|
2397
|
+
|
|
2398
|
+
- ``index`` -- integer; the variable's id
|
|
2399
|
+
|
|
2400
|
+
EXAMPLES::
|
|
2401
|
+
|
|
2402
|
+
sage: p = MixedIntegerLinearProgram(maximization=True,\
|
|
2403
|
+
....: solver='GLPK')
|
|
2404
|
+
sage: x = p.new_variable(nonnegative=True)
|
|
2405
|
+
sage: p.add_constraint(-x[0] + x[1] <= 2)
|
|
2406
|
+
sage: p.add_constraint(8 * x[0] + 2 * x[1] <= 17)
|
|
2407
|
+
sage: p.set_objective(5.5 * x[0] - 3 * x[1])
|
|
2408
|
+
sage: b = p.get_backend()
|
|
2409
|
+
sage: import sage.numerical.backends.glpk_backend as backend
|
|
2410
|
+
sage: b.solver_parameter(backend.glp_simplex_or_intopt, backend.glp_simplex_only)
|
|
2411
|
+
sage: b.solve()
|
|
2412
|
+
0
|
|
2413
|
+
sage: b.is_variable_basic(0)
|
|
2414
|
+
True
|
|
2415
|
+
sage: b.is_variable_basic(1)
|
|
2416
|
+
False
|
|
2417
|
+
"""
|
|
2418
|
+
return self.get_col_stat(index) == GLP_BS
|
|
2419
|
+
|
|
2420
|
+
cpdef bint is_variable_nonbasic_at_lower_bound(self, int index) noexcept:
|
|
2421
|
+
"""
|
|
2422
|
+
Test whether the given variable is nonbasic at lower bound.
|
|
2423
|
+
This assumes that the problem has been solved with the simplex method
|
|
2424
|
+
and a basis is available. Otherwise an exception will be raised.
|
|
2425
|
+
|
|
2426
|
+
INPUT:
|
|
2427
|
+
|
|
2428
|
+
- ``index`` -- integer; the variable's id
|
|
2429
|
+
|
|
2430
|
+
EXAMPLES::
|
|
2431
|
+
|
|
2432
|
+
sage: p = MixedIntegerLinearProgram(maximization=True,\
|
|
2433
|
+
....: solver='GLPK')
|
|
2434
|
+
sage: x = p.new_variable(nonnegative=True)
|
|
2435
|
+
sage: p.add_constraint(-x[0] + x[1] <= 2)
|
|
2436
|
+
sage: p.add_constraint(8 * x[0] + 2 * x[1] <= 17)
|
|
2437
|
+
sage: p.set_objective(5.5 * x[0] - 3 * x[1])
|
|
2438
|
+
sage: b = p.get_backend()
|
|
2439
|
+
sage: import sage.numerical.backends.glpk_backend as backend
|
|
2440
|
+
sage: b.solver_parameter(backend.glp_simplex_or_intopt, backend.glp_simplex_only)
|
|
2441
|
+
sage: b.solve()
|
|
2442
|
+
0
|
|
2443
|
+
sage: b.is_variable_nonbasic_at_lower_bound(0)
|
|
2444
|
+
False
|
|
2445
|
+
sage: b.is_variable_nonbasic_at_lower_bound(1)
|
|
2446
|
+
True
|
|
2447
|
+
"""
|
|
2448
|
+
return self.get_col_stat(index) == GLP_NL
|
|
2449
|
+
|
|
2450
|
+
cpdef bint is_slack_variable_basic(self, int index) noexcept:
|
|
2451
|
+
"""
|
|
2452
|
+
Test whether the slack variable of the given row is basic.
|
|
2453
|
+
|
|
2454
|
+
This assumes that the problem has been solved with the simplex method
|
|
2455
|
+
and a basis is available. Otherwise an exception will be raised.
|
|
2456
|
+
|
|
2457
|
+
INPUT:
|
|
2458
|
+
|
|
2459
|
+
- ``index`` -- integer; the variable's id
|
|
2460
|
+
|
|
2461
|
+
EXAMPLES::
|
|
2462
|
+
|
|
2463
|
+
sage: p = MixedIntegerLinearProgram(maximization=True,\
|
|
2464
|
+
....: solver='GLPK')
|
|
2465
|
+
sage: x = p.new_variable(nonnegative=True)
|
|
2466
|
+
sage: p.add_constraint(-x[0] + x[1] <= 2)
|
|
2467
|
+
sage: p.add_constraint(8 * x[0] + 2 * x[1] <= 17)
|
|
2468
|
+
sage: p.set_objective(5.5 * x[0] - 3 * x[1])
|
|
2469
|
+
sage: b = p.get_backend()
|
|
2470
|
+
sage: import sage.numerical.backends.glpk_backend as backend
|
|
2471
|
+
sage: b.solver_parameter(backend.glp_simplex_or_intopt, backend.glp_simplex_only)
|
|
2472
|
+
sage: b.solve()
|
|
2473
|
+
0
|
|
2474
|
+
sage: b.is_slack_variable_basic(0)
|
|
2475
|
+
True
|
|
2476
|
+
sage: b.is_slack_variable_basic(1)
|
|
2477
|
+
False
|
|
2478
|
+
"""
|
|
2479
|
+
return self.get_row_stat(index) == GLP_BS
|
|
2480
|
+
|
|
2481
|
+
cpdef bint is_slack_variable_nonbasic_at_lower_bound(self, int index) noexcept:
|
|
2482
|
+
"""
|
|
2483
|
+
Test whether the slack variable of the given row is nonbasic at lower bound.
|
|
2484
|
+
|
|
2485
|
+
This assumes that the problem has been solved with the simplex method
|
|
2486
|
+
and a basis is available. Otherwise an exception will be raised.
|
|
2487
|
+
|
|
2488
|
+
INPUT:
|
|
2489
|
+
|
|
2490
|
+
- ``index`` -- integer; the variable's id
|
|
2491
|
+
|
|
2492
|
+
EXAMPLES::
|
|
2493
|
+
|
|
2494
|
+
sage: p = MixedIntegerLinearProgram(maximization=True,\
|
|
2495
|
+
....: solver='GLPK')
|
|
2496
|
+
sage: x = p.new_variable(nonnegative=True)
|
|
2497
|
+
sage: p.add_constraint(-x[0] + x[1] <= 2)
|
|
2498
|
+
sage: p.add_constraint(8 * x[0] + 2 * x[1] <= 17)
|
|
2499
|
+
sage: p.set_objective(5.5 * x[0] - 3 * x[1])
|
|
2500
|
+
sage: b = p.get_backend()
|
|
2501
|
+
sage: import sage.numerical.backends.glpk_backend as backend
|
|
2502
|
+
sage: b.solver_parameter(backend.glp_simplex_or_intopt, backend.glp_simplex_only)
|
|
2503
|
+
sage: b.solve()
|
|
2504
|
+
0
|
|
2505
|
+
sage: b.is_slack_variable_nonbasic_at_lower_bound(0)
|
|
2506
|
+
False
|
|
2507
|
+
sage: b.is_slack_variable_nonbasic_at_lower_bound(1)
|
|
2508
|
+
True
|
|
2509
|
+
"""
|
|
2510
|
+
return self.get_row_stat(index) == GLP_NU
|
|
2511
|
+
|
|
2512
|
+
cpdef int print_ranges(self, filename=None) except -1:
|
|
2513
|
+
r"""
|
|
2514
|
+
Print results of a sensitivity analysis
|
|
2515
|
+
|
|
2516
|
+
If no filename is given as an input the results of the
|
|
2517
|
+
sensitivity analysis are displayed on the screen. If a
|
|
2518
|
+
filename is given they are written to a file.
|
|
2519
|
+
|
|
2520
|
+
INPUT:
|
|
2521
|
+
|
|
2522
|
+
- ``filename`` -- (optional) name of the file
|
|
2523
|
+
|
|
2524
|
+
OUTPUT: zero if the operations was successful otherwise nonzero
|
|
2525
|
+
|
|
2526
|
+
.. NOTE::
|
|
2527
|
+
|
|
2528
|
+
This method is only effective if an optimal solution has been found
|
|
2529
|
+
for the lp using the simplex algorithm. In all other cases an error
|
|
2530
|
+
message is printed.
|
|
2531
|
+
|
|
2532
|
+
EXAMPLES::
|
|
2533
|
+
|
|
2534
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
2535
|
+
sage: p = get_solver(solver = "GLPK")
|
|
2536
|
+
sage: p.add_variables(2)
|
|
2537
|
+
1
|
|
2538
|
+
sage: p.add_linear_constraint(list(zip([0, 1], [1, 2])), None, 3)
|
|
2539
|
+
sage: p.set_objective([2, 5])
|
|
2540
|
+
sage: import sage.numerical.backends.glpk_backend as backend
|
|
2541
|
+
sage: p.solver_parameter(backend.glp_simplex_or_intopt, backend.glp_simplex_only)
|
|
2542
|
+
sage: p.print_ranges()
|
|
2543
|
+
glp_print_ranges: optimal basic solution required
|
|
2544
|
+
1
|
|
2545
|
+
sage: p.solve()
|
|
2546
|
+
0
|
|
2547
|
+
sage: from tempfile import NamedTemporaryFile
|
|
2548
|
+
sage: with NamedTemporaryFile(mode='r+t', suffix='.tmp') as f:
|
|
2549
|
+
....: p.print_ranges(f.name)
|
|
2550
|
+
....: for ll in f.readlines():
|
|
2551
|
+
....: if ll: print(ll)
|
|
2552
|
+
...
|
|
2553
|
+
GLPK ... - SENSITIVITY ANALYSIS REPORT Page 1
|
|
2554
|
+
Problem:
|
|
2555
|
+
Objective: 7.5 (MAXimum)
|
|
2556
|
+
No. Row name St Activity Slack Lower bound Activity Obj coef Obj value at Limiting
|
|
2557
|
+
Marginal Upper bound range range break point variable
|
|
2558
|
+
------ ------------ -- ------------- ------------- ------------- ------------- ------------- ------------- ------------
|
|
2559
|
+
1 NU 3.00000 . -Inf . -2.50000 .
|
|
2560
|
+
2.50000 3.00000 +Inf +Inf +Inf
|
|
2561
|
+
GLPK ... - SENSITIVITY ANALYSIS REPORT Page 2
|
|
2562
|
+
Problem:
|
|
2563
|
+
Objective: 7.5 (MAXimum)
|
|
2564
|
+
No. Column name St Activity Obj coef Lower bound Activity Obj coef Obj value at Limiting
|
|
2565
|
+
Marginal Upper bound range range break point variable
|
|
2566
|
+
------ ------------ -- ------------- ------------- ------------- ------------- ------------- ------------- ------------
|
|
2567
|
+
1 NL . 2.00000 . -Inf -Inf +Inf
|
|
2568
|
+
-.50000 +Inf 3.00000 2.50000 6.00000
|
|
2569
|
+
2 BS 1.50000 5.00000 . -Inf 4.00000 6.00000
|
|
2570
|
+
. +Inf 1.50000 +Inf +Inf
|
|
2571
|
+
End of report
|
|
2572
|
+
"""
|
|
2573
|
+
if filename is None:
|
|
2574
|
+
import tempfile
|
|
2575
|
+
with tempfile.NamedTemporaryFile() as f:
|
|
2576
|
+
res = glp_print_ranges(self.lp, 0, 0, 0,
|
|
2577
|
+
str_to_bytes(f.name,
|
|
2578
|
+
FS_ENCODING,
|
|
2579
|
+
'surrogateescape'))
|
|
2580
|
+
if res == 0:
|
|
2581
|
+
# Success; now write it to stdout.
|
|
2582
|
+
with open(f.name) as fh:
|
|
2583
|
+
for line in fh:
|
|
2584
|
+
print(line, end=" ")
|
|
2585
|
+
print("\n")
|
|
2586
|
+
else:
|
|
2587
|
+
res = glp_print_ranges(self.lp, 0, 0, 0,
|
|
2588
|
+
str_to_bytes(filename,
|
|
2589
|
+
FS_ENCODING,
|
|
2590
|
+
'surrogateescape'))
|
|
2591
|
+
return res
|
|
2592
|
+
|
|
2593
|
+
cpdef double get_row_dual(self, int variable) noexcept:
|
|
2594
|
+
r"""
|
|
2595
|
+
Return the dual value of a constraint.
|
|
2596
|
+
|
|
2597
|
+
The dual value of the i-th row is also the value of the i-th variable
|
|
2598
|
+
of the dual problem.
|
|
2599
|
+
|
|
2600
|
+
The dual value of a constraint is the shadow price of the constraint.
|
|
2601
|
+
The shadow price is the amount by which the objective value will change
|
|
2602
|
+
if the constraints bounds change by one unit under the precondition
|
|
2603
|
+
that the basis remains the same.
|
|
2604
|
+
|
|
2605
|
+
INPUT:
|
|
2606
|
+
|
|
2607
|
+
- ``variable`` -- the number of the constraint
|
|
2608
|
+
|
|
2609
|
+
.. NOTE::
|
|
2610
|
+
|
|
2611
|
+
Behaviour is undefined unless ``solve`` has been called before.
|
|
2612
|
+
If the simplex algorithm has not been used for solving 0.0 will
|
|
2613
|
+
be returned.
|
|
2614
|
+
|
|
2615
|
+
EXAMPLES::
|
|
2616
|
+
|
|
2617
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
2618
|
+
sage: lp = get_solver(solver = "GLPK")
|
|
2619
|
+
sage: lp.add_variables(3)
|
|
2620
|
+
2
|
|
2621
|
+
sage: lp.add_linear_constraint(list(zip([0, 1, 2], [8, 6, 1])), None, 48)
|
|
2622
|
+
sage: lp.add_linear_constraint(list(zip([0, 1, 2], [4, 2, 1.5])), None, 20)
|
|
2623
|
+
sage: lp.add_linear_constraint(list(zip([0, 1, 2], [2, 1.5, 0.5])), None, 8)
|
|
2624
|
+
sage: lp.set_objective([60, 30, 20])
|
|
2625
|
+
sage: import sage.numerical.backends.glpk_backend as backend
|
|
2626
|
+
sage: lp.solver_parameter(backend.glp_simplex_or_intopt, backend.glp_simplex_only)
|
|
2627
|
+
sage: lp.solve()
|
|
2628
|
+
0
|
|
2629
|
+
sage: lp.get_row_dual(0) # tolerance 0.00001
|
|
2630
|
+
0.0
|
|
2631
|
+
sage: lp.get_row_dual(1) # tolerance 0.00001
|
|
2632
|
+
10.0
|
|
2633
|
+
"""
|
|
2634
|
+
|
|
2635
|
+
if self.simplex_or_intopt == simplex_only:
|
|
2636
|
+
return glp_get_row_dual(self.lp, variable+1)
|
|
2637
|
+
else:
|
|
2638
|
+
return 0.0
|
|
2639
|
+
|
|
2640
|
+
cpdef double get_col_dual(self, int variable) except? -1:
|
|
2641
|
+
"""
|
|
2642
|
+
Return the dual value (reduced cost) of a variable
|
|
2643
|
+
|
|
2644
|
+
The dual value is the reduced cost of a variable.
|
|
2645
|
+
The reduced cost is the amount by which the objective coefficient
|
|
2646
|
+
of a non basic variable has to change to become a basic variable.
|
|
2647
|
+
|
|
2648
|
+
INPUT:
|
|
2649
|
+
|
|
2650
|
+
- ``variable`` -- the number of the variable
|
|
2651
|
+
|
|
2652
|
+
.. NOTE::
|
|
2653
|
+
|
|
2654
|
+
Behaviour is undefined unless ``solve`` has been called before.
|
|
2655
|
+
If the simplex algorithm has not been used for solving just a
|
|
2656
|
+
0.0 will be returned.
|
|
2657
|
+
|
|
2658
|
+
EXAMPLES::
|
|
2659
|
+
|
|
2660
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
2661
|
+
sage: p = get_solver(solver = "GLPK")
|
|
2662
|
+
sage: p.add_variables(3)
|
|
2663
|
+
2
|
|
2664
|
+
sage: p.add_linear_constraint(list(zip([0, 1, 2], [8, 6, 1])), None, 48)
|
|
2665
|
+
sage: p.add_linear_constraint(list(zip([0, 1, 2], [4, 2, 1.5])), None, 20)
|
|
2666
|
+
sage: p.add_linear_constraint(list(zip([0, 1, 2], [2, 1.5, 0.5])), None, 8)
|
|
2667
|
+
sage: p.set_objective([60, 30, 20])
|
|
2668
|
+
sage: import sage.numerical.backends.glpk_backend as backend
|
|
2669
|
+
sage: p.solver_parameter(backend.glp_simplex_or_intopt, backend.glp_simplex_only)
|
|
2670
|
+
sage: p.solve()
|
|
2671
|
+
0
|
|
2672
|
+
sage: p.get_col_dual(1)
|
|
2673
|
+
-5.0
|
|
2674
|
+
|
|
2675
|
+
TESTS:
|
|
2676
|
+
|
|
2677
|
+
We sanity check the input that will be passed to GLPK::
|
|
2678
|
+
|
|
2679
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
2680
|
+
sage: p = get_solver(solver='GLPK')
|
|
2681
|
+
sage: p.get_col_dual(2)
|
|
2682
|
+
Traceback (most recent call last):
|
|
2683
|
+
...
|
|
2684
|
+
ValueError: invalid column index 2
|
|
2685
|
+
"""
|
|
2686
|
+
if variable < 0 or variable > (self.ncols() - 1):
|
|
2687
|
+
raise ValueError("invalid column index %d" % variable)
|
|
2688
|
+
|
|
2689
|
+
if self.simplex_or_intopt == simplex_only:
|
|
2690
|
+
return glp_get_col_dual(self.lp, variable+1)
|
|
2691
|
+
else:
|
|
2692
|
+
return 0.0
|
|
2693
|
+
|
|
2694
|
+
cpdef int get_row_stat(self, int i) except? -1:
|
|
2695
|
+
"""
|
|
2696
|
+
Retrieve the status of a constraint.
|
|
2697
|
+
|
|
2698
|
+
INPUT:
|
|
2699
|
+
|
|
2700
|
+
- ``i`` -- the index of the constraint
|
|
2701
|
+
|
|
2702
|
+
OUTPUT:
|
|
2703
|
+
|
|
2704
|
+
Current status assigned to the auxiliary variable associated with i-th
|
|
2705
|
+
row:
|
|
2706
|
+
|
|
2707
|
+
* GLP_BS = 1 basic variable
|
|
2708
|
+
* GLP_NL = 2 non-basic variable on lower bound
|
|
2709
|
+
* GLP_NU = 3 non-basic variable on upper bound
|
|
2710
|
+
* GLP_NF = 4 non-basic free (unbounded) variable
|
|
2711
|
+
* GLP_NS = 5 non-basic fixed variable
|
|
2712
|
+
|
|
2713
|
+
EXAMPLES::
|
|
2714
|
+
|
|
2715
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
2716
|
+
sage: lp = get_solver(solver = "GLPK")
|
|
2717
|
+
sage: lp.add_variables(3)
|
|
2718
|
+
2
|
|
2719
|
+
sage: lp.add_linear_constraint(list(zip([0, 1, 2], [8, 6, 1])), None, 48)
|
|
2720
|
+
sage: lp.add_linear_constraint(list(zip([0, 1, 2], [4, 2, 1.5])), None, 20)
|
|
2721
|
+
sage: lp.add_linear_constraint(list(zip([0, 1, 2], [2, 1.5, 0.5])), None, 8)
|
|
2722
|
+
sage: lp.set_objective([60, 30, 20])
|
|
2723
|
+
sage: import sage.numerical.backends.glpk_backend as backend
|
|
2724
|
+
sage: lp.solver_parameter(backend.glp_simplex_or_intopt, backend.glp_simplex_only)
|
|
2725
|
+
sage: lp.solve()
|
|
2726
|
+
0
|
|
2727
|
+
sage: lp.get_row_stat(0)
|
|
2728
|
+
1
|
|
2729
|
+
sage: lp.get_row_stat(1)
|
|
2730
|
+
3
|
|
2731
|
+
sage: lp.get_row_stat(-1)
|
|
2732
|
+
Traceback (most recent call last):
|
|
2733
|
+
...
|
|
2734
|
+
ValueError: The constraint's index i must satisfy 0 <= i < number_of_constraints
|
|
2735
|
+
"""
|
|
2736
|
+
if i < 0 or i >= glp_get_num_rows(self.lp):
|
|
2737
|
+
raise ValueError("The constraint's index i must satisfy 0 <= i < number_of_constraints")
|
|
2738
|
+
return glp_get_row_stat(self.lp, i+1)
|
|
2739
|
+
|
|
2740
|
+
cpdef int get_col_stat(self, int j) except? -1:
|
|
2741
|
+
"""
|
|
2742
|
+
Retrieve the status of a variable.
|
|
2743
|
+
|
|
2744
|
+
INPUT:
|
|
2745
|
+
|
|
2746
|
+
- ``j`` -- the index of the variable
|
|
2747
|
+
|
|
2748
|
+
OUTPUT:
|
|
2749
|
+
|
|
2750
|
+
Current status assigned to the structural variable associated
|
|
2751
|
+
with j-th column:
|
|
2752
|
+
|
|
2753
|
+
* GLP_BS = 1 basic variable
|
|
2754
|
+
* GLP_NL = 2 non-basic variable on lower bound
|
|
2755
|
+
* GLP_NU = 3 non-basic variable on upper bound
|
|
2756
|
+
* GLP_NF = 4 non-basic free (unbounded) variable
|
|
2757
|
+
* GLP_NS = 5 non-basic fixed variable
|
|
2758
|
+
|
|
2759
|
+
EXAMPLES::
|
|
2760
|
+
|
|
2761
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
2762
|
+
sage: lp = get_solver(solver = "GLPK")
|
|
2763
|
+
sage: lp.add_variables(3)
|
|
2764
|
+
2
|
|
2765
|
+
sage: lp.add_linear_constraint(list(zip([0, 1, 2], [8, 6, 1])), None, 48)
|
|
2766
|
+
sage: lp.add_linear_constraint(list(zip([0, 1, 2], [4, 2, 1.5])), None, 20)
|
|
2767
|
+
sage: lp.add_linear_constraint(list(zip([0, 1, 2], [2, 1.5, 0.5])), None, 8)
|
|
2768
|
+
sage: lp.set_objective([60, 30, 20])
|
|
2769
|
+
sage: import sage.numerical.backends.glpk_backend as backend
|
|
2770
|
+
sage: lp.solver_parameter(backend.glp_simplex_or_intopt, backend.glp_simplex_only)
|
|
2771
|
+
sage: lp.solve()
|
|
2772
|
+
0
|
|
2773
|
+
sage: lp.get_col_stat(0)
|
|
2774
|
+
1
|
|
2775
|
+
sage: lp.get_col_stat(1)
|
|
2776
|
+
2
|
|
2777
|
+
sage: lp.get_col_stat(100)
|
|
2778
|
+
Traceback (most recent call last):
|
|
2779
|
+
...
|
|
2780
|
+
ValueError: The variable's index j must satisfy 0 <= j < number_of_variables
|
|
2781
|
+
"""
|
|
2782
|
+
if j < 0 or j >= glp_get_num_cols(self.lp):
|
|
2783
|
+
raise ValueError("The variable's index j must satisfy 0 <= j < number_of_variables")
|
|
2784
|
+
|
|
2785
|
+
return glp_get_col_stat(self.lp, j+1)
|
|
2786
|
+
|
|
2787
|
+
cpdef set_row_stat(self, int i, int stat):
|
|
2788
|
+
r"""
|
|
2789
|
+
Set the status of a constraint.
|
|
2790
|
+
|
|
2791
|
+
INPUT:
|
|
2792
|
+
|
|
2793
|
+
- ``i`` -- the index of the constraint
|
|
2794
|
+
|
|
2795
|
+
- ``stat`` -- the status to set to
|
|
2796
|
+
|
|
2797
|
+
EXAMPLES::
|
|
2798
|
+
|
|
2799
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
2800
|
+
sage: lp = get_solver(solver = "GLPK")
|
|
2801
|
+
sage: lp.add_variables(3)
|
|
2802
|
+
2
|
|
2803
|
+
sage: lp.add_linear_constraint(list(zip([0, 1, 2], [8, 6, 1])), None, 48)
|
|
2804
|
+
sage: lp.add_linear_constraint(list(zip([0, 1, 2], [4, 2, 1.5])), None, 20)
|
|
2805
|
+
sage: lp.add_linear_constraint(list(zip([0, 1, 2], [2, 1.5, 0.5])), None, 8)
|
|
2806
|
+
sage: lp.set_objective([60, 30, 20])
|
|
2807
|
+
sage: import sage.numerical.backends.glpk_backend as backend
|
|
2808
|
+
sage: lp.solver_parameter(backend.glp_simplex_or_intopt, backend.glp_simplex_only)
|
|
2809
|
+
sage: lp.solve()
|
|
2810
|
+
0
|
|
2811
|
+
sage: lp.get_row_stat(0)
|
|
2812
|
+
1
|
|
2813
|
+
sage: lp.set_row_stat(0, 3)
|
|
2814
|
+
sage: lp.get_row_stat(0)
|
|
2815
|
+
3
|
|
2816
|
+
"""
|
|
2817
|
+
if i < 0 or i >= glp_get_num_rows(self.lp):
|
|
2818
|
+
raise ValueError("The constraint's index i must satisfy 0 <= i < number_of_constraints")
|
|
2819
|
+
|
|
2820
|
+
glp_set_row_stat(self.lp, i+1, stat)
|
|
2821
|
+
|
|
2822
|
+
cpdef set_col_stat(self, int j, int stat):
|
|
2823
|
+
r"""
|
|
2824
|
+
Set the status of a variable.
|
|
2825
|
+
|
|
2826
|
+
INPUT:
|
|
2827
|
+
|
|
2828
|
+
- ``j`` -- the index of the constraint
|
|
2829
|
+
|
|
2830
|
+
- ``stat`` -- the status to set to
|
|
2831
|
+
|
|
2832
|
+
EXAMPLES::
|
|
2833
|
+
|
|
2834
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
2835
|
+
sage: lp = get_solver(solver = "GLPK")
|
|
2836
|
+
sage: lp.add_variables(3)
|
|
2837
|
+
2
|
|
2838
|
+
sage: lp.add_linear_constraint(list(zip([0, 1, 2], [8, 6, 1])), None, 48)
|
|
2839
|
+
sage: lp.add_linear_constraint(list(zip([0, 1, 2], [4, 2, 1.5])), None, 20)
|
|
2840
|
+
sage: lp.add_linear_constraint(list(zip([0, 1, 2], [2, 1.5, 0.5])), None, 8)
|
|
2841
|
+
sage: lp.set_objective([60, 30, 20])
|
|
2842
|
+
sage: import sage.numerical.backends.glpk_backend as backend
|
|
2843
|
+
sage: lp.solver_parameter(backend.glp_simplex_or_intopt, backend.glp_simplex_only)
|
|
2844
|
+
sage: lp.solve()
|
|
2845
|
+
0
|
|
2846
|
+
sage: lp.get_col_stat(0)
|
|
2847
|
+
1
|
|
2848
|
+
sage: lp.set_col_stat(0, 2)
|
|
2849
|
+
sage: lp.get_col_stat(0)
|
|
2850
|
+
2
|
|
2851
|
+
"""
|
|
2852
|
+
if j < 0 or j >= glp_get_num_cols(self.lp):
|
|
2853
|
+
raise ValueError("The variable's index j must satisfy 0 <= j < number_of_variables")
|
|
2854
|
+
|
|
2855
|
+
glp_set_col_stat(self.lp, j+1, stat)
|
|
2856
|
+
|
|
2857
|
+
cpdef int warm_up(self) noexcept:
|
|
2858
|
+
r"""
|
|
2859
|
+
Warm up the basis using current statuses assigned to rows and cols.
|
|
2860
|
+
|
|
2861
|
+
OUTPUT: the warming up status
|
|
2862
|
+
|
|
2863
|
+
* 0 The operation has been successfully performed.
|
|
2864
|
+
* GLP_EBADB The basis matrix is invalid.
|
|
2865
|
+
* GLP_ESING The basis matrix is singular within the working precision.
|
|
2866
|
+
* GLP_ECOND The basis matrix is ill-conditioned.
|
|
2867
|
+
|
|
2868
|
+
EXAMPLES::
|
|
2869
|
+
|
|
2870
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
2871
|
+
sage: lp = get_solver(solver = "GLPK")
|
|
2872
|
+
sage: lp.add_variables(3)
|
|
2873
|
+
2
|
|
2874
|
+
sage: lp.add_linear_constraint(list(zip([0, 1, 2], [8, 6, 1])), None, 48)
|
|
2875
|
+
sage: lp.add_linear_constraint(list(zip([0, 1, 2], [4, 2, 1.5])), None, 20)
|
|
2876
|
+
sage: lp.add_linear_constraint(list(zip([0, 1, 2], [2, 1.5, 0.5])), None, 8)
|
|
2877
|
+
sage: lp.set_objective([60, 30, 20])
|
|
2878
|
+
sage: import sage.numerical.backends.glpk_backend as backend
|
|
2879
|
+
sage: lp.solver_parameter(backend.glp_simplex_or_intopt, backend.glp_simplex_only)
|
|
2880
|
+
sage: lp.solve()
|
|
2881
|
+
0
|
|
2882
|
+
sage: lp.get_objective_value()
|
|
2883
|
+
280.0
|
|
2884
|
+
sage: lp.set_row_stat(0,3)
|
|
2885
|
+
sage: lp.set_col_stat(1,1)
|
|
2886
|
+
sage: lp.warm_up()
|
|
2887
|
+
0
|
|
2888
|
+
"""
|
|
2889
|
+
return glp_warm_up(self.lp)
|
|
2890
|
+
|
|
2891
|
+
cpdef eval_tab_row(self, int k):
|
|
2892
|
+
r"""
|
|
2893
|
+
Compute a row of the current simplex tableau.
|
|
2894
|
+
|
|
2895
|
+
A row corresponds to some basic variable specified by the parameter
|
|
2896
|
+
``k`` as follows:
|
|
2897
|
+
|
|
2898
|
+
- if `0 \leq k \leq m-1`, the basic variable is `k`-th auxiliary
|
|
2899
|
+
variable,
|
|
2900
|
+
|
|
2901
|
+
- if `m \leq k \leq m+n-1`, the basic variable is `(k-m)`-th structural
|
|
2902
|
+
variable,
|
|
2903
|
+
|
|
2904
|
+
where `m` is the number of rows and `n` is the number of columns in the
|
|
2905
|
+
specified problem object.
|
|
2906
|
+
|
|
2907
|
+
.. NOTE::
|
|
2908
|
+
|
|
2909
|
+
The basis factorization must exist and the variable with
|
|
2910
|
+
index ``k`` must be basic. Otherwise, a :exc:`ValueError` is
|
|
2911
|
+
be raised.
|
|
2912
|
+
|
|
2913
|
+
INPUT:
|
|
2914
|
+
|
|
2915
|
+
- ``k`` -- integer; the id of the basic variable
|
|
2916
|
+
|
|
2917
|
+
OUTPUT:
|
|
2918
|
+
|
|
2919
|
+
A pair ``(indices, coeffs)`` where ``indices`` lists the
|
|
2920
|
+
entries whose coefficient is nonzero, and to which ``coeffs``
|
|
2921
|
+
associates their coefficient in the computed row
|
|
2922
|
+
of the current simplex tableau.
|
|
2923
|
+
|
|
2924
|
+
.. NOTE::
|
|
2925
|
+
|
|
2926
|
+
Elements in ``indices`` have the same sense as index ``k``.
|
|
2927
|
+
All these variables are non-basic by definition.
|
|
2928
|
+
|
|
2929
|
+
EXAMPLES::
|
|
2930
|
+
|
|
2931
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
2932
|
+
sage: lp = get_solver(solver = "GLPK")
|
|
2933
|
+
sage: lp.add_variables(3)
|
|
2934
|
+
2
|
|
2935
|
+
sage: lp.add_linear_constraint(list(zip([0, 1, 2], [8, 6, 1])), None, 48)
|
|
2936
|
+
sage: lp.add_linear_constraint(list(zip([0, 1, 2], [4, 2, 1.5])), None, 20)
|
|
2937
|
+
sage: lp.add_linear_constraint(list(zip([0, 1, 2], [2, 1.5, 0.5])), None, 8)
|
|
2938
|
+
sage: lp.set_objective([60, 30, 20])
|
|
2939
|
+
sage: import sage.numerical.backends.glpk_backend as backend
|
|
2940
|
+
sage: lp.solver_parameter(backend.glp_simplex_or_intopt, backend.glp_simplex_only)
|
|
2941
|
+
sage: lp.eval_tab_row(0)
|
|
2942
|
+
Traceback (most recent call last):
|
|
2943
|
+
...
|
|
2944
|
+
ValueError: basis factorization does not exist
|
|
2945
|
+
sage: lp.solve()
|
|
2946
|
+
0
|
|
2947
|
+
sage: lp.eval_tab_row(0)
|
|
2948
|
+
([1, 2, 4], [-2.0, 8.0, -2.0])
|
|
2949
|
+
sage: lp.eval_tab_row(3)
|
|
2950
|
+
([1, 2, 4], [-0.5, 1.5, -1.25])
|
|
2951
|
+
sage: lp.eval_tab_row(5)
|
|
2952
|
+
([1, 2, 4], [2.0, -4.0, 2.0])
|
|
2953
|
+
sage: lp.eval_tab_row(1)
|
|
2954
|
+
Traceback (most recent call last):
|
|
2955
|
+
...
|
|
2956
|
+
ValueError: slack variable 1 is not basic
|
|
2957
|
+
sage: lp.eval_tab_row(-1)
|
|
2958
|
+
Traceback (most recent call last):
|
|
2959
|
+
...
|
|
2960
|
+
ValueError: ...
|
|
2961
|
+
"""
|
|
2962
|
+
cdef int m = self.nrows()
|
|
2963
|
+
cdef int n = self.ncols()
|
|
2964
|
+
cdef int i,j
|
|
2965
|
+
|
|
2966
|
+
if k < 0 or k >= n + m:
|
|
2967
|
+
raise ValueError("k = %s; Variable number out of range" % k)
|
|
2968
|
+
|
|
2969
|
+
if glp_bf_exists(self.lp) == 0:
|
|
2970
|
+
raise ValueError("basis factorization does not exist")
|
|
2971
|
+
|
|
2972
|
+
if k < m:
|
|
2973
|
+
if not self.is_slack_variable_basic(k):
|
|
2974
|
+
raise ValueError("slack variable %d is not basic" % k)
|
|
2975
|
+
else:
|
|
2976
|
+
if not self.is_variable_basic(k-m):
|
|
2977
|
+
raise ValueError("variable %d is not basic" % (k-m) )
|
|
2978
|
+
|
|
2979
|
+
cdef MemoryAllocator mem = MemoryAllocator()
|
|
2980
|
+
cdef int * c_indices = <int*>mem.allocarray(n+1, sizeof(int))
|
|
2981
|
+
cdef double * c_values = <double*>mem.allocarray(n+1, sizeof(double))
|
|
2982
|
+
|
|
2983
|
+
i = glp_eval_tab_row(self.lp, k + 1, c_indices, c_values)
|
|
2984
|
+
|
|
2985
|
+
indices = [c_indices[j + 1] - 1 for j in range(i)]
|
|
2986
|
+
values = [c_values[j + 1] for j in range(i)]
|
|
2987
|
+
return (indices, values)
|
|
2988
|
+
|
|
2989
|
+
cpdef eval_tab_col(self, int k):
|
|
2990
|
+
r"""
|
|
2991
|
+
Compute a column of the current simplex tableau.
|
|
2992
|
+
|
|
2993
|
+
A (column) corresponds to some non-basic variable specified by the
|
|
2994
|
+
parameter ``k`` as follows:
|
|
2995
|
+
|
|
2996
|
+
- if `0 \leq k \leq m-1`, the non-basic variable is `k`-th auxiliary
|
|
2997
|
+
variable,
|
|
2998
|
+
|
|
2999
|
+
- if `m \leq k \leq m+n-1`, the non-basic variable is `(k-m)`-th
|
|
3000
|
+
structural variable,
|
|
3001
|
+
|
|
3002
|
+
where `m` is the number of rows and `n` is the number of columns
|
|
3003
|
+
in the specified problem object.
|
|
3004
|
+
|
|
3005
|
+
.. NOTE::
|
|
3006
|
+
|
|
3007
|
+
The basis factorization must exist and the variable with
|
|
3008
|
+
index ``k`` must not be basic. Otherwise, a :exc:`ValueError` is
|
|
3009
|
+
be raised.
|
|
3010
|
+
|
|
3011
|
+
INPUT:
|
|
3012
|
+
|
|
3013
|
+
- ``k`` -- integer; the id of the non-basic variable
|
|
3014
|
+
|
|
3015
|
+
OUTPUT:
|
|
3016
|
+
|
|
3017
|
+
A pair ``(indices, coeffs)`` where ``indices`` lists the
|
|
3018
|
+
entries whose coefficient is nonzero, and to which ``coeffs``
|
|
3019
|
+
associates their coefficient in the computed column
|
|
3020
|
+
of the current simplex tableau.
|
|
3021
|
+
|
|
3022
|
+
.. NOTE::
|
|
3023
|
+
|
|
3024
|
+
Elements in ``indices`` have the same sense as index `k`.
|
|
3025
|
+
All these variables are basic by definition.
|
|
3026
|
+
|
|
3027
|
+
EXAMPLES::
|
|
3028
|
+
|
|
3029
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
3030
|
+
sage: lp = get_solver(solver = "GLPK")
|
|
3031
|
+
sage: lp.add_variables(3)
|
|
3032
|
+
2
|
|
3033
|
+
sage: lp.add_linear_constraint(list(zip([0, 1, 2], [8, 6, 1])), None, 48)
|
|
3034
|
+
sage: lp.add_linear_constraint(list(zip([0, 1, 2], [4, 2, 1.5])), None, 20)
|
|
3035
|
+
sage: lp.add_linear_constraint(list(zip([0, 1, 2], [2, 1.5, 0.5])), None, 8)
|
|
3036
|
+
sage: lp.set_objective([60, 30, 20])
|
|
3037
|
+
sage: import sage.numerical.backends.glpk_backend as backend
|
|
3038
|
+
sage: lp.solver_parameter(backend.glp_simplex_or_intopt, backend.glp_simplex_only)
|
|
3039
|
+
sage: lp.eval_tab_col(1)
|
|
3040
|
+
Traceback (most recent call last):
|
|
3041
|
+
...
|
|
3042
|
+
ValueError: basis factorization does not exist
|
|
3043
|
+
sage: lp.solve()
|
|
3044
|
+
0
|
|
3045
|
+
sage: lp.eval_tab_col(1)
|
|
3046
|
+
([0, 5, 3], [-2.0, 2.0, -0.5])
|
|
3047
|
+
sage: lp.eval_tab_col(2)
|
|
3048
|
+
([0, 5, 3], [8.0, -4.0, 1.5])
|
|
3049
|
+
sage: lp.eval_tab_col(4)
|
|
3050
|
+
([0, 5, 3], [-2.0, 2.0, -1.25])
|
|
3051
|
+
sage: lp.eval_tab_col(0)
|
|
3052
|
+
Traceback (most recent call last):
|
|
3053
|
+
...
|
|
3054
|
+
ValueError: slack variable 0 is basic
|
|
3055
|
+
sage: lp.eval_tab_col(-1)
|
|
3056
|
+
Traceback (most recent call last):
|
|
3057
|
+
...
|
|
3058
|
+
ValueError: ...
|
|
3059
|
+
"""
|
|
3060
|
+
cdef int m = self.nrows()
|
|
3061
|
+
cdef int n = self.ncols()
|
|
3062
|
+
cdef int i,j
|
|
3063
|
+
|
|
3064
|
+
if k < 0 or k >= m + n:
|
|
3065
|
+
raise ValueError("k = %s; Variable number out of range" % k)
|
|
3066
|
+
|
|
3067
|
+
if glp_bf_exists(self.lp) == 0:
|
|
3068
|
+
raise ValueError("basis factorization does not exist")
|
|
3069
|
+
|
|
3070
|
+
if k < m:
|
|
3071
|
+
if self.is_slack_variable_basic(k):
|
|
3072
|
+
raise ValueError("slack variable %d is basic" % k)
|
|
3073
|
+
else:
|
|
3074
|
+
if self.is_variable_basic(k-m):
|
|
3075
|
+
raise ValueError("variable %d is basic" % (k-m) )
|
|
3076
|
+
|
|
3077
|
+
cdef MemoryAllocator mem = MemoryAllocator()
|
|
3078
|
+
cdef int * c_indices = <int*>mem.allocarray(m+1, sizeof(int))
|
|
3079
|
+
cdef double * c_values = <double*>mem.allocarray(m+1, sizeof(double))
|
|
3080
|
+
|
|
3081
|
+
i = glp_eval_tab_col(self.lp, k + 1, c_indices, c_values)
|
|
3082
|
+
|
|
3083
|
+
indices = [c_indices[j + 1] - 1 for j in range(i)]
|
|
3084
|
+
values = [c_values[j + 1] for j in range(i)]
|
|
3085
|
+
return (indices, values)
|
|
3086
|
+
|
|
3087
|
+
def __dealloc__(self):
|
|
3088
|
+
"""
|
|
3089
|
+
Destructor
|
|
3090
|
+
"""
|
|
3091
|
+
glp_delete_prob(self.lp)
|
|
3092
|
+
sig_free(self.iocp)
|
|
3093
|
+
sig_free(self.smcp)
|
|
3094
|
+
|
|
3095
|
+
cdef void glp_callback(glp_tree* tree, void* info) noexcept:
|
|
3096
|
+
r"""
|
|
3097
|
+
A callback routine called by glp_intopt
|
|
3098
|
+
|
|
3099
|
+
This function fills the ``search_tree_data`` structure of a ``GLPKBackend``
|
|
3100
|
+
object. It is fed to ``glp_intopt`` (which calls it while the search tree is
|
|
3101
|
+
being built) through ``iocp.cb_func``.
|
|
3102
|
+
|
|
3103
|
+
INPUT:
|
|
3104
|
+
|
|
3105
|
+
- ``tree`` -- a pointer toward ``glp_tree``, which is GLPK's search tree
|
|
3106
|
+
|
|
3107
|
+
- ``info`` -- a ``void *`` to let the function know *where* it should store
|
|
3108
|
+
the data we need. The value of ``info`` is equal to the one stored in
|
|
3109
|
+
iocp.cb_info.
|
|
3110
|
+
"""
|
|
3111
|
+
cdef search_tree_data_t * data = <search_tree_data_t *> info
|
|
3112
|
+
data.mip_gap = glp_ios_mip_gap(tree)
|
|
3113
|
+
|
|
3114
|
+
cdef int node_id = glp_ios_best_node(tree)
|
|
3115
|
+
data.best_bound = glp_ios_node_bound(tree, node_id)
|
|
3116
|
+
|
|
3117
|
+
# parameter names
|
|
3118
|
+
|
|
3119
|
+
cdef enum solver_parameter_names:
|
|
3120
|
+
timelimit_seconds, timelimit_simplex, timelimit_intopt, simplex_or_intopt,
|
|
3121
|
+
msg_lev_simplex,
|
|
3122
|
+
msg_lev_intopt, br_tech, bt_tech, pp_tech, fp_heur, gmi_cuts,
|
|
3123
|
+
mir_cuts, cov_cuts, clq_cuts, tol_int, tol_obj, mip_gap,
|
|
3124
|
+
tm_lim_intopt, out_frq_intopt, out_dly_intopt, presolve_intopt,
|
|
3125
|
+
binarize, meth, pricing, r_test, tol_bnd, tol_dj, tol_piv, obj_ll,
|
|
3126
|
+
obj_ul, it_lim, tm_lim_simplex, out_frq_simplex, out_dly_simplex,
|
|
3127
|
+
presolve_simplex
|
|
3128
|
+
|
|
3129
|
+
glp_tm_lim_simplex = timelimit_simplex
|
|
3130
|
+
glp_tm_lim_intopt = timelimit_intopt
|
|
3131
|
+
glp_simplex_or_intopt = simplex_or_intopt
|
|
3132
|
+
glp_msg_lev_intopt = glp_verbosity_intopt = msg_lev_intopt
|
|
3133
|
+
glp_msg_lev_simplex = glp_verbosity_simplex = msg_lev_simplex
|
|
3134
|
+
glp_br_tech = glp_branching = br_tech
|
|
3135
|
+
glp_bt_tech = glp_backtracking = bt_tech
|
|
3136
|
+
glp_pp_tech = glp_preprocessing = pp_tech
|
|
3137
|
+
glp_fp_heur = glp_feasibility_pump = fp_heur
|
|
3138
|
+
glp_gmi_cuts = glp_gomory_cuts = gmi_cuts
|
|
3139
|
+
glp_mir_cuts = glp_mixed_int_rounding_cuts = mir_cuts
|
|
3140
|
+
glp_cov_cuts = glp_mixed_cover_cuts = cov_cuts
|
|
3141
|
+
glp_clq_cuts = glp_clique_cuts = clq_cuts
|
|
3142
|
+
glp_tol_int = glp_absolute_tolerance = tol_int
|
|
3143
|
+
glp_tol_obj = glp_relative_tolerance = tol_obj
|
|
3144
|
+
glp_mip_gap = glp_mip_gap_tolerance = mip_gap
|
|
3145
|
+
glp_out_frq_intopt = glp_output_frequency_intopt = out_frq_intopt
|
|
3146
|
+
glp_out_dly_intopt = glp_output_delay_intopt = out_dly_intopt
|
|
3147
|
+
glp_presolve_intopt = presolve_intopt
|
|
3148
|
+
glp_binarize = binarize
|
|
3149
|
+
glp_meth = glp_primal_v_dual = meth
|
|
3150
|
+
glp_pricing = pricing
|
|
3151
|
+
glp_r_test = glp_ratio_test = r_test
|
|
3152
|
+
glp_tol_bnd = glp_tolerance_primal = tol_bnd
|
|
3153
|
+
glp_tol_dj = glp_tolerance_dual = tol_dj
|
|
3154
|
+
glp_tol_piv = glp_tolerance_pivot = tol_piv
|
|
3155
|
+
glp_obj_ll = glp_obj_lower_limit = obj_ll
|
|
3156
|
+
glp_obj_ul = glp_obj_upper_limit = obj_ul
|
|
3157
|
+
glp_it_lim = glp_iteration_limit = it_lim
|
|
3158
|
+
glp_out_frq_simplex = glp_output_frequency_intopt = out_frq_simplex
|
|
3159
|
+
glp_out_dly_simplex = glp_output_delay_simplex = out_dly_simplex
|
|
3160
|
+
glp_presolve_simplex = presolve_simplex
|
|
3161
|
+
|
|
3162
|
+
solver_parameter_names_dict = {
|
|
3163
|
+
'timelimit': timelimit_seconds,
|
|
3164
|
+
'timelimit_intopt': timelimit_intopt,
|
|
3165
|
+
'tm_lim_intopt': timelimit_intopt,
|
|
3166
|
+
'timelimit_simplex': timelimit_simplex,
|
|
3167
|
+
'tm_lim_simplex': timelimit_simplex,
|
|
3168
|
+
'simplex_or_intopt': simplex_or_intopt,
|
|
3169
|
+
'msg_lev_simplex': msg_lev_simplex, 'verbosity_simplex': msg_lev_simplex,
|
|
3170
|
+
'msg_lev_intopt': msg_lev_intopt, 'verbosity_intopt': msg_lev_intopt,
|
|
3171
|
+
'br_tech': br_tech, 'branching': br_tech,
|
|
3172
|
+
'bt_tech': bt_tech, 'backtracking': bt_tech,
|
|
3173
|
+
'pp_tech': pp_tech, 'preprocessing': pp_tech,
|
|
3174
|
+
'fp_heur': fp_heur, 'feasibility_pump': fp_heur,
|
|
3175
|
+
'gmi_cuts': gmi_cuts, 'gomory_cuts': gmi_cuts,
|
|
3176
|
+
'mir_cuts': mir_cuts, 'mixed_int_rounding_cuts': mir_cuts,
|
|
3177
|
+
'cov_cuts': cov_cuts, 'mixed_cover_cuts': cov_cuts,
|
|
3178
|
+
'clq_cuts': clq_cuts, 'clique_cuts': clq_cuts,
|
|
3179
|
+
'tol_int': tol_int, 'absolute_tolerance': tol_int,
|
|
3180
|
+
'tol_obj': tol_obj, 'relative_tolerance': tol_obj,
|
|
3181
|
+
'mip_gap': mip_gap, 'mip_gap_tolerance': mip_gap,
|
|
3182
|
+
'out_frq_intopt': out_frq_intopt, 'output_frequency_intopt': out_frq_intopt,
|
|
3183
|
+
'out_dly_intopt': out_dly_intopt, 'output_delay_intopt': out_dly_intopt,
|
|
3184
|
+
'presolve_intopt': presolve_intopt, 'binarize': binarize,
|
|
3185
|
+
'meth': meth, 'primal_v_dual': meth,
|
|
3186
|
+
'pricing': pricing,
|
|
3187
|
+
'r_test': r_test, 'ratio_test': r_test,
|
|
3188
|
+
'tol_bnd': tol_bnd, 'tolerance_primal': tol_bnd,
|
|
3189
|
+
'tol_dj': tol_dj, 'tolerance_dual': tol_dj,
|
|
3190
|
+
'tol_piv': tol_piv, 'tolerance_pivot': tol_piv,
|
|
3191
|
+
'obj_ll': obj_ll, 'obj_lower_limit': obj_ll,
|
|
3192
|
+
'obj_ul': obj_ul, 'obj_upper_limit': obj_ul,
|
|
3193
|
+
'it_lim': it_lim, 'iteration_limit': it_lim,
|
|
3194
|
+
'out_frq_simplex': out_frq_simplex, 'output_frequency_simplex': out_frq_simplex,
|
|
3195
|
+
'out_dly_simplex': out_dly_simplex, 'output_delay_simplex': out_dly_simplex,
|
|
3196
|
+
'presolve_simplex': presolve_simplex
|
|
3197
|
+
}
|
|
3198
|
+
|
|
3199
|
+
# parameter values
|
|
3200
|
+
|
|
3201
|
+
glp_msg_off = GLP_MSG_OFF
|
|
3202
|
+
glp_msg_on = GLP_MSG_ON
|
|
3203
|
+
glp_msg_err = GLP_MSG_ERR
|
|
3204
|
+
glp_msg_all = GLP_MSG_ALL
|
|
3205
|
+
glp_msg_dbg = GLP_MSG_DBG
|
|
3206
|
+
|
|
3207
|
+
glp_primal = GLP_PRIMAL
|
|
3208
|
+
glp_dual = GLP_DUAL
|
|
3209
|
+
glp_dualp = GLP_DUALP
|
|
3210
|
+
|
|
3211
|
+
glp_pt_std = GLP_PT_STD
|
|
3212
|
+
glp_pt_pse = GLP_PT_PSE
|
|
3213
|
+
|
|
3214
|
+
glp_rt_std = GLP_RT_STD
|
|
3215
|
+
glp_rt_har = GLP_RT_HAR
|
|
3216
|
+
|
|
3217
|
+
dbl_max = DBL_MAX
|
|
3218
|
+
int_max = INT_MAX
|
|
3219
|
+
|
|
3220
|
+
glp_on = GLP_ON
|
|
3221
|
+
glp_off = GLP_OFF
|
|
3222
|
+
|
|
3223
|
+
glp_br_ffv = GLP_BR_FFV
|
|
3224
|
+
glp_br_lfv = GLP_BR_LFV
|
|
3225
|
+
glp_br_mfv = GLP_BR_MFV
|
|
3226
|
+
glp_br_dth = GLP_BR_DTH
|
|
3227
|
+
glp_br_pch = GLP_BR_PCH
|
|
3228
|
+
|
|
3229
|
+
glp_bt_dfs = GLP_BT_DFS
|
|
3230
|
+
glp_bt_bfs = GLP_BT_BFS
|
|
3231
|
+
glp_bt_blb = GLP_BT_BLB
|
|
3232
|
+
glp_bt_bph = GLP_BT_BPH
|
|
3233
|
+
|
|
3234
|
+
glp_pp_none = GLP_PP_NONE
|
|
3235
|
+
glp_pp_root = GLP_PP_ROOT
|
|
3236
|
+
glp_pp_all = GLP_PP_ALL
|
|
3237
|
+
|
|
3238
|
+
glp_max = GLP_MAX
|
|
3239
|
+
glp_min = GLP_MIN
|
|
3240
|
+
glp_up = GLP_UP
|
|
3241
|
+
glp_fr = GLP_FR
|
|
3242
|
+
glp_db = GLP_DB
|
|
3243
|
+
glp_fx = GLP_FX
|
|
3244
|
+
glp_lo = GLP_LO
|
|
3245
|
+
glp_cv = GLP_CV
|
|
3246
|
+
glp_iv = GLP_IV
|
|
3247
|
+
glp_bv = GLP_BV
|
|
3248
|
+
glp_mps_deck = GLP_MPS_DECK
|
|
3249
|
+
glp_mps_file = GLP_MPS_FILE
|
|
3250
|
+
|
|
3251
|
+
glp_undef = GLP_UNDEF
|
|
3252
|
+
glp_opt = GLP_OPT
|
|
3253
|
+
glp_feas = GLP_FEAS
|
|
3254
|
+
glp_nofeas = GLP_NOFEAS
|
|
3255
|
+
|
|
3256
|
+
glp_bs = GLP_BS
|
|
3257
|
+
glp_nl = GLP_NL
|
|
3258
|
+
glp_nu = GLP_NU
|
|
3259
|
+
glp_nf = GLP_NF
|
|
3260
|
+
|
|
3261
|
+
cdef enum more_parameter_values:
|
|
3262
|
+
simplex_only, simplex_then_intopt, intopt_only, exact_simplex_only
|
|
3263
|
+
|
|
3264
|
+
glp_simplex_only = simplex_only
|
|
3265
|
+
glp_simplex_then_intopt = simplex_then_intopt
|
|
3266
|
+
glp_intopt_only = intopt_only
|
|
3267
|
+
glp_exact_simplex_only = exact_simplex_only
|
|
3268
|
+
|
|
3269
|
+
# dictionaries for those who prefer to use strings
|
|
3270
|
+
|
|
3271
|
+
solver_parameter_values = {
|
|
3272
|
+
|
|
3273
|
+
'simplex_only': simplex_only,
|
|
3274
|
+
'simplex_then_intopt': simplex_then_intopt,
|
|
3275
|
+
'intopt_only': intopt_only,
|
|
3276
|
+
'exact_simplex_only': exact_simplex_only,
|
|
3277
|
+
|
|
3278
|
+
'GLP_MSG_OFF': GLP_MSG_OFF,
|
|
3279
|
+
'GLP_MSG_ON': GLP_MSG_ON,
|
|
3280
|
+
'GLP_MSG_ERR': GLP_MSG_ERR,
|
|
3281
|
+
'GLP_MSG_ALL': GLP_MSG_ALL,
|
|
3282
|
+
'GLP_MSG_DBG': GLP_MSG_DBG,
|
|
3283
|
+
|
|
3284
|
+
'GLP_PRIMAL': GLP_PRIMAL,
|
|
3285
|
+
'GLP_DUAL': GLP_DUAL,
|
|
3286
|
+
'GLP_DUALP': GLP_DUALP,
|
|
3287
|
+
|
|
3288
|
+
'GLP_PT_STD': GLP_PT_STD,
|
|
3289
|
+
'GLP_PT_PSE': GLP_PT_PSE,
|
|
3290
|
+
|
|
3291
|
+
'GLP_RT_STD': GLP_RT_STD,
|
|
3292
|
+
'GLP_RT_HAR': GLP_RT_HAR,
|
|
3293
|
+
|
|
3294
|
+
'DBL_MAX': DBL_MAX,
|
|
3295
|
+
'INT_MAX': INT_MAX,
|
|
3296
|
+
|
|
3297
|
+
'GLP_ON': GLP_ON,
|
|
3298
|
+
'GLP_OFF': GLP_OFF,
|
|
3299
|
+
|
|
3300
|
+
'GLP_BR_FFV': GLP_BR_FFV,
|
|
3301
|
+
'GLP_BR_LFV': GLP_BR_LFV,
|
|
3302
|
+
'GLP_BR_MFV': GLP_BR_MFV,
|
|
3303
|
+
'GLP_BR_DTH': GLP_BR_DTH,
|
|
3304
|
+
'GLP_BR_PCH': GLP_BR_PCH,
|
|
3305
|
+
|
|
3306
|
+
'GLP_BT_DFS': GLP_BT_DFS,
|
|
3307
|
+
'GLP_BT_BFS': GLP_BT_BFS,
|
|
3308
|
+
'GLP_BT_BLB': GLP_BT_BLB,
|
|
3309
|
+
'GLP_BT_BPH': GLP_BT_BPH,
|
|
3310
|
+
|
|
3311
|
+
'GLP_PP_NONE': GLP_PP_NONE,
|
|
3312
|
+
'GLP_PP_ROOT': GLP_PP_ROOT,
|
|
3313
|
+
'GLP_PP_ALL': GLP_PP_ALL,
|
|
3314
|
+
|
|
3315
|
+
'GLP_MAX': GLP_MAX,
|
|
3316
|
+
'GLP_MIN': GLP_MIN,
|
|
3317
|
+
'GLP_UP': GLP_UP,
|
|
3318
|
+
'GLP_FR': GLP_FR,
|
|
3319
|
+
'GLP_DB': GLP_DB,
|
|
3320
|
+
'GLP_FX': GLP_FX,
|
|
3321
|
+
'GLP_LO': GLP_LO,
|
|
3322
|
+
'GLP_CV': GLP_CV,
|
|
3323
|
+
'GLP_IV': GLP_IV,
|
|
3324
|
+
'GLP_BV': GLP_BV,
|
|
3325
|
+
'GLP_MPS_DECK': GLP_MPS_DECK,
|
|
3326
|
+
'GLP_MPS_FILE': GLP_MPS_FILE,
|
|
3327
|
+
|
|
3328
|
+
'GLP_UNDEF': GLP_UNDEF,
|
|
3329
|
+
'GLP_OPT': GLP_OPT,
|
|
3330
|
+
'GLP_FEAS': GLP_FEAS,
|
|
3331
|
+
'GLP_NOFEAS': GLP_NOFEAS
|
|
3332
|
+
|
|
3333
|
+
}
|
|
3334
|
+
|
|
3335
|
+
cdef dict solve_status_msg = {
|
|
3336
|
+
GLP_EBADB: "The initial basis specified in the problem object is invalid",
|
|
3337
|
+
GLP_ESING: "The basis matrix corresponding to the initial basis is singular within the working precision",
|
|
3338
|
+
GLP_ECOND: "The basis matrix corresponding to the initial basis is ill-conditioned, i.e. its condition number is too large",
|
|
3339
|
+
GLP_EBOUND: "Some variables (auxiliary or structural) have incorrect bounds",
|
|
3340
|
+
GLP_EFAIL: "Solver failure",
|
|
3341
|
+
GLP_EOBJLL: "The objective lower limit has been reached",
|
|
3342
|
+
GLP_EOBJUL: "The objective upper limit has been reached",
|
|
3343
|
+
GLP_EITLIM: "The iteration limit has been exceeded",
|
|
3344
|
+
GLP_ETMLIM: "The time limit has been exceeded",
|
|
3345
|
+
GLP_ENOPFS: "The LP (relaxation) problem has no primal feasible solution",
|
|
3346
|
+
GLP_ENODFS: "The LP (relaxation) problem has no dual feasible solution",
|
|
3347
|
+
GLP_EROOT: "Optimal basis for initial LP relaxation is not provided",
|
|
3348
|
+
GLP_ESTOP: "The search was prematurely terminated by application",
|
|
3349
|
+
GLP_EMIPGAP: "The relative mip gap tolerance has been reached",
|
|
3350
|
+
}
|
|
3351
|
+
|
|
3352
|
+
cdef dict solution_status_msg = {
|
|
3353
|
+
GLP_UNDEF: "Solution is undefined",
|
|
3354
|
+
GLP_FEAS: "Feasible solution found, while optimality has not been proven",
|
|
3355
|
+
GLP_INFEAS: "Solution is infeasible",
|
|
3356
|
+
GLP_NOFEAS: "Problem has no feasible solution",
|
|
3357
|
+
GLP_OPT: "Solution is optimal",
|
|
3358
|
+
GLP_UNBND: "Problem has unbounded solution",
|
|
3359
|
+
}
|