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.

Files changed (31) hide show
  1. passagemath_glpk/__init__.py +3 -0
  2. passagemath_glpk-10.6.46.dist-info/DELVEWHEEL +2 -0
  3. passagemath_glpk-10.6.46.dist-info/METADATA +103 -0
  4. passagemath_glpk-10.6.46.dist-info/RECORD +31 -0
  5. passagemath_glpk-10.6.46.dist-info/WHEEL +5 -0
  6. passagemath_glpk-10.6.46.dist-info/top_level.txt +3 -0
  7. passagemath_glpk.libs/libgcc_s_seh-1-8ac28fe21ad5783af7630f2f0c8f554d.dll +0 -0
  8. passagemath_glpk.libs/libglpk-40-a2b083517ab17d71a64901ccf3d73f9d.dll +0 -0
  9. passagemath_glpk.libs/libgmp-10-23decc023ff052bc2d748f1d5bb87892.dll +0 -0
  10. passagemath_glpk.libs/libwinpthread-1-e271f374468d584905afcdf7da96a6ad.dll +0 -0
  11. sage/all__sagemath_glpk.py +11 -0
  12. sage/libs/all__sagemath_glpk.py +1 -0
  13. sage/libs/glpk/__init__.py +1 -0
  14. sage/libs/glpk/constants.pxd +94 -0
  15. sage/libs/glpk/env.pxd +14 -0
  16. sage/libs/glpk/graph.pxd +43 -0
  17. sage/libs/glpk/lp.pxd +95 -0
  18. sage/libs/glpk/types.pxd +87 -0
  19. sage/numerical/all__sagemath_glpk.py +1 -0
  20. sage/numerical/backends/all__sagemath_glpk.py +1 -0
  21. sage/numerical/backends/glpk_backend.cp314t-win_amd64.pyd +0 -0
  22. sage/numerical/backends/glpk_backend.pxd +41 -0
  23. sage/numerical/backends/glpk_backend.pyx +3359 -0
  24. sage/numerical/backends/glpk_backend_test.py +13 -0
  25. sage/numerical/backends/glpk_exact_backend.cp314t-win_amd64.pyd +0 -0
  26. sage/numerical/backends/glpk_exact_backend.pxd +17 -0
  27. sage/numerical/backends/glpk_exact_backend.pyx +190 -0
  28. sage/numerical/backends/glpk_exact_backend_test.py +12 -0
  29. sage/numerical/backends/glpk_graph_backend.cp314t-win_amd64.pyd +0 -0
  30. sage/numerical/backends/glpk_graph_backend.pxd +56 -0
  31. 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
+ }