passagemath-polyhedra 10.6.37__cp314-cp314-musllinux_1_2_x86_64.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.
- passagemath_polyhedra/__init__.py +3 -0
- passagemath_polyhedra-10.6.37.dist-info/METADATA +367 -0
- passagemath_polyhedra-10.6.37.dist-info/METADATA.bak +369 -0
- passagemath_polyhedra-10.6.37.dist-info/RECORD +209 -0
- passagemath_polyhedra-10.6.37.dist-info/WHEEL +5 -0
- passagemath_polyhedra-10.6.37.dist-info/top_level.txt +3 -0
- passagemath_polyhedra.libs/libgcc_s-0cd532bd.so.1 +0 -0
- passagemath_polyhedra.libs/libgmp-0e7fc84e.so.10.5.0 +0 -0
- passagemath_polyhedra.libs/libgomp-8949ffbe.so.1.0.0 +0 -0
- passagemath_polyhedra.libs/libstdc++-5d72f927.so.6.0.33 +0 -0
- sage/all__sagemath_polyhedra.py +50 -0
- sage/game_theory/all.py +8 -0
- sage/game_theory/catalog.py +6 -0
- sage/game_theory/catalog_normal_form_games.py +923 -0
- sage/game_theory/cooperative_game.py +844 -0
- sage/game_theory/matching_game.py +1181 -0
- sage/game_theory/normal_form_game.py +2697 -0
- sage/game_theory/parser.py +275 -0
- sage/geometry/all__sagemath_polyhedra.py +22 -0
- sage/geometry/cone.py +6940 -0
- sage/geometry/cone_catalog.py +847 -0
- sage/geometry/cone_critical_angles.py +1027 -0
- sage/geometry/convex_set.py +1119 -0
- sage/geometry/fan.py +3743 -0
- sage/geometry/fan_isomorphism.py +389 -0
- sage/geometry/fan_morphism.py +1884 -0
- sage/geometry/hasse_diagram.py +202 -0
- sage/geometry/hyperplane_arrangement/affine_subspace.py +390 -0
- sage/geometry/hyperplane_arrangement/all.py +1 -0
- sage/geometry/hyperplane_arrangement/arrangement.py +3905 -0
- sage/geometry/hyperplane_arrangement/check_freeness.py +145 -0
- sage/geometry/hyperplane_arrangement/hyperplane.py +773 -0
- sage/geometry/hyperplane_arrangement/library.py +825 -0
- sage/geometry/hyperplane_arrangement/ordered_arrangement.py +642 -0
- sage/geometry/hyperplane_arrangement/plot.py +520 -0
- sage/geometry/integral_points.py +35 -0
- sage/geometry/integral_points_generic_dense.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/integral_points_generic_dense.pyx +7 -0
- sage/geometry/lattice_polytope.py +5894 -0
- sage/geometry/linear_expression.py +773 -0
- sage/geometry/newton_polygon.py +767 -0
- sage/geometry/point_collection.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/point_collection.pyx +1008 -0
- sage/geometry/polyhedral_complex.py +2616 -0
- sage/geometry/polyhedron/all.py +8 -0
- sage/geometry/polyhedron/backend_cdd.py +460 -0
- sage/geometry/polyhedron/backend_cdd_rdf.py +231 -0
- sage/geometry/polyhedron/backend_field.py +347 -0
- sage/geometry/polyhedron/backend_normaliz.py +2503 -0
- sage/geometry/polyhedron/backend_number_field.py +168 -0
- sage/geometry/polyhedron/backend_polymake.py +765 -0
- sage/geometry/polyhedron/backend_ppl.py +582 -0
- sage/geometry/polyhedron/base.py +1206 -0
- sage/geometry/polyhedron/base0.py +1444 -0
- sage/geometry/polyhedron/base1.py +886 -0
- sage/geometry/polyhedron/base2.py +812 -0
- sage/geometry/polyhedron/base3.py +1845 -0
- sage/geometry/polyhedron/base4.py +1262 -0
- sage/geometry/polyhedron/base5.py +2700 -0
- sage/geometry/polyhedron/base6.py +1741 -0
- sage/geometry/polyhedron/base7.py +997 -0
- sage/geometry/polyhedron/base_QQ.py +1258 -0
- sage/geometry/polyhedron/base_RDF.py +98 -0
- sage/geometry/polyhedron/base_ZZ.py +934 -0
- sage/geometry/polyhedron/base_mutable.py +215 -0
- sage/geometry/polyhedron/base_number_field.py +122 -0
- sage/geometry/polyhedron/cdd_file_format.py +155 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/all.py +1 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/base.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd +76 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +3859 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd +39 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +1038 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/conversions.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pxd +9 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx +501 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd +207 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +102 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +2274 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd +370 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pyx +84 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd +31 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx +587 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd +52 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx +560 -0
- sage/geometry/polyhedron/constructor.py +773 -0
- sage/geometry/polyhedron/double_description.py +753 -0
- sage/geometry/polyhedron/double_description_inhomogeneous.py +564 -0
- sage/geometry/polyhedron/face.py +1060 -0
- sage/geometry/polyhedron/generating_function.py +1810 -0
- sage/geometry/polyhedron/lattice_euclidean_group_element.py +178 -0
- sage/geometry/polyhedron/library.py +3502 -0
- sage/geometry/polyhedron/misc.py +121 -0
- sage/geometry/polyhedron/modules/all.py +1 -0
- sage/geometry/polyhedron/modules/formal_polyhedra_module.py +155 -0
- sage/geometry/polyhedron/palp_database.py +447 -0
- sage/geometry/polyhedron/parent.py +1279 -0
- sage/geometry/polyhedron/plot.py +1986 -0
- sage/geometry/polyhedron/ppl_lattice_polygon.py +556 -0
- sage/geometry/polyhedron/ppl_lattice_polytope.py +1257 -0
- sage/geometry/polyhedron/representation.py +1723 -0
- sage/geometry/pseudolines.py +515 -0
- sage/geometry/relative_interior.py +445 -0
- sage/geometry/toric_plotter.py +1103 -0
- sage/geometry/triangulation/all.py +2 -0
- sage/geometry/triangulation/base.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/triangulation/base.pyx +963 -0
- sage/geometry/triangulation/data.h +147 -0
- sage/geometry/triangulation/data.pxd +4 -0
- sage/geometry/triangulation/element.py +914 -0
- sage/geometry/triangulation/functions.h +10 -0
- sage/geometry/triangulation/functions.pxd +4 -0
- sage/geometry/triangulation/point_configuration.py +2256 -0
- sage/geometry/triangulation/triangulations.h +49 -0
- sage/geometry/triangulation/triangulations.pxd +7 -0
- sage/geometry/voronoi_diagram.py +319 -0
- sage/interfaces/all__sagemath_polyhedra.py +1 -0
- sage/interfaces/polymake.py +2028 -0
- sage/numerical/all.py +13 -0
- sage/numerical/all__sagemath_polyhedra.py +11 -0
- sage/numerical/backends/all.py +1 -0
- sage/numerical/backends/all__sagemath_polyhedra.py +1 -0
- sage/numerical/backends/cvxopt_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/cvxopt_backend.pyx +1006 -0
- sage/numerical/backends/cvxopt_backend_test.py +19 -0
- sage/numerical/backends/cvxopt_sdp_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/cvxopt_sdp_backend.pyx +382 -0
- sage/numerical/backends/cvxpy_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/cvxpy_backend.pxd +41 -0
- sage/numerical/backends/cvxpy_backend.pyx +934 -0
- sage/numerical/backends/cvxpy_backend_test.py +13 -0
- sage/numerical/backends/generic_backend_test.py +24 -0
- sage/numerical/backends/interactivelp_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/interactivelp_backend.pxd +36 -0
- sage/numerical/backends/interactivelp_backend.pyx +1231 -0
- sage/numerical/backends/interactivelp_backend_test.py +12 -0
- sage/numerical/backends/logging_backend.py +391 -0
- sage/numerical/backends/matrix_sdp_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/matrix_sdp_backend.pxd +15 -0
- sage/numerical/backends/matrix_sdp_backend.pyx +478 -0
- sage/numerical/backends/ppl_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/ppl_backend.pyx +1126 -0
- sage/numerical/backends/ppl_backend_test.py +13 -0
- sage/numerical/backends/scip_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/scip_backend.pxd +22 -0
- sage/numerical/backends/scip_backend.pyx +1289 -0
- sage/numerical/backends/scip_backend_test.py +13 -0
- sage/numerical/interactive_simplex_method.py +5338 -0
- sage/numerical/knapsack.py +665 -0
- sage/numerical/linear_functions.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/linear_functions.pxd +31 -0
- sage/numerical/linear_functions.pyx +1648 -0
- sage/numerical/linear_tensor.py +470 -0
- sage/numerical/linear_tensor_constraints.py +448 -0
- sage/numerical/linear_tensor_element.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/linear_tensor_element.pxd +6 -0
- sage/numerical/linear_tensor_element.pyx +459 -0
- sage/numerical/mip.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/mip.pxd +40 -0
- sage/numerical/mip.pyx +3667 -0
- sage/numerical/sdp.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/sdp.pxd +39 -0
- sage/numerical/sdp.pyx +1433 -0
- sage/rings/all__sagemath_polyhedra.py +3 -0
- sage/rings/polynomial/all__sagemath_polyhedra.py +10 -0
- sage/rings/polynomial/omega.py +982 -0
- sage/schemes/all__sagemath_polyhedra.py +2 -0
- sage/schemes/toric/all.py +10 -0
- sage/schemes/toric/chow_group.py +1248 -0
- sage/schemes/toric/divisor.py +2082 -0
- sage/schemes/toric/divisor_class.cpython-314-x86_64-linux-musl.so +0 -0
- sage/schemes/toric/divisor_class.pyx +322 -0
- sage/schemes/toric/fano_variety.py +1606 -0
- sage/schemes/toric/homset.py +650 -0
- sage/schemes/toric/ideal.py +451 -0
- sage/schemes/toric/library.py +1322 -0
- sage/schemes/toric/morphism.py +1958 -0
- sage/schemes/toric/points.py +1032 -0
- sage/schemes/toric/sheaf/all.py +1 -0
- sage/schemes/toric/sheaf/constructor.py +302 -0
- sage/schemes/toric/sheaf/klyachko.py +921 -0
- sage/schemes/toric/toric_subscheme.py +905 -0
- sage/schemes/toric/variety.py +3460 -0
- sage/schemes/toric/weierstrass.py +1078 -0
- sage/schemes/toric/weierstrass_covering.py +457 -0
- sage/schemes/toric/weierstrass_higher.py +288 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.info +10 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v03 +0 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v04 +0 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v05 +1 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v06 +1 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.info +22 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v04 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v05 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v06 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v07 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v08 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v09 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v10 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v11 +1 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v12 +1 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v13 +1 -0
- sage_wheels/share/reflexive_polytopes/reflexive_polytopes_2d +80 -0
- sage_wheels/share/reflexive_polytopes/reflexive_polytopes_3d +37977 -0
|
@@ -0,0 +1,1289 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-polyhedra
|
|
2
|
+
# distutils: language = c++
|
|
3
|
+
# sage.doctest: optional - pyscipopt
|
|
4
|
+
"""
|
|
5
|
+
SCIP Backend
|
|
6
|
+
|
|
7
|
+
AUTHORS:
|
|
8
|
+
|
|
9
|
+
- Nathann Cohen (2010-10): generic backend
|
|
10
|
+
- Matthias Koeppe (2017): stubs
|
|
11
|
+
- Moritz Firsching (2018-04): rest
|
|
12
|
+
"""
|
|
13
|
+
# *****************************************************************************
|
|
14
|
+
# Copyright (C) 2010 Nathann Cohen <nathann.cohen@gmail.com>
|
|
15
|
+
# 2017-2022 Matthias Koeppe
|
|
16
|
+
# 2018 Moritz Firsching
|
|
17
|
+
#
|
|
18
|
+
# This program is free software: you can redistribute it and/or modify
|
|
19
|
+
# it under the terms of the GNU General Public License as published by
|
|
20
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
21
|
+
# (at your option) any later version.
|
|
22
|
+
# https://www.gnu.org/licenses/
|
|
23
|
+
# *****************************************************************************
|
|
24
|
+
|
|
25
|
+
from os.path import splitext
|
|
26
|
+
from sage.numerical.mip import MIPSolverException
|
|
27
|
+
from pyscipopt import Model
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
cdef class SCIPBackend(GenericBackend):
|
|
31
|
+
"""
|
|
32
|
+
MIP Backend that uses the SCIP solver.
|
|
33
|
+
|
|
34
|
+
TESTS:
|
|
35
|
+
|
|
36
|
+
General backend testsuite::
|
|
37
|
+
|
|
38
|
+
sage: p = MixedIntegerLinearProgram(solver='SCIP')
|
|
39
|
+
sage: TestSuite(p.get_backend()).run(skip='_test_pickling')
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def __cinit__(self, maximization=True):
|
|
43
|
+
"""
|
|
44
|
+
Constructor.
|
|
45
|
+
|
|
46
|
+
EXAMPLES::
|
|
47
|
+
|
|
48
|
+
sage: p = MixedIntegerLinearProgram(solver='SCIP')
|
|
49
|
+
"""
|
|
50
|
+
self.model = Model('')
|
|
51
|
+
if maximization:
|
|
52
|
+
self.set_sense(1)
|
|
53
|
+
else:
|
|
54
|
+
self.set_sense(-1)
|
|
55
|
+
self.obj_constant_term = 0.0
|
|
56
|
+
self.variables = []
|
|
57
|
+
self.model.hideOutput()
|
|
58
|
+
# always set this to None if the list of constraints may change
|
|
59
|
+
self.constraints = None
|
|
60
|
+
|
|
61
|
+
def get_constraints(self):
|
|
62
|
+
"""
|
|
63
|
+
Get all constraints of the problem.
|
|
64
|
+
|
|
65
|
+
EXAMPLES::
|
|
66
|
+
|
|
67
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
68
|
+
sage: lp = get_solver(solver='SCIP')
|
|
69
|
+
sage: lp.add_variables(3)
|
|
70
|
+
2
|
|
71
|
+
sage: lp.add_linear_constraint(zip([0, 1, 2], [8, 6, 1]), None, 48)
|
|
72
|
+
sage: lp.add_linear_constraint(zip([0, 1, 2], [2, 1.5, 0.5]), None, 8)
|
|
73
|
+
|
|
74
|
+
sage: lp.get_constraints()
|
|
75
|
+
[c1, c2]
|
|
76
|
+
sage: lp.row(1) # indirect doctest
|
|
77
|
+
([0, 1, 2], [2.0, 1.5, 0.5])
|
|
78
|
+
"""
|
|
79
|
+
if self.constraints is None:
|
|
80
|
+
self.constraints = self.model.getConss()
|
|
81
|
+
return self.constraints
|
|
82
|
+
|
|
83
|
+
cpdef _get_model(self):
|
|
84
|
+
"""
|
|
85
|
+
Get the model as a pyscipopt Model.
|
|
86
|
+
|
|
87
|
+
EXAMPLES::
|
|
88
|
+
|
|
89
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
90
|
+
sage: p = get_solver(solver = "SCIP")
|
|
91
|
+
sage: p._get_model()
|
|
92
|
+
<pyscipopt.scip.Model object at ...
|
|
93
|
+
"""
|
|
94
|
+
return self.model
|
|
95
|
+
|
|
96
|
+
cpdef int add_variable(self, lower_bound=0.0, upper_bound=None,
|
|
97
|
+
binary=False, continuous=False, integer=False,
|
|
98
|
+
obj=0.0, name=None) except -1:
|
|
99
|
+
"""
|
|
100
|
+
Add a variable.
|
|
101
|
+
|
|
102
|
+
This amounts to adding a new column to the matrix. By default,
|
|
103
|
+
the variable is both positive, real and the coefficient in the
|
|
104
|
+
objective function is 0.0.
|
|
105
|
+
|
|
106
|
+
INPUT:
|
|
107
|
+
|
|
108
|
+
- ``lower_bound`` -- the lower bound of the variable (default: 0)
|
|
109
|
+
|
|
110
|
+
- ``upper_bound`` -- the upper bound of the variable (default: ``None``)
|
|
111
|
+
|
|
112
|
+
- ``binary`` -- ``True`` if the variable is binary (default: ``False``)
|
|
113
|
+
|
|
114
|
+
- ``continuous`` -- ``True`` if the variable is binary (default: ``True``)
|
|
115
|
+
|
|
116
|
+
- ``integer`` -- ``True`` if the variable is binary (default: ``False``)
|
|
117
|
+
|
|
118
|
+
- ``obj`` -- (optional) coefficient of this variable in the objective function (default: 0.0)
|
|
119
|
+
|
|
120
|
+
- ``name`` -- an optional name for the newly added variable (default: ``None``)
|
|
121
|
+
|
|
122
|
+
OUTPUT: the index of the newly created variable
|
|
123
|
+
|
|
124
|
+
EXAMPLES::
|
|
125
|
+
|
|
126
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
127
|
+
sage: p = get_solver(solver = "SCIP")
|
|
128
|
+
sage: p.ncols()
|
|
129
|
+
0
|
|
130
|
+
sage: p.add_variable()
|
|
131
|
+
0
|
|
132
|
+
sage: p.ncols()
|
|
133
|
+
1
|
|
134
|
+
sage: p.add_variable(binary=True)
|
|
135
|
+
1
|
|
136
|
+
sage: p.add_variable(lower_bound=-2.0, integer=True)
|
|
137
|
+
2
|
|
138
|
+
sage: p.add_variable(continuous=True, integer=True)
|
|
139
|
+
Traceback (most recent call last):
|
|
140
|
+
...
|
|
141
|
+
ValueError: ...
|
|
142
|
+
sage: p.add_variable(name='x', obj=1.0)
|
|
143
|
+
3
|
|
144
|
+
sage: p.col_name(3)
|
|
145
|
+
'x'
|
|
146
|
+
sage: p.objective_coefficient(3)
|
|
147
|
+
1.0
|
|
148
|
+
"""
|
|
149
|
+
if self.model.getStatus() != 'unknown':
|
|
150
|
+
self.model.freeTransform()
|
|
151
|
+
cdef int vtype = int(bool(binary)) + int(bool(continuous)) + int(bool(integer))
|
|
152
|
+
if vtype == 0:
|
|
153
|
+
continuous = True
|
|
154
|
+
elif vtype != 1:
|
|
155
|
+
raise ValueError("Exactly one parameter of 'binary', 'integer'"
|
|
156
|
+
"and 'continuous' must be 'True'.")
|
|
157
|
+
|
|
158
|
+
if name is None:
|
|
159
|
+
vname = ''
|
|
160
|
+
else:
|
|
161
|
+
assert type(name) in [str, unicode]
|
|
162
|
+
vname = name
|
|
163
|
+
|
|
164
|
+
if continuous:
|
|
165
|
+
vtypestr = 'C'
|
|
166
|
+
if binary:
|
|
167
|
+
vtypestr = 'B'
|
|
168
|
+
if integer:
|
|
169
|
+
vtypestr = 'I'
|
|
170
|
+
|
|
171
|
+
v = self.model.addVar(name=vname, vtype=vtypestr, ub=upper_bound,
|
|
172
|
+
lb=lower_bound, obj=obj, pricedVar=False)
|
|
173
|
+
index = v.getIndex()
|
|
174
|
+
assert index == self.ncols()
|
|
175
|
+
self.variables.append(v)
|
|
176
|
+
|
|
177
|
+
return index
|
|
178
|
+
|
|
179
|
+
cpdef set_variable_type(self, int variable, int vtype):
|
|
180
|
+
"""
|
|
181
|
+
Set the type of a variable.
|
|
182
|
+
|
|
183
|
+
INPUT:
|
|
184
|
+
|
|
185
|
+
- ``variable`` -- integer; the variable's id
|
|
186
|
+
|
|
187
|
+
- ``vtype`` -- integer:
|
|
188
|
+
|
|
189
|
+
* 1 Integer
|
|
190
|
+
* 0 Binary
|
|
191
|
+
* -1 Real
|
|
192
|
+
|
|
193
|
+
EXAMPLES::
|
|
194
|
+
|
|
195
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
196
|
+
sage: p = get_solver(solver = "SCIP")
|
|
197
|
+
sage: p.ncols()
|
|
198
|
+
0
|
|
199
|
+
sage: p.add_variable()
|
|
200
|
+
0
|
|
201
|
+
sage: p.set_variable_type(0,1)
|
|
202
|
+
sage: p.is_variable_integer(0)
|
|
203
|
+
True
|
|
204
|
+
"""
|
|
205
|
+
if self.model.getStatus() != 'unknown':
|
|
206
|
+
self.model.freeTransform()
|
|
207
|
+
vtypenames = {1: 'I', 0: 'B', -1: 'C'}
|
|
208
|
+
self.model.chgVarType(var=self.variables[variable], vtype=vtypenames[vtype])
|
|
209
|
+
|
|
210
|
+
cpdef set_sense(self, int sense):
|
|
211
|
+
"""
|
|
212
|
+
Set the direction (maximization/minimization).
|
|
213
|
+
|
|
214
|
+
INPUT:
|
|
215
|
+
|
|
216
|
+
- ``sense`` -- integer:
|
|
217
|
+
|
|
218
|
+
* +1 => Maximization
|
|
219
|
+
* -1 => Minimization
|
|
220
|
+
|
|
221
|
+
EXAMPLES::
|
|
222
|
+
|
|
223
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
224
|
+
sage: p = get_solver(solver = "SCIP")
|
|
225
|
+
sage: p.is_maximization()
|
|
226
|
+
True
|
|
227
|
+
sage: p.set_sense(-1)
|
|
228
|
+
sage: p.is_maximization()
|
|
229
|
+
False
|
|
230
|
+
"""
|
|
231
|
+
if self.model.getStatus() != 'unknown':
|
|
232
|
+
self.model.freeTransform()
|
|
233
|
+
if sense == 1:
|
|
234
|
+
self.model.setMaximize()
|
|
235
|
+
elif sense == -1:
|
|
236
|
+
self.model.setMinimize()
|
|
237
|
+
else:
|
|
238
|
+
raise AssertionError("sense must be either 1 or -1")
|
|
239
|
+
|
|
240
|
+
cpdef objective_coefficient(self, int variable, coeff=None):
|
|
241
|
+
"""
|
|
242
|
+
Set or get the coefficient of a variable in the objective function.
|
|
243
|
+
|
|
244
|
+
INPUT:
|
|
245
|
+
|
|
246
|
+
- ``variable`` -- integer; the variable's id
|
|
247
|
+
|
|
248
|
+
- ``coeff`` -- double; its coefficient or ``None`` for
|
|
249
|
+
reading (default: ``None``)
|
|
250
|
+
|
|
251
|
+
EXAMPLES::
|
|
252
|
+
|
|
253
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
254
|
+
sage: p = get_solver(solver = "SCIP")
|
|
255
|
+
sage: p.add_variable()
|
|
256
|
+
0
|
|
257
|
+
sage: p.objective_coefficient(0)
|
|
258
|
+
0.0
|
|
259
|
+
sage: p.objective_coefficient(0,2)
|
|
260
|
+
sage: p.objective_coefficient(0)
|
|
261
|
+
2.0
|
|
262
|
+
"""
|
|
263
|
+
if self.model.getStatus() != 'unknown':
|
|
264
|
+
self.model.freeTransform()
|
|
265
|
+
if coeff is None:
|
|
266
|
+
return self.variables[variable].getObj()
|
|
267
|
+
else:
|
|
268
|
+
objexpr = self.model.getObjective()
|
|
269
|
+
var = self.variables[variable]
|
|
270
|
+
linfun = sum([e * c for e, c in objexpr.terms.iteritems() if e != var]) + var * coeff
|
|
271
|
+
self.model.setObjective(linfun, sense=self.model.getObjectiveSense())
|
|
272
|
+
|
|
273
|
+
cpdef problem_name(self, name=None):
|
|
274
|
+
"""
|
|
275
|
+
Return or define the problem's name.
|
|
276
|
+
|
|
277
|
+
INPUT:
|
|
278
|
+
|
|
279
|
+
- ``name`` -- string; the problem's name. When set to
|
|
280
|
+
``None`` (default), the method returns the problem's name.
|
|
281
|
+
|
|
282
|
+
EXAMPLES::
|
|
283
|
+
|
|
284
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
285
|
+
sage: p = get_solver(solver = "SCIP")
|
|
286
|
+
sage: p.problem_name("Nomen est omen")
|
|
287
|
+
sage: p.problem_name()
|
|
288
|
+
'Nomen est omen'
|
|
289
|
+
"""
|
|
290
|
+
if name is None:
|
|
291
|
+
return self.model.getProbName()
|
|
292
|
+
else:
|
|
293
|
+
self.model.setProbName(name)
|
|
294
|
+
|
|
295
|
+
cpdef set_objective(self, list coeff, d=0.0):
|
|
296
|
+
"""
|
|
297
|
+
Set the objective function.
|
|
298
|
+
|
|
299
|
+
INPUT:
|
|
300
|
+
|
|
301
|
+
- ``coeff`` -- list of real values, whose i-th element is the
|
|
302
|
+
coefficient of the i-th variable in the objective function
|
|
303
|
+
|
|
304
|
+
- ``d`` -- double; the constant term in the linear function (set to `0`
|
|
305
|
+
by default)
|
|
306
|
+
|
|
307
|
+
EXAMPLES::
|
|
308
|
+
|
|
309
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
310
|
+
sage: p = get_solver(solver = "SCIP")
|
|
311
|
+
sage: p.add_variables(5)
|
|
312
|
+
4
|
|
313
|
+
sage: p.set_objective([1, 1, 2, 1, 3])
|
|
314
|
+
sage: [p.objective_coefficient(x) for x in range(5)]
|
|
315
|
+
[1.0, 1.0, 2.0, 1.0, 3.0]
|
|
316
|
+
"""
|
|
317
|
+
if self.model.getStatus() != 'unknown':
|
|
318
|
+
self.model.freeTransform()
|
|
319
|
+
linfun = sum([c * x for c, x in zip(coeff, self.variables)]) + d
|
|
320
|
+
self.model.setObjective(linfun, sense=self.model.getObjectiveSense())
|
|
321
|
+
|
|
322
|
+
cpdef set_verbosity(self, int level):
|
|
323
|
+
"""
|
|
324
|
+
Set the verbosity level.
|
|
325
|
+
|
|
326
|
+
INPUT:
|
|
327
|
+
|
|
328
|
+
- ``level`` -- integer; from 0 (no verbosity) to 1
|
|
329
|
+
|
|
330
|
+
EXAMPLES::
|
|
331
|
+
|
|
332
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
333
|
+
sage: p = get_solver(solver = "SCIP")
|
|
334
|
+
sage: p.set_verbosity(1)
|
|
335
|
+
|
|
336
|
+
TODOs::
|
|
337
|
+
|
|
338
|
+
- Currently, the output is written to stdout, even when running
|
|
339
|
+
Jupyter: https://github.com/SCIP-Interfaces/PySCIPOpt/issues/116 .
|
|
340
|
+
This should be fixed upstream
|
|
341
|
+
- get access to more verbosity levels (e.g. via parameter settings)
|
|
342
|
+
"""
|
|
343
|
+
if level == 0:
|
|
344
|
+
self.model.hideOutput()
|
|
345
|
+
elif level == 1:
|
|
346
|
+
self.model.hideOutput(False)
|
|
347
|
+
else:
|
|
348
|
+
raise AssertionError('level must be "0" or "1"')
|
|
349
|
+
|
|
350
|
+
cpdef remove_constraint(self, int i):
|
|
351
|
+
r"""
|
|
352
|
+
Remove a constraint from ``self``.
|
|
353
|
+
|
|
354
|
+
INPUT:
|
|
355
|
+
|
|
356
|
+
- ``i`` -- index of the constraint to remove
|
|
357
|
+
|
|
358
|
+
EXAMPLES::
|
|
359
|
+
|
|
360
|
+
sage: p = MixedIntegerLinearProgram(solver='SCIP')
|
|
361
|
+
sage: x, y = p['x'], p['y']
|
|
362
|
+
sage: p.add_constraint(2*x + 3*y <= 6)
|
|
363
|
+
sage: p.add_constraint(3*x + 2*y <= 6)
|
|
364
|
+
sage: p.add_constraint(x >= 0)
|
|
365
|
+
sage: p.set_objective(x + y + 7)
|
|
366
|
+
sage: p.set_integer(x); p.set_integer(y)
|
|
367
|
+
sage: p.solve()
|
|
368
|
+
9.0
|
|
369
|
+
sage: p.remove_constraint(0)
|
|
370
|
+
sage: p.solve()
|
|
371
|
+
10.0
|
|
372
|
+
|
|
373
|
+
Removing fancy constraints does not make Sage crash::
|
|
374
|
+
|
|
375
|
+
sage: MixedIntegerLinearProgram(solver='SCIP').remove_constraint(-2)
|
|
376
|
+
Traceback (most recent call last):
|
|
377
|
+
...
|
|
378
|
+
ValueError: The constraint's index i must satisfy 0 <= i < number_of_constraints
|
|
379
|
+
"""
|
|
380
|
+
if i < 0 or i >= self.nrows():
|
|
381
|
+
raise ValueError("The constraint's index i must satisfy 0 <= i < number_of_constraints")
|
|
382
|
+
if self.model.getStatus() != 'unknown':
|
|
383
|
+
self.model.freeTransform()
|
|
384
|
+
self.constraints = None
|
|
385
|
+
self.model.delCons(self.get_constraints()[i])
|
|
386
|
+
self.constraints = None
|
|
387
|
+
|
|
388
|
+
cpdef remove_constraints(self, constraints):
|
|
389
|
+
r"""
|
|
390
|
+
Remove several constraints.
|
|
391
|
+
|
|
392
|
+
INPUT:
|
|
393
|
+
|
|
394
|
+
- ``constraints`` -- an iterable containing the indices of the rows to remove
|
|
395
|
+
|
|
396
|
+
EXAMPLES::
|
|
397
|
+
|
|
398
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
399
|
+
sage: p = get_solver(solver='SCIP')
|
|
400
|
+
sage: p.add_variables(2)
|
|
401
|
+
1
|
|
402
|
+
sage: p.add_linear_constraint([(0, 2), (1, 3)], None, 6)
|
|
403
|
+
sage: p.add_linear_constraint([(0, 3), (1, 2)], None, 6)
|
|
404
|
+
sage: p.row(0)
|
|
405
|
+
([0, 1], [2.0, 3.0])
|
|
406
|
+
sage: p.remove_constraints([0, 1])
|
|
407
|
+
sage: p.nrows()
|
|
408
|
+
0
|
|
409
|
+
"""
|
|
410
|
+
if isinstance(constraints, int):
|
|
411
|
+
self.remove_constraint(constraints)
|
|
412
|
+
return
|
|
413
|
+
|
|
414
|
+
if self.model.getStatus() != 'unknown':
|
|
415
|
+
self.model.freeTransform()
|
|
416
|
+
self.constraints = None
|
|
417
|
+
|
|
418
|
+
all_constraints = self.get_constraints()
|
|
419
|
+
to_remove = [all_constraints[i] for i in constraints]
|
|
420
|
+
for constraint in to_remove:
|
|
421
|
+
self.model.delCons(constraint)
|
|
422
|
+
self.constraints = None
|
|
423
|
+
|
|
424
|
+
cpdef add_linear_constraint(self, coefficients, lower_bound, upper_bound, name=None):
|
|
425
|
+
"""
|
|
426
|
+
Add a linear constraint.
|
|
427
|
+
|
|
428
|
+
INPUT:
|
|
429
|
+
|
|
430
|
+
- ``coefficients`` an iterable with ``(c, v)`` pairs where ``c``
|
|
431
|
+
is a variable index (integer) and ``v`` is a value (real
|
|
432
|
+
value).
|
|
433
|
+
|
|
434
|
+
- ``lower_bound`` -- a lower bound, either a real value or ``None``
|
|
435
|
+
|
|
436
|
+
- ``upper_bound`` -- an upper bound, either a real value or ``None``
|
|
437
|
+
|
|
438
|
+
- ``name`` -- an optional name for this row (default: ``None``)
|
|
439
|
+
|
|
440
|
+
EXAMPLES::
|
|
441
|
+
|
|
442
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
443
|
+
sage: p = get_solver(solver = "SCIP")
|
|
444
|
+
sage: p.add_variables(5)
|
|
445
|
+
4
|
|
446
|
+
sage: p.add_linear_constraint( zip(range(5), range(5)), 2.0, 2.0)
|
|
447
|
+
sage: p.row_bounds(0)
|
|
448
|
+
(2.0, 2.0)
|
|
449
|
+
sage: p.add_linear_constraint( zip(range(5), range(5)), 1.0, 1.0, name='foo')
|
|
450
|
+
sage: p.row_name(1)
|
|
451
|
+
'foo'
|
|
452
|
+
"""
|
|
453
|
+
if self.model.getStatus() != 'unknown':
|
|
454
|
+
self.model.freeTransform()
|
|
455
|
+
mvars = self.variables
|
|
456
|
+
from pyscipopt.scip import quicksum
|
|
457
|
+
linfun = quicksum([v * mvars[c] for c, v in coefficients])
|
|
458
|
+
# we introduced patch 0001 for pyscipopt, in order to handle the case
|
|
459
|
+
# when linfun is an empty expression.
|
|
460
|
+
if name is None:
|
|
461
|
+
name = ''
|
|
462
|
+
|
|
463
|
+
if lower_bound is None:
|
|
464
|
+
lower_bound = -self.model.infinity()
|
|
465
|
+
if upper_bound is None:
|
|
466
|
+
upper_bound = self.model.infinity()
|
|
467
|
+
|
|
468
|
+
cons = lower_bound <= (linfun <= upper_bound)
|
|
469
|
+
self.model.addCons(cons, name=name)
|
|
470
|
+
self.constraints = None
|
|
471
|
+
|
|
472
|
+
cpdef row(self, int index):
|
|
473
|
+
r"""
|
|
474
|
+
Return a row.
|
|
475
|
+
|
|
476
|
+
INPUT:
|
|
477
|
+
|
|
478
|
+
- ``index`` -- integer; the constraint's id
|
|
479
|
+
|
|
480
|
+
OUTPUT:
|
|
481
|
+
|
|
482
|
+
A pair ``(indices, coeffs)`` where ``indices`` lists the
|
|
483
|
+
entries whose coefficient is nonzero, and to which ``coeffs``
|
|
484
|
+
associates their coefficient on the model of the
|
|
485
|
+
``add_linear_constraint`` method.
|
|
486
|
+
|
|
487
|
+
EXAMPLES::
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
491
|
+
sage: p = get_solver(solver = "SCIP")
|
|
492
|
+
sage: p.add_variables(5)
|
|
493
|
+
4
|
|
494
|
+
sage: p.add_linear_constraint(zip(range(5), range(5)), 2, 2)
|
|
495
|
+
sage: p.row(0)
|
|
496
|
+
([1, 2, 3, 4], [1.0, 2.0, 3.0, 4.0])
|
|
497
|
+
sage: p.row_bounds(0)
|
|
498
|
+
(2.0, 2.0)
|
|
499
|
+
"""
|
|
500
|
+
namedvars = [var.name for var in self.variables]
|
|
501
|
+
valslinear = self.model.getValsLinear(self.get_constraints()[index])
|
|
502
|
+
cdef list indices = []
|
|
503
|
+
cdef list values = []
|
|
504
|
+
for var, coeff in valslinear.iteritems():
|
|
505
|
+
indices.append(namedvars.index(var))
|
|
506
|
+
values.append(coeff)
|
|
507
|
+
return (indices, values)
|
|
508
|
+
|
|
509
|
+
cpdef row_bounds(self, int index):
|
|
510
|
+
"""
|
|
511
|
+
Return the bounds of a specific constraint.
|
|
512
|
+
|
|
513
|
+
INPUT:
|
|
514
|
+
|
|
515
|
+
- ``index`` -- integer; the constraint's id
|
|
516
|
+
|
|
517
|
+
OUTPUT:
|
|
518
|
+
|
|
519
|
+
A pair ``(lower_bound, upper_bound)``. Each of them can be set
|
|
520
|
+
to ``None`` if the constraint is not bounded in the
|
|
521
|
+
corresponding direction, and is a real value otherwise.
|
|
522
|
+
|
|
523
|
+
EXAMPLES::
|
|
524
|
+
|
|
525
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
526
|
+
sage: p = get_solver(solver = "SCIP")
|
|
527
|
+
sage: p.add_variables(5)
|
|
528
|
+
4
|
|
529
|
+
sage: p.add_linear_constraint(zip(range(5), range(5)), 2, 2)
|
|
530
|
+
sage: p.row_bounds(0)
|
|
531
|
+
(2.0, 2.0)
|
|
532
|
+
"""
|
|
533
|
+
cons = self.get_constraints()[index]
|
|
534
|
+
lhs = self.model.getLhs(cons)
|
|
535
|
+
rhs = self.model.getRhs(cons)
|
|
536
|
+
if lhs == -self.model.infinity():
|
|
537
|
+
lhs = None
|
|
538
|
+
if rhs == self.model.infinity():
|
|
539
|
+
rhs = None
|
|
540
|
+
return (lhs, rhs)
|
|
541
|
+
|
|
542
|
+
cpdef col_bounds(self, int index):
|
|
543
|
+
"""
|
|
544
|
+
Return the bounds of a specific variable.
|
|
545
|
+
|
|
546
|
+
INPUT:
|
|
547
|
+
|
|
548
|
+
- ``index`` -- integer; the variable's id
|
|
549
|
+
|
|
550
|
+
OUTPUT:
|
|
551
|
+
|
|
552
|
+
A pair ``(lower_bound, upper_bound)``. Each of them can be set
|
|
553
|
+
to ``None`` if the variable is not bounded in the
|
|
554
|
+
corresponding direction, and is a real value otherwise.
|
|
555
|
+
|
|
556
|
+
EXAMPLES::
|
|
557
|
+
|
|
558
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
559
|
+
sage: p = get_solver(solver = "SCIP")
|
|
560
|
+
sage: p.add_variable()
|
|
561
|
+
0
|
|
562
|
+
sage: p.col_bounds(0)
|
|
563
|
+
(0.0, None)
|
|
564
|
+
sage: p.variable_upper_bound(0, 5)
|
|
565
|
+
sage: p.col_bounds(0)
|
|
566
|
+
(0.0, 5.0)
|
|
567
|
+
"""
|
|
568
|
+
var = self.variables[index]
|
|
569
|
+
lb = var.getLbOriginal()
|
|
570
|
+
if lb == -self.model.infinity():
|
|
571
|
+
lb = None
|
|
572
|
+
ub = var.getUbOriginal()
|
|
573
|
+
if ub == self.model.infinity():
|
|
574
|
+
ub = None
|
|
575
|
+
return (lb, ub)
|
|
576
|
+
|
|
577
|
+
cpdef add_col(self, indices, coeffs):
|
|
578
|
+
"""
|
|
579
|
+
Add a column.
|
|
580
|
+
|
|
581
|
+
INPUT:
|
|
582
|
+
|
|
583
|
+
- ``indices`` -- list of integers; this list contains the
|
|
584
|
+
indices of the constraints in which the variable's
|
|
585
|
+
coefficient is nonzero
|
|
586
|
+
|
|
587
|
+
- ``coeffs`` -- list of real values; associates a coefficient
|
|
588
|
+
to the variable in each of the constraints in which it
|
|
589
|
+
appears. Namely, the i-th entry of ``coeffs`` corresponds to
|
|
590
|
+
the coefficient of the variable in the constraint
|
|
591
|
+
represented by the i-th entry in ``indices``.
|
|
592
|
+
|
|
593
|
+
.. NOTE::
|
|
594
|
+
|
|
595
|
+
``indices`` and ``coeffs`` are expected to be of the same
|
|
596
|
+
length.
|
|
597
|
+
|
|
598
|
+
EXAMPLES::
|
|
599
|
+
|
|
600
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
601
|
+
sage: p = get_solver(solver = "SCIP")
|
|
602
|
+
sage: p.ncols()
|
|
603
|
+
0
|
|
604
|
+
sage: p.nrows()
|
|
605
|
+
0
|
|
606
|
+
sage: p.add_linear_constraints(5, 0, None)
|
|
607
|
+
sage: p.add_col(range(5), range(5))
|
|
608
|
+
sage: p.nrows()
|
|
609
|
+
5
|
|
610
|
+
"""
|
|
611
|
+
mcons = self.get_constraints()
|
|
612
|
+
self.constraints = None # is this necessary?
|
|
613
|
+
# after update of
|
|
614
|
+
index = self.add_variable(lower_bound=-self.model.infinity())
|
|
615
|
+
var = self.variables[index]
|
|
616
|
+
|
|
617
|
+
for i, coeff in zip(indices, coeffs):
|
|
618
|
+
self.model.addConsCoeff(var=var, cons=mcons[i], coeff=coeff)
|
|
619
|
+
|
|
620
|
+
cpdef int solve(self) except -1:
|
|
621
|
+
"""
|
|
622
|
+
Solve the problem.
|
|
623
|
+
|
|
624
|
+
Sage uses SCIP's implementation of the branch-and-cut algorithm
|
|
625
|
+
to solve the mixed-integer linear program.
|
|
626
|
+
(If all variables are continuous, the algorithm reduces to solving the
|
|
627
|
+
linear program by the simplex method.)
|
|
628
|
+
|
|
629
|
+
EXAMPLES::
|
|
630
|
+
|
|
631
|
+
sage: lp = MixedIntegerLinearProgram(solver = 'SCIP', maximization = False)
|
|
632
|
+
sage: x, y = lp[0], lp[1]
|
|
633
|
+
sage: lp.add_constraint(-2*x + y <= 1)
|
|
634
|
+
sage: lp.add_constraint(x - y <= 1)
|
|
635
|
+
sage: lp.add_constraint(x + y >= 2)
|
|
636
|
+
sage: lp.set_objective(x + y)
|
|
637
|
+
sage: lp.set_integer(x)
|
|
638
|
+
sage: lp.set_integer(y)
|
|
639
|
+
sage: lp.solve()
|
|
640
|
+
2.0
|
|
641
|
+
sage: lp.get_values([x, y])
|
|
642
|
+
[1.0, 1.0]
|
|
643
|
+
|
|
644
|
+
.. NOTE::
|
|
645
|
+
|
|
646
|
+
This method raises ``MIPSolverException`` exceptions when
|
|
647
|
+
the solution can not be computed for any reason (none
|
|
648
|
+
exists, or the LP solver was not able to find it, etc...)
|
|
649
|
+
|
|
650
|
+
EXAMPLES::
|
|
651
|
+
|
|
652
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
653
|
+
sage: p = get_solver(solver = "SCIP")
|
|
654
|
+
sage: p.add_linear_constraints(5, 0, None)
|
|
655
|
+
sage: p.add_col(range(5), range(5))
|
|
656
|
+
sage: p.solve()
|
|
657
|
+
0
|
|
658
|
+
sage: p.objective_coefficient(0,1)
|
|
659
|
+
sage: p.solve()
|
|
660
|
+
Traceback (most recent call last):
|
|
661
|
+
...
|
|
662
|
+
MIPSolverException: ...
|
|
663
|
+
|
|
664
|
+
sage: lp = MixedIntegerLinearProgram(solver = "SCIP")
|
|
665
|
+
sage: v = lp.new_variable(nonnegative=True)
|
|
666
|
+
sage: lp.add_constraint(v[1] +v[2] -2.0 *v[3], max=-1.0)
|
|
667
|
+
sage: lp.add_constraint(v[0] -4.0/3 *v[1] +1.0/3 *v[2], max=-1.0/3)
|
|
668
|
+
sage: lp.add_constraint(v[0] +0.5 *v[1] -0.5 *v[2] +0.25 *v[3], max=-0.25)
|
|
669
|
+
sage: lp.solve()
|
|
670
|
+
0.0
|
|
671
|
+
|
|
672
|
+
Solving a LP within the acceptable gap. No exception is raised, even if
|
|
673
|
+
the result is not optimal. To do this, we try to compute the maximum
|
|
674
|
+
number of disjoint balls (of diameter 1) in a hypercube::
|
|
675
|
+
|
|
676
|
+
sage: g = graphs.CubeGraph(9)
|
|
677
|
+
sage: p = MixedIntegerLinearProgram(solver = "SCIP")
|
|
678
|
+
|
|
679
|
+
sage: b = p.new_variable(binary=True)
|
|
680
|
+
sage: p.set_objective(p.sum(b[v] for v in g))
|
|
681
|
+
sage: for v in g:
|
|
682
|
+
....: p.add_constraint(b[v] + p.sum(b[u] for u in g.neighbors(v)) <= 1)
|
|
683
|
+
sage: p.add_constraint(b[v] == 1) # Force an easy non-0 solution
|
|
684
|
+
sage: p.solver_parameter("limits/absgap", 100)
|
|
685
|
+
sage: p.solve() # rel tol 100
|
|
686
|
+
1
|
|
687
|
+
"""
|
|
688
|
+
if self.model.getStatus() != 'unknown' or self.model.getStage() > 1:
|
|
689
|
+
# This should actually be self.model.freeReoptSolve, but it seems to fail.
|
|
690
|
+
self.model.freeTransform()
|
|
691
|
+
self.model.optimize()
|
|
692
|
+
|
|
693
|
+
status = self.model.getStatus()
|
|
694
|
+
|
|
695
|
+
if status == 'unbounded':
|
|
696
|
+
raise MIPSolverException("SCIP: Solution is unbounded")
|
|
697
|
+
elif status == 'infeasible':
|
|
698
|
+
raise MIPSolverException("SCIP: There is no feasible solution")
|
|
699
|
+
# elif status == 'timelimit':
|
|
700
|
+
# raise MIPSolverException("SCIP: Time limit reached")
|
|
701
|
+
return 0
|
|
702
|
+
|
|
703
|
+
cpdef get_objective_value(self):
|
|
704
|
+
"""
|
|
705
|
+
Return the value of the objective function.
|
|
706
|
+
|
|
707
|
+
.. NOTE::
|
|
708
|
+
|
|
709
|
+
Behaviour is undefined unless ``solve`` has been called before.
|
|
710
|
+
|
|
711
|
+
EXAMPLES::
|
|
712
|
+
|
|
713
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
714
|
+
sage: p = get_solver(solver = "SCIP")
|
|
715
|
+
sage: p.add_variables(2)
|
|
716
|
+
1
|
|
717
|
+
sage: p.add_linear_constraint([[0, 1], [1, 2]], None, 3)
|
|
718
|
+
sage: p.set_objective([2, 5])
|
|
719
|
+
sage: p.solve()
|
|
720
|
+
0
|
|
721
|
+
sage: p.get_objective_value()
|
|
722
|
+
7.5
|
|
723
|
+
sage: p.get_variable_value(0) # abs tol 1e-15
|
|
724
|
+
0.0
|
|
725
|
+
sage: p.get_variable_value(1)
|
|
726
|
+
1.5
|
|
727
|
+
"""
|
|
728
|
+
return self.model.getObjVal()
|
|
729
|
+
|
|
730
|
+
cpdef best_known_objective_bound(self):
|
|
731
|
+
r"""
|
|
732
|
+
Return the value of the currently best known bound.
|
|
733
|
+
|
|
734
|
+
This method returns the current best upper (resp. lower) bound on the
|
|
735
|
+
optimal value of the objective function in a maximization
|
|
736
|
+
(resp. minimization) problem. It is equal to the output of
|
|
737
|
+
:meth:`get_objective_value` if the MILP found an optimal solution, but
|
|
738
|
+
it can differ if it was interrupted manually or after a time limit (cf
|
|
739
|
+
:meth:`solver_parameter`).
|
|
740
|
+
|
|
741
|
+
.. NOTE::
|
|
742
|
+
|
|
743
|
+
Has no meaning unless ``solve`` has been called before.
|
|
744
|
+
|
|
745
|
+
EXAMPLES::
|
|
746
|
+
|
|
747
|
+
sage: # needs sage.graphs
|
|
748
|
+
sage: g = graphs.CubeGraph(9)
|
|
749
|
+
sage: p = MixedIntegerLinearProgram(solver='SCIP')
|
|
750
|
+
sage: p.solver_parameter("limits/gap", 100)
|
|
751
|
+
sage: b = p.new_variable(binary=True)
|
|
752
|
+
sage: p.set_objective(p.sum(b[v] for v in g))
|
|
753
|
+
sage: for v in g:
|
|
754
|
+
....: p.add_constraint(b[v]+p.sum(b[u] for u in g.neighbors(v)) <= 1)
|
|
755
|
+
sage: p.add_constraint(b[v] == 1) # Force an easy non-0 solution
|
|
756
|
+
sage: p.solve() # rel tol 100
|
|
757
|
+
1.0
|
|
758
|
+
sage: backend = p.get_backend()
|
|
759
|
+
sage: backend.best_known_objective_bound() # random
|
|
760
|
+
31.0
|
|
761
|
+
"""
|
|
762
|
+
return self.model.getPrimalbound()
|
|
763
|
+
|
|
764
|
+
cpdef get_relative_objective_gap(self):
|
|
765
|
+
r"""
|
|
766
|
+
Return the relative objective gap of the best known solution.
|
|
767
|
+
|
|
768
|
+
For a minimization problem, this value is computed by
|
|
769
|
+
`(\texttt{bestinteger} - \texttt{bestobjective}) / (1e-10 +
|
|
770
|
+
|\texttt{bestobjective}|)`, where ``bestinteger`` is the value returned
|
|
771
|
+
by :meth:`get_objective_value` and ``bestobjective`` is the value
|
|
772
|
+
returned by :meth:`best_known_objective_bound`. For a maximization
|
|
773
|
+
problem, the value is computed by `(\texttt{bestobjective} -
|
|
774
|
+
\texttt{bestinteger}) / (1e-10 + |\texttt{bestobjective}|)`.
|
|
775
|
+
|
|
776
|
+
.. NOTE::
|
|
777
|
+
|
|
778
|
+
Has no meaning unless ``solve`` has been called before.
|
|
779
|
+
|
|
780
|
+
EXAMPLES::
|
|
781
|
+
|
|
782
|
+
sage: # needs sage.graphs
|
|
783
|
+
sage: g = graphs.CubeGraph(9)
|
|
784
|
+
sage: p = MixedIntegerLinearProgram(solver='SCIP')
|
|
785
|
+
sage: p.solver_parameter("limits/gap", 100)
|
|
786
|
+
sage: b = p.new_variable(binary=True)
|
|
787
|
+
sage: p.set_objective(p.sum(b[v] for v in g))
|
|
788
|
+
sage: for v in g:
|
|
789
|
+
....: p.add_constraint(b[v]+p.sum(b[u] for u in g.neighbors(v)) <= 1)
|
|
790
|
+
sage: p.add_constraint(b[v] == 1) # Force an easy non-0 solution
|
|
791
|
+
sage: p.solve() # rel tol 100
|
|
792
|
+
1.0
|
|
793
|
+
sage: backend = p.get_backend()
|
|
794
|
+
sage: backend.get_relative_objective_gap() # random
|
|
795
|
+
46.99999999999999
|
|
796
|
+
|
|
797
|
+
TESTS:
|
|
798
|
+
|
|
799
|
+
Just make sure that the variable *has* been defined, and is not just
|
|
800
|
+
undefined::
|
|
801
|
+
|
|
802
|
+
sage: backend.get_relative_objective_gap() > 1 # needs sage.graphs
|
|
803
|
+
True
|
|
804
|
+
"""
|
|
805
|
+
return self.model.getGap()
|
|
806
|
+
|
|
807
|
+
cpdef get_variable_value(self, int variable):
|
|
808
|
+
"""
|
|
809
|
+
Return the value of a variable given by the solver.
|
|
810
|
+
|
|
811
|
+
.. NOTE::
|
|
812
|
+
|
|
813
|
+
Behaviour is undefined unless ``solve`` has been called before.
|
|
814
|
+
|
|
815
|
+
EXAMPLES::
|
|
816
|
+
|
|
817
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
818
|
+
sage: p = get_solver(solver = "SCIP")
|
|
819
|
+
sage: p.add_variables(2)
|
|
820
|
+
1
|
|
821
|
+
sage: p.add_linear_constraint([[0, 1], [1, 2]], None, 3)
|
|
822
|
+
sage: p.set_objective([2, 5])
|
|
823
|
+
sage: p.solve()
|
|
824
|
+
0
|
|
825
|
+
sage: p.get_objective_value()
|
|
826
|
+
7.5
|
|
827
|
+
sage: p.get_variable_value(0) # abs tol 1e-15
|
|
828
|
+
0.0
|
|
829
|
+
sage: p.get_variable_value(1)
|
|
830
|
+
1.5
|
|
831
|
+
"""
|
|
832
|
+
return self.model.getVal(self.variables[variable])
|
|
833
|
+
|
|
834
|
+
cpdef get_row_prim(self, int i):
|
|
835
|
+
r"""
|
|
836
|
+
Return the value of the auxiliary variable associated with i-th row.
|
|
837
|
+
|
|
838
|
+
.. NOTE::
|
|
839
|
+
|
|
840
|
+
Behaviour is undefined unless ``solve`` has been called before.
|
|
841
|
+
|
|
842
|
+
EXAMPLES::
|
|
843
|
+
|
|
844
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
845
|
+
sage: lp = get_solver(solver = "SCIP")
|
|
846
|
+
sage: lp.add_variables(3)
|
|
847
|
+
2
|
|
848
|
+
sage: lp.add_linear_constraint(zip([0, 1, 2], [8, 6, 1]), None, 48)
|
|
849
|
+
sage: lp.add_linear_constraint(zip([0, 1, 2], [4, 2, 1.5]), None, 20)
|
|
850
|
+
sage: lp.add_linear_constraint(zip([0, 1, 2], [2, 1.5, 0.5]), None, 8)
|
|
851
|
+
sage: lp.set_objective([60, 30, 20])
|
|
852
|
+
sage: lp.solver_parameter('presolving/maxrounds',0)
|
|
853
|
+
sage: lp.solve()
|
|
854
|
+
0
|
|
855
|
+
sage: lp.get_objective_value()
|
|
856
|
+
280.0
|
|
857
|
+
sage: lp.get_row_prim(0)
|
|
858
|
+
24.0
|
|
859
|
+
sage: lp.get_row_prim(1)
|
|
860
|
+
20.0
|
|
861
|
+
sage: lp.get_row_prim(2)
|
|
862
|
+
8.0
|
|
863
|
+
"""
|
|
864
|
+
return self.model.getActivity(self.get_constraints()[i])
|
|
865
|
+
|
|
866
|
+
cpdef int ncols(self) noexcept:
|
|
867
|
+
"""
|
|
868
|
+
Return the number of columns/variables.
|
|
869
|
+
|
|
870
|
+
EXAMPLES::
|
|
871
|
+
|
|
872
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
873
|
+
sage: p = get_solver(solver = "SCIP")
|
|
874
|
+
sage: p.ncols()
|
|
875
|
+
0
|
|
876
|
+
sage: p.add_variables(2)
|
|
877
|
+
1
|
|
878
|
+
sage: p.ncols()
|
|
879
|
+
2
|
|
880
|
+
"""
|
|
881
|
+
return len(self.variables)
|
|
882
|
+
|
|
883
|
+
cpdef int nrows(self) noexcept:
|
|
884
|
+
"""
|
|
885
|
+
Return the number of rows/constraints.
|
|
886
|
+
|
|
887
|
+
EXAMPLES::
|
|
888
|
+
|
|
889
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
890
|
+
sage: p = get_solver(solver = "SCIP")
|
|
891
|
+
sage: p.nrows()
|
|
892
|
+
0
|
|
893
|
+
sage: p.add_linear_constraints(2, 2, None)
|
|
894
|
+
sage: p.nrows()
|
|
895
|
+
2
|
|
896
|
+
|
|
897
|
+
TESTS::
|
|
898
|
+
|
|
899
|
+
After calling :meth:`remove_constraints` we know that
|
|
900
|
+
`self.constraints is None`. `SCIP` keeps track of the number
|
|
901
|
+
of constraints, so we can do the optimization::
|
|
902
|
+
|
|
903
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
904
|
+
sage: p = get_solver(solver='SCIP')
|
|
905
|
+
sage: p.add_variables(2)
|
|
906
|
+
1
|
|
907
|
+
sage: p.add_linear_constraint([(0, 2), (1, 3)], None, 6)
|
|
908
|
+
sage: p.row(0)
|
|
909
|
+
([0, 1], [2.0, 3.0])
|
|
910
|
+
sage: p.remove_constraints([0])
|
|
911
|
+
sage: p.nrows()
|
|
912
|
+
0
|
|
913
|
+
"""
|
|
914
|
+
if self.constraints is None:
|
|
915
|
+
return self.model.getNConss()
|
|
916
|
+
return len(self.get_constraints())
|
|
917
|
+
|
|
918
|
+
cpdef col_name(self, int index):
|
|
919
|
+
"""
|
|
920
|
+
Return the ``index``-th col name.
|
|
921
|
+
|
|
922
|
+
INPUT:
|
|
923
|
+
|
|
924
|
+
- ``index`` -- integer; the col's id
|
|
925
|
+
|
|
926
|
+
EXAMPLES::
|
|
927
|
+
|
|
928
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
929
|
+
sage: p = get_solver(solver = "SCIP")
|
|
930
|
+
sage: p.add_variable(name='I am a variable')
|
|
931
|
+
0
|
|
932
|
+
sage: p.col_name(0)
|
|
933
|
+
'I am a variable'
|
|
934
|
+
"""
|
|
935
|
+
return self.variables[index].name
|
|
936
|
+
|
|
937
|
+
cpdef row_name(self, int index):
|
|
938
|
+
"""
|
|
939
|
+
Return the ``index``-th row name.
|
|
940
|
+
|
|
941
|
+
INPUT:
|
|
942
|
+
|
|
943
|
+
- ``index`` -- integer; the row's id
|
|
944
|
+
|
|
945
|
+
EXAMPLES::
|
|
946
|
+
|
|
947
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
948
|
+
sage: p = get_solver(solver = "SCIP")
|
|
949
|
+
sage: p.add_linear_constraints(1, 2, None, names=['Empty constraint 1'])
|
|
950
|
+
sage: p.row_name(0)
|
|
951
|
+
'Empty constraint 1'
|
|
952
|
+
"""
|
|
953
|
+
return self.get_constraints()[index].name
|
|
954
|
+
|
|
955
|
+
cpdef bint is_variable_binary(self, int index) noexcept:
|
|
956
|
+
"""
|
|
957
|
+
Test whether the given variable is of binary type.
|
|
958
|
+
|
|
959
|
+
INPUT:
|
|
960
|
+
|
|
961
|
+
- ``index`` -- integer; the variable's id
|
|
962
|
+
|
|
963
|
+
EXAMPLES::
|
|
964
|
+
|
|
965
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
966
|
+
sage: p = get_solver(solver = "SCIP")
|
|
967
|
+
sage: p.ncols()
|
|
968
|
+
0
|
|
969
|
+
sage: p.add_variable()
|
|
970
|
+
0
|
|
971
|
+
sage: p.set_variable_type(0,0)
|
|
972
|
+
sage: p.is_variable_binary(0)
|
|
973
|
+
True
|
|
974
|
+
"""
|
|
975
|
+
return self.variables[index].vtype() == 'BINARY'
|
|
976
|
+
|
|
977
|
+
cpdef bint is_variable_integer(self, int index) noexcept:
|
|
978
|
+
"""
|
|
979
|
+
Test whether the given variable is of integer type.
|
|
980
|
+
|
|
981
|
+
INPUT:
|
|
982
|
+
|
|
983
|
+
- ``index`` -- integer; the variable's id
|
|
984
|
+
|
|
985
|
+
EXAMPLES::
|
|
986
|
+
|
|
987
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
988
|
+
sage: p = get_solver(solver = "SCIP")
|
|
989
|
+
sage: p.ncols()
|
|
990
|
+
0
|
|
991
|
+
sage: p.add_variable()
|
|
992
|
+
0
|
|
993
|
+
sage: p.set_variable_type(0,1)
|
|
994
|
+
sage: p.is_variable_integer(0)
|
|
995
|
+
True
|
|
996
|
+
"""
|
|
997
|
+
return self.variables[index].vtype() == 'INTEGER'
|
|
998
|
+
|
|
999
|
+
cpdef bint is_variable_continuous(self, int index) noexcept:
|
|
1000
|
+
"""
|
|
1001
|
+
Test whether the given variable is of continuous/real type.
|
|
1002
|
+
|
|
1003
|
+
INPUT:
|
|
1004
|
+
|
|
1005
|
+
- ``index`` -- integer; the variable's id
|
|
1006
|
+
|
|
1007
|
+
EXAMPLES::
|
|
1008
|
+
|
|
1009
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
1010
|
+
sage: p = get_solver(solver = "SCIP")
|
|
1011
|
+
sage: p.ncols()
|
|
1012
|
+
0
|
|
1013
|
+
sage: p.add_variable()
|
|
1014
|
+
0
|
|
1015
|
+
sage: p.is_variable_continuous(0)
|
|
1016
|
+
True
|
|
1017
|
+
sage: p.set_variable_type(0,1)
|
|
1018
|
+
sage: p.is_variable_continuous(0)
|
|
1019
|
+
False
|
|
1020
|
+
"""
|
|
1021
|
+
return self.variables[index].vtype() == 'CONTINUOUS'
|
|
1022
|
+
|
|
1023
|
+
cpdef bint is_maximization(self) noexcept:
|
|
1024
|
+
"""
|
|
1025
|
+
Test whether the problem is a maximization
|
|
1026
|
+
|
|
1027
|
+
EXAMPLES::
|
|
1028
|
+
|
|
1029
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
1030
|
+
sage: p = get_solver(solver = "SCIP")
|
|
1031
|
+
sage: p.is_maximization()
|
|
1032
|
+
True
|
|
1033
|
+
sage: p.set_sense(-1)
|
|
1034
|
+
sage: p.is_maximization()
|
|
1035
|
+
False
|
|
1036
|
+
"""
|
|
1037
|
+
return self.model.getObjectiveSense() != 'minimize'
|
|
1038
|
+
|
|
1039
|
+
cpdef variable_upper_bound(self, int index, value=False):
|
|
1040
|
+
"""
|
|
1041
|
+
Return or define the upper bound on a variable.
|
|
1042
|
+
|
|
1043
|
+
INPUT:
|
|
1044
|
+
|
|
1045
|
+
- ``index`` -- integer; the variable's id
|
|
1046
|
+
|
|
1047
|
+
- ``value`` -- real value, or ``None`` to mean that the
|
|
1048
|
+
variable has not upper bound. When set to ``False``
|
|
1049
|
+
(default), the method returns the current value.
|
|
1050
|
+
|
|
1051
|
+
EXAMPLES::
|
|
1052
|
+
|
|
1053
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
1054
|
+
sage: p = get_solver(solver = "SCIP")
|
|
1055
|
+
sage: p.add_variable()
|
|
1056
|
+
0
|
|
1057
|
+
sage: p.col_bounds(0)
|
|
1058
|
+
(0.0, None)
|
|
1059
|
+
sage: p.variable_upper_bound(0, 5)
|
|
1060
|
+
sage: p.col_bounds(0)
|
|
1061
|
+
(0.0, 5.0)
|
|
1062
|
+
|
|
1063
|
+
TESTS:
|
|
1064
|
+
|
|
1065
|
+
:issue:`14581`::
|
|
1066
|
+
|
|
1067
|
+
sage: P = MixedIntegerLinearProgram(solver='SCIP')
|
|
1068
|
+
sage: x = P["x"]
|
|
1069
|
+
sage: P.set_max(x, 0)
|
|
1070
|
+
sage: P.get_max(x)
|
|
1071
|
+
0.0
|
|
1072
|
+
|
|
1073
|
+
Check that :issue:`10232` is fixed::
|
|
1074
|
+
|
|
1075
|
+
sage: p = get_solver(solver='SCIP')
|
|
1076
|
+
sage: p.variable_upper_bound(2)
|
|
1077
|
+
Traceback (most recent call last):
|
|
1078
|
+
...
|
|
1079
|
+
ValueError: The variable's id must satisfy 0 <= id < number_of_variables
|
|
1080
|
+
sage: p.variable_upper_bound(3, 5)
|
|
1081
|
+
Traceback (most recent call last):
|
|
1082
|
+
...
|
|
1083
|
+
ValueError: The variable's id must satisfy 0 <= id < number_of_variables
|
|
1084
|
+
|
|
1085
|
+
sage: p.add_variable()
|
|
1086
|
+
0
|
|
1087
|
+
sage: p.variable_upper_bound(0, 'hey!')
|
|
1088
|
+
Traceback (most recent call last):
|
|
1089
|
+
...
|
|
1090
|
+
TypeError: must be real number, not str
|
|
1091
|
+
"""
|
|
1092
|
+
if index < 0 or index >= self.ncols():
|
|
1093
|
+
raise ValueError("The variable's id must satisfy 0 <= id < number_of_variables")
|
|
1094
|
+
var = self.variables[index]
|
|
1095
|
+
if value is False:
|
|
1096
|
+
return var.getUbOriginal()
|
|
1097
|
+
else:
|
|
1098
|
+
self.model.chgVarUb(var=var, ub=value)
|
|
1099
|
+
|
|
1100
|
+
cpdef variable_lower_bound(self, int index, value=False):
|
|
1101
|
+
"""
|
|
1102
|
+
Return or define the lower bound on a variable.
|
|
1103
|
+
|
|
1104
|
+
INPUT:
|
|
1105
|
+
|
|
1106
|
+
- ``index`` -- integer; the variable's id
|
|
1107
|
+
|
|
1108
|
+
- ``value`` -- real value, or ``None`` to mean that the
|
|
1109
|
+
variable has not lower bound. When set to ``False``
|
|
1110
|
+
(default), the method returns the current value.
|
|
1111
|
+
|
|
1112
|
+
EXAMPLES::
|
|
1113
|
+
|
|
1114
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
1115
|
+
sage: p = get_solver(solver = "SCIP")
|
|
1116
|
+
sage: p.add_variable()
|
|
1117
|
+
0
|
|
1118
|
+
sage: p.col_bounds(0)
|
|
1119
|
+
(0.0, None)
|
|
1120
|
+
sage: p.variable_lower_bound(0, 5)
|
|
1121
|
+
sage: p.col_bounds(0)
|
|
1122
|
+
(5.0, None)
|
|
1123
|
+
|
|
1124
|
+
TESTS:
|
|
1125
|
+
|
|
1126
|
+
:issue:`14581`::
|
|
1127
|
+
|
|
1128
|
+
sage: P = MixedIntegerLinearProgram(solver='SCIP')
|
|
1129
|
+
sage: x = P["x"]
|
|
1130
|
+
sage: P.set_min(x, 5)
|
|
1131
|
+
sage: P.set_min(x, 0)
|
|
1132
|
+
sage: P.get_min(x)
|
|
1133
|
+
0.0
|
|
1134
|
+
|
|
1135
|
+
Check that :issue:`10232` is fixed::
|
|
1136
|
+
|
|
1137
|
+
sage: p = get_solver(solver='SCIP')
|
|
1138
|
+
sage: p.variable_lower_bound(2)
|
|
1139
|
+
Traceback (most recent call last):
|
|
1140
|
+
...
|
|
1141
|
+
ValueError: The variable's id must satisfy 0 <= id < number_of_variables
|
|
1142
|
+
sage: p.variable_lower_bound(3, 5)
|
|
1143
|
+
Traceback (most recent call last):
|
|
1144
|
+
...
|
|
1145
|
+
ValueError: The variable's id must satisfy 0 <= id < number_of_variables
|
|
1146
|
+
|
|
1147
|
+
sage: p.add_variable()
|
|
1148
|
+
0
|
|
1149
|
+
sage: p.variable_lower_bound(0, 'hey!')
|
|
1150
|
+
Traceback (most recent call last):
|
|
1151
|
+
...
|
|
1152
|
+
TypeError: must be real number, not str
|
|
1153
|
+
"""
|
|
1154
|
+
if index < 0 or index >= self.ncols():
|
|
1155
|
+
raise ValueError("The variable's id must satisfy 0 <= id < number_of_variables")
|
|
1156
|
+
var = self.variables[index]
|
|
1157
|
+
if value is False:
|
|
1158
|
+
return var.getLbOriginal()
|
|
1159
|
+
else:
|
|
1160
|
+
self.model.chgVarLb(var=var, lb=value)
|
|
1161
|
+
|
|
1162
|
+
cpdef write_cip(self, filename):
|
|
1163
|
+
"""
|
|
1164
|
+
Write the problem to a ``.cip`` file.
|
|
1165
|
+
|
|
1166
|
+
INPUT:
|
|
1167
|
+
|
|
1168
|
+
- ``filename`` -- string
|
|
1169
|
+
|
|
1170
|
+
EXAMPLES::
|
|
1171
|
+
|
|
1172
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
1173
|
+
sage: p = get_solver(solver = "SCIP")
|
|
1174
|
+
sage: p.add_variables(2)
|
|
1175
|
+
1
|
|
1176
|
+
sage: p.add_linear_constraint([[0, 1], [1, 2]], None, 3)
|
|
1177
|
+
sage: p.set_objective([2, 5])
|
|
1178
|
+
sage: import tempfile
|
|
1179
|
+
sage: with tempfile.NamedTemporaryFile(suffix='.cip') as f:
|
|
1180
|
+
....: p.write_cip(f.name)
|
|
1181
|
+
wrote problem to file ...
|
|
1182
|
+
"""
|
|
1183
|
+
self.model.writeProblem(filename)
|
|
1184
|
+
|
|
1185
|
+
cpdef write_lp(self, filename):
|
|
1186
|
+
"""
|
|
1187
|
+
Write the problem to a ``.lp`` file.
|
|
1188
|
+
|
|
1189
|
+
INPUT:
|
|
1190
|
+
|
|
1191
|
+
- ``filename`` -- string
|
|
1192
|
+
|
|
1193
|
+
EXAMPLES::
|
|
1194
|
+
|
|
1195
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
1196
|
+
sage: p = get_solver(solver = "SCIP")
|
|
1197
|
+
sage: p.add_variables(2)
|
|
1198
|
+
1
|
|
1199
|
+
sage: p.add_linear_constraint([[0, 1], [1, 2]], None, 3)
|
|
1200
|
+
sage: p.set_objective([2, 5])
|
|
1201
|
+
sage: import tempfile
|
|
1202
|
+
sage: with tempfile.NamedTemporaryFile(suffix='.lp') as f:
|
|
1203
|
+
....: p.write_lp(f.name)
|
|
1204
|
+
wrote problem to file ...
|
|
1205
|
+
"""
|
|
1206
|
+
filenamestr = filename
|
|
1207
|
+
fname, fext = splitext(filenamestr)
|
|
1208
|
+
|
|
1209
|
+
if fext.lower() != 'lp':
|
|
1210
|
+
filenamestr = filename + '.lp'
|
|
1211
|
+
|
|
1212
|
+
self.model.writeProblem(filenamestr)
|
|
1213
|
+
|
|
1214
|
+
cpdef write_mps(self, filename, int modern):
|
|
1215
|
+
"""
|
|
1216
|
+
Write the problem to a ``.mps`` file.
|
|
1217
|
+
|
|
1218
|
+
INPUT:
|
|
1219
|
+
|
|
1220
|
+
- ``filename`` -- string
|
|
1221
|
+
|
|
1222
|
+
EXAMPLES::
|
|
1223
|
+
|
|
1224
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
1225
|
+
sage: p = get_solver(solver = "SCIP")
|
|
1226
|
+
sage: p.add_variables(2)
|
|
1227
|
+
1
|
|
1228
|
+
sage: p.add_linear_constraint([[0, 1], [1, 2]], None, 3)
|
|
1229
|
+
sage: p.set_objective([2, 5])
|
|
1230
|
+
sage: import tempfile
|
|
1231
|
+
sage: with tempfile.NamedTemporaryFile(suffix='.mps') as f:
|
|
1232
|
+
....: p.write_mps(f.name, 2)
|
|
1233
|
+
wrote problem to file ...
|
|
1234
|
+
"""
|
|
1235
|
+
filenamestr = filename
|
|
1236
|
+
fname, fext = splitext(filenamestr)
|
|
1237
|
+
|
|
1238
|
+
if fext.lower() != 'mps':
|
|
1239
|
+
filenamestr = filename + '.mps'
|
|
1240
|
+
|
|
1241
|
+
self.model.writeProblem(filenamestr)
|
|
1242
|
+
|
|
1243
|
+
cpdef __copy__(self):
|
|
1244
|
+
"""
|
|
1245
|
+
Return a copy of ``self``.
|
|
1246
|
+
|
|
1247
|
+
EXAMPLES::
|
|
1248
|
+
|
|
1249
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
1250
|
+
sage: p = MixedIntegerLinearProgram(solver = "SCIP")
|
|
1251
|
+
sage: b = p.new_variable()
|
|
1252
|
+
sage: p.add_constraint(b[1] + b[2] <= 6)
|
|
1253
|
+
sage: p.set_objective(b[1] + b[2])
|
|
1254
|
+
sage: copy(p).solve()
|
|
1255
|
+
6.0
|
|
1256
|
+
"""
|
|
1257
|
+
cdef SCIPBackend cp = type(self)(maximization=self.is_maximization())
|
|
1258
|
+
cp.model = Model(sourceModel=self.model, origcopy=True)
|
|
1259
|
+
cp.problem_name(self.problem_name())
|
|
1260
|
+
cp.obj_constant_term = self.obj_constant_term
|
|
1261
|
+
cp.variables = cp.model.getVars()
|
|
1262
|
+
return cp
|
|
1263
|
+
|
|
1264
|
+
cpdef solver_parameter(self, name, value=None):
|
|
1265
|
+
"""
|
|
1266
|
+
Return or define a solver parameter.
|
|
1267
|
+
|
|
1268
|
+
INPUT:
|
|
1269
|
+
|
|
1270
|
+
- ``name`` -- string; the parameter
|
|
1271
|
+
|
|
1272
|
+
- ``value`` -- the parameter's value if it is to be defined,
|
|
1273
|
+
or ``None`` (default) to obtain its current value
|
|
1274
|
+
|
|
1275
|
+
EXAMPLES:
|
|
1276
|
+
|
|
1277
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
1278
|
+
sage: p = get_solver(solver='SCIP')
|
|
1279
|
+
sage: p.solver_parameter("limits/time", 1)
|
|
1280
|
+
sage: p.solver_parameter("limits/time")
|
|
1281
|
+
1.0
|
|
1282
|
+
"""
|
|
1283
|
+
if value is not None:
|
|
1284
|
+
if name.lower() == 'timelimit':
|
|
1285
|
+
self.model.setRealParam("limits/time", float(value))
|
|
1286
|
+
else:
|
|
1287
|
+
self.model.setParam(name, value)
|
|
1288
|
+
else:
|
|
1289
|
+
return self.model.getParam(name)
|