passagemath-polyhedra 10.6.31rc3__cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_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.

Potentially problematic release.


This version of passagemath-polyhedra might be problematic. Click here for more details.

Files changed (206) hide show
  1. passagemath_polyhedra-10.6.31rc3.dist-info/METADATA +367 -0
  2. passagemath_polyhedra-10.6.31rc3.dist-info/METADATA.bak +369 -0
  3. passagemath_polyhedra-10.6.31rc3.dist-info/RECORD +206 -0
  4. passagemath_polyhedra-10.6.31rc3.dist-info/WHEEL +6 -0
  5. passagemath_polyhedra-10.6.31rc3.dist-info/top_level.txt +2 -0
  6. passagemath_polyhedra.libs/libgmp-6e109695.so.10.5.0 +0 -0
  7. passagemath_polyhedra.libs/libgomp-e985bcbb.so.1.0.0 +0 -0
  8. sage/all__sagemath_polyhedra.py +50 -0
  9. sage/game_theory/all.py +8 -0
  10. sage/game_theory/catalog.py +6 -0
  11. sage/game_theory/catalog_normal_form_games.py +923 -0
  12. sage/game_theory/cooperative_game.py +844 -0
  13. sage/game_theory/matching_game.py +1181 -0
  14. sage/game_theory/normal_form_game.py +2697 -0
  15. sage/game_theory/parser.py +275 -0
  16. sage/geometry/all__sagemath_polyhedra.py +22 -0
  17. sage/geometry/cone.py +6940 -0
  18. sage/geometry/cone_catalog.py +847 -0
  19. sage/geometry/cone_critical_angles.py +1027 -0
  20. sage/geometry/convex_set.py +1119 -0
  21. sage/geometry/fan.py +3743 -0
  22. sage/geometry/fan_isomorphism.py +389 -0
  23. sage/geometry/fan_morphism.py +1884 -0
  24. sage/geometry/hasse_diagram.py +202 -0
  25. sage/geometry/hyperplane_arrangement/affine_subspace.py +390 -0
  26. sage/geometry/hyperplane_arrangement/all.py +1 -0
  27. sage/geometry/hyperplane_arrangement/arrangement.py +3895 -0
  28. sage/geometry/hyperplane_arrangement/check_freeness.py +145 -0
  29. sage/geometry/hyperplane_arrangement/hyperplane.py +773 -0
  30. sage/geometry/hyperplane_arrangement/library.py +825 -0
  31. sage/geometry/hyperplane_arrangement/ordered_arrangement.py +642 -0
  32. sage/geometry/hyperplane_arrangement/plot.py +520 -0
  33. sage/geometry/integral_points.py +35 -0
  34. sage/geometry/integral_points_generic_dense.cpython-314-x86_64-linux-gnu.so +0 -0
  35. sage/geometry/integral_points_generic_dense.pyx +7 -0
  36. sage/geometry/lattice_polytope.py +5894 -0
  37. sage/geometry/linear_expression.py +773 -0
  38. sage/geometry/newton_polygon.py +767 -0
  39. sage/geometry/point_collection.cpython-314-x86_64-linux-gnu.so +0 -0
  40. sage/geometry/point_collection.pyx +1008 -0
  41. sage/geometry/polyhedral_complex.py +2616 -0
  42. sage/geometry/polyhedron/all.py +8 -0
  43. sage/geometry/polyhedron/backend_cdd.py +460 -0
  44. sage/geometry/polyhedron/backend_cdd_rdf.py +231 -0
  45. sage/geometry/polyhedron/backend_field.py +347 -0
  46. sage/geometry/polyhedron/backend_normaliz.py +2503 -0
  47. sage/geometry/polyhedron/backend_number_field.py +168 -0
  48. sage/geometry/polyhedron/backend_polymake.py +765 -0
  49. sage/geometry/polyhedron/backend_ppl.py +582 -0
  50. sage/geometry/polyhedron/base.py +1206 -0
  51. sage/geometry/polyhedron/base0.py +1444 -0
  52. sage/geometry/polyhedron/base1.py +886 -0
  53. sage/geometry/polyhedron/base2.py +812 -0
  54. sage/geometry/polyhedron/base3.py +1845 -0
  55. sage/geometry/polyhedron/base4.py +1262 -0
  56. sage/geometry/polyhedron/base5.py +2700 -0
  57. sage/geometry/polyhedron/base6.py +1741 -0
  58. sage/geometry/polyhedron/base7.py +997 -0
  59. sage/geometry/polyhedron/base_QQ.py +1258 -0
  60. sage/geometry/polyhedron/base_RDF.py +98 -0
  61. sage/geometry/polyhedron/base_ZZ.py +934 -0
  62. sage/geometry/polyhedron/base_mutable.py +215 -0
  63. sage/geometry/polyhedron/base_number_field.py +122 -0
  64. sage/geometry/polyhedron/cdd_file_format.py +155 -0
  65. sage/geometry/polyhedron/combinatorial_polyhedron/all.py +1 -0
  66. sage/geometry/polyhedron/combinatorial_polyhedron/base.cpython-314-x86_64-linux-gnu.so +0 -0
  67. sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd +76 -0
  68. sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +3859 -0
  69. sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.cpython-314-x86_64-linux-gnu.so +0 -0
  70. sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd +39 -0
  71. sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +1038 -0
  72. sage/geometry/polyhedron/combinatorial_polyhedron/conversions.cpython-314-x86_64-linux-gnu.so +0 -0
  73. sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pxd +9 -0
  74. sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx +501 -0
  75. sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd +207 -0
  76. sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.cpython-314-x86_64-linux-gnu.so +0 -0
  77. sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +102 -0
  78. sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +2274 -0
  79. sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.cpython-314-x86_64-linux-gnu.so +0 -0
  80. sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd +370 -0
  81. sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pyx +84 -0
  82. sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.cpython-314-x86_64-linux-gnu.so +0 -0
  83. sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd +31 -0
  84. sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx +587 -0
  85. sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.cpython-314-x86_64-linux-gnu.so +0 -0
  86. sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd +52 -0
  87. sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx +560 -0
  88. sage/geometry/polyhedron/constructor.py +773 -0
  89. sage/geometry/polyhedron/double_description.py +753 -0
  90. sage/geometry/polyhedron/double_description_inhomogeneous.py +564 -0
  91. sage/geometry/polyhedron/face.py +1060 -0
  92. sage/geometry/polyhedron/generating_function.py +1810 -0
  93. sage/geometry/polyhedron/lattice_euclidean_group_element.py +178 -0
  94. sage/geometry/polyhedron/library.py +3502 -0
  95. sage/geometry/polyhedron/misc.py +121 -0
  96. sage/geometry/polyhedron/modules/all.py +1 -0
  97. sage/geometry/polyhedron/modules/formal_polyhedra_module.py +155 -0
  98. sage/geometry/polyhedron/palp_database.py +447 -0
  99. sage/geometry/polyhedron/parent.py +1279 -0
  100. sage/geometry/polyhedron/plot.py +1986 -0
  101. sage/geometry/polyhedron/ppl_lattice_polygon.py +556 -0
  102. sage/geometry/polyhedron/ppl_lattice_polytope.py +1257 -0
  103. sage/geometry/polyhedron/representation.py +1723 -0
  104. sage/geometry/pseudolines.py +515 -0
  105. sage/geometry/relative_interior.py +445 -0
  106. sage/geometry/toric_plotter.py +1103 -0
  107. sage/geometry/triangulation/all.py +2 -0
  108. sage/geometry/triangulation/base.cpython-314-x86_64-linux-gnu.so +0 -0
  109. sage/geometry/triangulation/base.pyx +963 -0
  110. sage/geometry/triangulation/data.h +147 -0
  111. sage/geometry/triangulation/data.pxd +4 -0
  112. sage/geometry/triangulation/element.py +914 -0
  113. sage/geometry/triangulation/functions.h +10 -0
  114. sage/geometry/triangulation/functions.pxd +4 -0
  115. sage/geometry/triangulation/point_configuration.py +2256 -0
  116. sage/geometry/triangulation/triangulations.h +49 -0
  117. sage/geometry/triangulation/triangulations.pxd +7 -0
  118. sage/geometry/voronoi_diagram.py +319 -0
  119. sage/interfaces/all__sagemath_polyhedra.py +1 -0
  120. sage/interfaces/polymake.py +2028 -0
  121. sage/numerical/all.py +13 -0
  122. sage/numerical/all__sagemath_polyhedra.py +11 -0
  123. sage/numerical/backends/all.py +1 -0
  124. sage/numerical/backends/all__sagemath_polyhedra.py +1 -0
  125. sage/numerical/backends/cvxopt_backend.cpython-314-x86_64-linux-gnu.so +0 -0
  126. sage/numerical/backends/cvxopt_backend.pyx +1006 -0
  127. sage/numerical/backends/cvxopt_backend_test.py +19 -0
  128. sage/numerical/backends/cvxopt_sdp_backend.cpython-314-x86_64-linux-gnu.so +0 -0
  129. sage/numerical/backends/cvxopt_sdp_backend.pyx +382 -0
  130. sage/numerical/backends/cvxpy_backend.cpython-314-x86_64-linux-gnu.so +0 -0
  131. sage/numerical/backends/cvxpy_backend.pxd +41 -0
  132. sage/numerical/backends/cvxpy_backend.pyx +934 -0
  133. sage/numerical/backends/cvxpy_backend_test.py +13 -0
  134. sage/numerical/backends/generic_backend_test.py +24 -0
  135. sage/numerical/backends/interactivelp_backend.cpython-314-x86_64-linux-gnu.so +0 -0
  136. sage/numerical/backends/interactivelp_backend.pxd +36 -0
  137. sage/numerical/backends/interactivelp_backend.pyx +1231 -0
  138. sage/numerical/backends/interactivelp_backend_test.py +12 -0
  139. sage/numerical/backends/logging_backend.py +391 -0
  140. sage/numerical/backends/matrix_sdp_backend.cpython-314-x86_64-linux-gnu.so +0 -0
  141. sage/numerical/backends/matrix_sdp_backend.pxd +15 -0
  142. sage/numerical/backends/matrix_sdp_backend.pyx +478 -0
  143. sage/numerical/backends/ppl_backend.cpython-314-x86_64-linux-gnu.so +0 -0
  144. sage/numerical/backends/ppl_backend.pyx +1126 -0
  145. sage/numerical/backends/ppl_backend_test.py +13 -0
  146. sage/numerical/backends/scip_backend.cpython-314-x86_64-linux-gnu.so +0 -0
  147. sage/numerical/backends/scip_backend.pxd +22 -0
  148. sage/numerical/backends/scip_backend.pyx +1289 -0
  149. sage/numerical/backends/scip_backend_test.py +13 -0
  150. sage/numerical/interactive_simplex_method.py +5338 -0
  151. sage/numerical/knapsack.py +665 -0
  152. sage/numerical/linear_functions.cpython-314-x86_64-linux-gnu.so +0 -0
  153. sage/numerical/linear_functions.pxd +31 -0
  154. sage/numerical/linear_functions.pyx +1648 -0
  155. sage/numerical/linear_tensor.py +470 -0
  156. sage/numerical/linear_tensor_constraints.py +448 -0
  157. sage/numerical/linear_tensor_element.cpython-314-x86_64-linux-gnu.so +0 -0
  158. sage/numerical/linear_tensor_element.pxd +6 -0
  159. sage/numerical/linear_tensor_element.pyx +459 -0
  160. sage/numerical/mip.cpython-314-x86_64-linux-gnu.so +0 -0
  161. sage/numerical/mip.pxd +40 -0
  162. sage/numerical/mip.pyx +3667 -0
  163. sage/numerical/sdp.cpython-314-x86_64-linux-gnu.so +0 -0
  164. sage/numerical/sdp.pxd +39 -0
  165. sage/numerical/sdp.pyx +1433 -0
  166. sage/rings/all__sagemath_polyhedra.py +3 -0
  167. sage/rings/polynomial/all__sagemath_polyhedra.py +10 -0
  168. sage/rings/polynomial/omega.py +982 -0
  169. sage/schemes/all__sagemath_polyhedra.py +2 -0
  170. sage/schemes/toric/all.py +10 -0
  171. sage/schemes/toric/chow_group.py +1248 -0
  172. sage/schemes/toric/divisor.py +2082 -0
  173. sage/schemes/toric/divisor_class.cpython-314-x86_64-linux-gnu.so +0 -0
  174. sage/schemes/toric/divisor_class.pyx +322 -0
  175. sage/schemes/toric/fano_variety.py +1606 -0
  176. sage/schemes/toric/homset.py +650 -0
  177. sage/schemes/toric/ideal.py +451 -0
  178. sage/schemes/toric/library.py +1322 -0
  179. sage/schemes/toric/morphism.py +1958 -0
  180. sage/schemes/toric/points.py +1032 -0
  181. sage/schemes/toric/sheaf/all.py +1 -0
  182. sage/schemes/toric/sheaf/constructor.py +302 -0
  183. sage/schemes/toric/sheaf/klyachko.py +921 -0
  184. sage/schemes/toric/toric_subscheme.py +905 -0
  185. sage/schemes/toric/variety.py +3460 -0
  186. sage/schemes/toric/weierstrass.py +1078 -0
  187. sage/schemes/toric/weierstrass_covering.py +457 -0
  188. sage/schemes/toric/weierstrass_higher.py +288 -0
  189. sage_wheels/share/reflexive_polytopes/Full2d/zzdb.info +10 -0
  190. sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v03 +0 -0
  191. sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v04 +0 -0
  192. sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v05 +1 -0
  193. sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v06 +1 -0
  194. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.info +22 -0
  195. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v04 +0 -0
  196. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v05 +0 -0
  197. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v06 +0 -0
  198. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v07 +0 -0
  199. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v08 +0 -0
  200. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v09 +0 -0
  201. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v10 +0 -0
  202. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v11 +1 -0
  203. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v12 +1 -0
  204. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v13 +1 -0
  205. sage_wheels/share/reflexive_polytopes/reflexive_polytopes_2d +80 -0
  206. sage_wheels/share/reflexive_polytopes/reflexive_polytopes_3d +37977 -0
sage/numerical/mip.pyx ADDED
@@ -0,0 +1,3667 @@
1
+ # sage_setup: distribution = sagemath-polyhedra
2
+ r"""
3
+ Mixed Integer Linear Programming
4
+
5
+ This module implements classes and methods for the efficient solving of Linear
6
+ Programs (:wikipedia:`LP <Linear_programming>`) and Mixed
7
+ Integer Linear Programs (:wikipedia:`MILP
8
+ <Mixed_integer_linear_programming>`).
9
+
10
+ *Do you want to understand how the simplex method works?* See the
11
+ :mod:`~sage.numerical.interactive_simplex_method` module (educational purposes
12
+ only)
13
+
14
+ Definition
15
+ ----------
16
+
17
+ A linear program (:wikipedia:`LP <Linear_programming>`)
18
+ is an optimization problem (:wikipedia:`Optimization_(mathematics)`)
19
+ in the following form
20
+
21
+ .. MATH::
22
+ \max \{ c^T x \;|\; A x \leq b, x \geq 0 \}
23
+
24
+ with given `A \in \mathbb{R}^{m,n}`, `b \in \mathbb{R}^m`,
25
+ `c \in \mathbb{R}^n` and unknown `x \in \mathbb{R}^{n}`.
26
+ If some or all variables in the vector `x` are restricted over
27
+ the integers `\ZZ`, the problem is called mixed integer
28
+ linear program (:wikipedia:`MILP <Mixed_integer_linear_programming>`).
29
+ A wide variety of problems in optimization
30
+ can be formulated in this standard form. Then, solvers are
31
+ able to calculate a solution.
32
+
33
+ Example
34
+ -------
35
+
36
+ Imagine you want to solve the following linear system of three equations:
37
+
38
+ - `w_0 + w_1 + w_2 - 14 w_3 = 0`
39
+ - `w_1 + 2 w_2 - 8 w_3 = 0`
40
+ - `2 w_2 - 3 w_3 = 0`
41
+
42
+ and this additional inequality:
43
+
44
+ - `w_0 - w_1 - w_2 \geq 0`
45
+
46
+ where all `w_i \in \ZZ^+`. You know that the trivial solution is `w_i=0`,
47
+ but what is the first non-trivial one with `w_3 \geq 1`?
48
+
49
+ A mixed integer linear program can give you an answer:
50
+
51
+ #. You have to create an instance of :class:`MixedIntegerLinearProgram` and
52
+ -- in our case -- specify that it is a minimization.
53
+ #. Create a dictionary ``w`` of nonnegative integer variables ``w`` via ``w =
54
+ p.new_variable(integer=True, nonnegative=True)``.
55
+ #. Add those three equations as equality constraints via
56
+ :meth:`add_constraint <sage.numerical.mip.MixedIntegerLinearProgram.add_constraint>`.
57
+ #. Also add the inequality constraint.
58
+ #. Add an inequality constraint `w_3 \geq 1` to exclude the trivial solution.
59
+ #. Specify the objective function via :meth:`set_objective <sage.numerical.mip.MixedIntegerLinearProgram.set_objective>`.
60
+ In our case that is just `w_3`. If it
61
+ is a pure constraint satisfaction problem, specify it as ``None``.
62
+ #. To check if everything is set up correctly, you can print the problem via
63
+ :meth:`show <sage.numerical.mip.MixedIntegerLinearProgram.show>`.
64
+ #. :meth:`Solve <sage.numerical.mip.MixedIntegerLinearProgram.solve>` it and print the solution.
65
+
66
+ The following example shows all these steps::
67
+
68
+ sage: p = MixedIntegerLinearProgram(maximization=False, solver='GLPK')
69
+ sage: w = p.new_variable(integer=True, nonnegative=True)
70
+ sage: p.add_constraint(w[0] + w[1] + w[2] - 14*w[3] == 0)
71
+ sage: p.add_constraint(w[1] + 2*w[2] - 8*w[3] == 0)
72
+ sage: p.add_constraint(2*w[2] - 3*w[3] == 0)
73
+ sage: p.add_constraint(w[0] - w[1] - w[2] >= 0)
74
+ sage: p.add_constraint(w[3] >= 1)
75
+ sage: p.set_objective(w[3])
76
+ sage: p.show()
77
+ Minimization:
78
+ x_3
79
+ Constraints:
80
+ 0.0 <= x_0 + x_1 + x_2 - 14.0 x_3 <= 0.0
81
+ 0.0 <= x_1 + 2.0 x_2 - 8.0 x_3 <= 0.0
82
+ 0.0 <= 2.0 x_2 - 3.0 x_3 <= 0.0
83
+ - x_0 + x_1 + x_2 <= 0.0
84
+ - x_3 <= -1.0
85
+ Variables:
86
+ x_0 is an integer variable (min=0.0, max=+oo)
87
+ x_1 is an integer variable (min=0.0, max=+oo)
88
+ x_2 is an integer variable (min=0.0, max=+oo)
89
+ x_3 is an integer variable (min=0.0, max=+oo)
90
+ sage: print('Objective Value: {}'.format(p.solve()))
91
+ Objective Value: 2.0
92
+ sage: for i, v in sorted(p.get_values(w, convert=ZZ, tolerance=1e-3).items()):
93
+ ....: print(f'w_{i} = {v}')
94
+ w_0 = 15
95
+ w_1 = 10
96
+ w_2 = 3
97
+ w_3 = 2
98
+
99
+ Different backends compute with different base fields, for example::
100
+
101
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
102
+ sage: p.base_ring()
103
+ Real Double Field
104
+ sage: x = p.new_variable(real=True, nonnegative=True)
105
+ sage: 0.5 + 3/2*x[1]
106
+ 0.5 + 1.5*x_0
107
+
108
+ sage: p = MixedIntegerLinearProgram(solver='ppl')
109
+ sage: p.base_ring()
110
+ Rational Field
111
+ sage: x = p.new_variable(nonnegative=True)
112
+ sage: 0.5 + 3/2*x[1]
113
+ 1/2 + 3/2*x_0
114
+
115
+ More about MIP variables
116
+ ------------------------
117
+
118
+ The underlying MILP backends always work with matrices
119
+ where each column corresponds to a linear variable. The
120
+ variable corresponding to the `i`-th column (counting from 0)
121
+ is displayed as ``x_i``.
122
+
123
+ :class:`MixedIntegerLinearProgram` maintains a dynamic mapping
124
+ from the arbitrary keys indexing the components of :class:`MIPVariable`
125
+ objects to the backend variables (indexed by nonnegative integers).
126
+ Backend variables are created when a component of a :class:`MIPVariable`
127
+ is accessed.
128
+
129
+ To make your code more readable, you can construct one or several
130
+ :class:`MIPVariable` objects that can be arbitrarily named and
131
+ indexed. This can be done by calling :meth:`new_variable` several times,
132
+ or by the following special syntax::
133
+
134
+ sage: mip.<a,b> = MixedIntegerLinearProgram(solver='GLPK')
135
+ sage: a
136
+ MIPVariable a with 0 real components
137
+ sage: 5 + a[1] + 2*b[3]
138
+ 5 + x_0 + 2*x_1
139
+
140
+ Indices can be any object, not necessarily integers. Multi-indices are
141
+ also allowed::
142
+
143
+ sage: a[4, 'string', QQ]
144
+ x_2
145
+ sage: a[4, 'string', QQ] - 7*b[2]
146
+ x_2 - 7*x_3
147
+ sage: mip.show()
148
+ Maximization:
149
+ <BLANKLINE>
150
+ Constraints:
151
+ Variables:
152
+ a[1] = x_0 is a continuous variable (min=-oo, max=+oo)
153
+ b[3] = x_1 is a continuous variable (min=-oo, max=+oo)
154
+ a[(4, 'string', Rational Field)] = x_2 is a continuous variable (min=-oo, max=+oo)
155
+ b[2] = x_3 is a continuous variable (min=-oo, max=+oo)
156
+
157
+ Upper/lower bounds on a variable can be specified either as separate constraints
158
+ (see :meth:`add_constraint <sage.numerical.mip.MixedIntegerLinearProgram.add_constraint>`) or
159
+ using the methods :meth:`set_max <sage.numerical.mip.MixedIntegerLinearProgram.set_max>`
160
+ and :meth:`set_min <sage.numerical.mip.MixedIntegerLinearProgram.set_min>`
161
+ respectively.
162
+
163
+ The default MIP variable
164
+ ------------------------
165
+
166
+ As a special shortcut, it is not necessary to call :meth:`new_variable`.
167
+ A :class:`MixedIntegerLinearProgram` has a default :class:`MIPVariable`,
168
+ whose components are obtained by using the syntax ``mip[key]``, where
169
+ `key` is an arbitrary key::
170
+
171
+ sage: mip = MixedIntegerLinearProgram(solver='GLPK')
172
+ sage: 5 + mip[2] + 2*mip[7]
173
+ 5 + x_0 + 2*x_1
174
+
175
+ Index of functions and methods
176
+ ------------------------------
177
+
178
+ Below are listed the methods of :class:`MixedIntegerLinearProgram`. This module
179
+ also implements the :class:`MIPSolverException` exception, as well as the
180
+ :class:`MIPVariable` class.
181
+
182
+ .. csv-table::
183
+ :class: contentstable
184
+ :widths: 30, 70
185
+ :delim: |
186
+
187
+ :meth:`~MixedIntegerLinearProgram.add_constraint` | Add a constraint to the ``MixedIntegerLinearProgram``
188
+ :meth:`~MixedIntegerLinearProgram.base_ring` | Return the base ring
189
+ :meth:`~MixedIntegerLinearProgram.best_known_objective_bound`| Return the value of the currently best known bound
190
+ :meth:`~MixedIntegerLinearProgram.constraints` | Return a list of constraints, as 3-tuples
191
+ :meth:`~MixedIntegerLinearProgram.default_variable` | Return the default ``MIPVariable`` of ``self``.
192
+ :meth:`~MixedIntegerLinearProgram.get_backend` | Return the backend instance used
193
+ :meth:`~MixedIntegerLinearProgram.get_max` | Return the maximum value of a variable
194
+ :meth:`~MixedIntegerLinearProgram.get_min` | Return the minimum value of a variable
195
+ :meth:`~MixedIntegerLinearProgram.get_objective_value` | Return the value of the objective function
196
+ :meth:`~MixedIntegerLinearProgram.get_relative_objective_gap`| Return the relative objective gap of the best known solution
197
+ :meth:`~MixedIntegerLinearProgram.get_values` | Return values found by the previous call to ``solve()``
198
+ :meth:`~MixedIntegerLinearProgram.is_binary` | Test whether the variable ``e`` is binary
199
+ :meth:`~MixedIntegerLinearProgram.is_integer` | Test whether the variable is an integer
200
+ :meth:`~MixedIntegerLinearProgram.is_real` | Test whether the variable is real
201
+ :meth:`~MixedIntegerLinearProgram.linear_constraints_parent` | Return the parent for all linear constraints
202
+ :meth:`~MixedIntegerLinearProgram.linear_functions_parent` | Return the parent for all linear functions
203
+ :meth:`~MixedIntegerLinearProgram.new_variable` | Return an instance of ``MIPVariable`` associated
204
+ :meth:`~MixedIntegerLinearProgram.number_of_constraints` | Return the number of constraints assigned so far
205
+ :meth:`~MixedIntegerLinearProgram.number_of_variables` | Return the number of variables used so far
206
+ :meth:`~MixedIntegerLinearProgram.polyhedron` | Return the polyhedron defined by the Linear Program
207
+ :meth:`~MixedIntegerLinearProgram.remove_constraint` | Remove a constraint from self
208
+ :meth:`~MixedIntegerLinearProgram.remove_constraints` | Remove several constraints
209
+ :meth:`~MixedIntegerLinearProgram.set_binary` | Set a variable or a ``MIPVariable`` as binary
210
+ :meth:`~MixedIntegerLinearProgram.set_integer` | Set a variable or a ``MIPVariable`` as integer
211
+ :meth:`~MixedIntegerLinearProgram.set_max` | Set the maximum value of a variable
212
+ :meth:`~MixedIntegerLinearProgram.set_min` | Set the minimum value of a variable
213
+ :meth:`~MixedIntegerLinearProgram.set_objective` | Set the objective of the ``MixedIntegerLinearProgram``
214
+ :meth:`~MixedIntegerLinearProgram.set_problem_name` | Set the name of the ``MixedIntegerLinearProgram``
215
+ :meth:`~MixedIntegerLinearProgram.set_real` | Set a variable or a ``MIPVariable`` as real
216
+ :meth:`~MixedIntegerLinearProgram.show` | Display the ``MixedIntegerLinearProgram`` in a human-readable
217
+ :meth:`~MixedIntegerLinearProgram.solve` | Solve the ``MixedIntegerLinearProgram``
218
+ :meth:`~MixedIntegerLinearProgram.solver_parameter` | Return or define a solver parameter
219
+ :meth:`~MixedIntegerLinearProgram.sum` | Efficiently computes the sum of a sequence of LinearFunction elements
220
+ :meth:`~MixedIntegerLinearProgram.write_lp` | Write the linear program as a LP file
221
+ :meth:`~MixedIntegerLinearProgram.write_mps` | Write the linear program as a MPS file
222
+
223
+ AUTHORS:
224
+
225
+ - Risan (2012/02): added extension for exact computation
226
+ """
227
+
228
+ # ****************************************************************************
229
+ # Copyright (C) 2012 Nathann Cohen <nathann.cohen@gmail.com>
230
+ #
231
+ # Distributed under the terms of the GNU General Public License (GPL)
232
+ # as published by the Free Software Foundation; either version 2 of
233
+ # the License, or (at your option) any later version.
234
+ # https://www.gnu.org/licenses/
235
+ # ****************************************************************************
236
+
237
+ from copy import copy
238
+ from sage.structure.element import Matrix
239
+ from sage.rings.integer_ring import ZZ
240
+
241
+
242
+ cdef class MixedIntegerLinearProgram(SageObject):
243
+ r"""
244
+ The ``MixedIntegerLinearProgram`` class is the link between Sage, linear
245
+ programming (LP) and mixed integer programming (MIP) solvers.
246
+
247
+ A Mixed Integer Linear Program (MILP) consists of variables, linear
248
+ constraints on these variables, and an objective function which is to be
249
+ maximised or minimised under these constraints.
250
+
251
+ See the thematic tutorial on `Linear Programming (Mixed Integer)
252
+ <../../../../thematic_tutorials/linear_programming.html>`_
253
+ or :wikipedia:`Linear_programming` for further information on linear
254
+ programming, and the :mod:`MILP module <sage.numerical.mip>` for its use in
255
+ Sage.
256
+
257
+ INPUT:
258
+
259
+ - ``solver`` -- selects a solver; see `Solvers (backends)
260
+ <../../../../thematic_tutorials/linear_programming.html#solvers-backends>`_
261
+ for more information and installation instructions for optional
262
+ solvers.
263
+
264
+ - ``solver="GLPK"``: the `GNU Linear Programming Kit
265
+ <http://www.gnu.org/software/glpk/>`_.
266
+
267
+ - ``solver="GLPK/exact"``: GLPK's implementation of an exact rational simplex
268
+ method.
269
+
270
+ - ``solver="Coin"``: the `COIN-OR CBC (COIN Branch and Cut) solver
271
+ <http://www.coin-or.org>`_.
272
+
273
+ - ``solver="CPLEX"``: provided by the proprietary `IBM ILOG CPLEX
274
+ Optimization Studio <https://www.ibm.com/products/ilog-cplex-optimization-studio/>`_.
275
+
276
+ - ``solver="Gurobi"``: the proprietary `Gurobi solver <http://www.gurobi.com/>`_.
277
+
278
+ - ``solver="CVXOPT"``: see the `CVXOPT <http://www.cvxopt.org/>`_ web site.
279
+
280
+ - ``solver="PPL"``: an exact rational solver (for small scale instances)
281
+ provided by the `Parma Polyhedra Library (PPL) <http://bugseng.com/products/ppl>`_.
282
+
283
+ - ``solver="InteractiveLP"``: a didactical
284
+ implementation of the revised simplex method in Sage. It works over
285
+ any exact ordered field, the default is ``QQ``.
286
+
287
+ - If ``solver=None`` (default), the default solver is used (see
288
+ :func:`default_mip_solver`).
289
+
290
+ - ``solver`` can also be a callable (such as a class),
291
+ see :func:`sage.numerical.backends.generic_backend.get_solver` for
292
+ examples.
293
+
294
+ - ``maximization``
295
+
296
+ - When set to ``True`` (default), the ``MixedIntegerLinearProgram``
297
+ is defined as a maximization.
298
+
299
+ - When set to ``False``, the ``MixedIntegerLinearProgram`` is
300
+ defined as a minimization.
301
+
302
+ - ``constraint_generation`` -- only used when ``solver=None``
303
+
304
+ - When set to ``True``, after solving the ``MixedIntegerLinearProgram``,
305
+ it is possible to add a constraint, and then solve it again.
306
+ The effect is that solvers that do not support this feature will not be
307
+ used.
308
+
309
+ - Defaults to ``False``.
310
+
311
+ .. SEEALSO::
312
+
313
+ - :func:`default_mip_solver` -- returns/sets the default MIP solver
314
+
315
+ EXAMPLES:
316
+
317
+ Computation of a maximum stable set in Petersen's graph::
318
+
319
+ sage: # needs sage.graphs
320
+ sage: g = graphs.PetersenGraph()
321
+ sage: p = MixedIntegerLinearProgram(maximization=True, solver='GLPK')
322
+ sage: b = p.new_variable(binary=True)
323
+ sage: p.set_objective(sum([b[v] for v in g]))
324
+ sage: for (u,v) in g.edges(sort=False, labels=None):
325
+ ....: p.add_constraint(b[u] + b[v], max=1)
326
+ sage: p.solve(objective_only=True)
327
+ 4.0
328
+
329
+ TESTS:
330
+
331
+ Check that :issue:`16497` is fixed::
332
+
333
+ sage: for type in ["binary", "integer"]:
334
+ ....: k = 3
335
+ ....: items = [1/5, 1/3, 2/3, 3/4, 5/7]
336
+ ....: maximum = 1
337
+ ....: p = MixedIntegerLinearProgram(solver='GLPK')
338
+ ....: box = p.new_variable(nonnegative=True, **{type: True})
339
+ ....: for b in range(k):
340
+ ....: p.add_constraint(p.sum([items[i]*box[i,b] for i in range(len(items))]) <= maximum)
341
+ ....: for i in range(len(items)):
342
+ ....: p.add_constraint(p.sum([box[i,b] for b in range(k)]) == 1)
343
+ ....: p.set_objective(None)
344
+ ....: _ = p.solve()
345
+ ....: box = p.get_values(box)
346
+ ....: print(all(v in ZZ for v in box.values()))
347
+ True
348
+ True
349
+ """
350
+
351
+ def __init__(self, solver=None, maximization=True,
352
+ constraint_generation=False, check_redundant=False,
353
+ names=tuple(), base_ring=None):
354
+ r"""
355
+ Constructor for the ``MixedIntegerLinearProgram`` class.
356
+
357
+ INPUT:
358
+
359
+ - ``solver`` -- one of the following:
360
+
361
+ - a string indicating one of the available solvers
362
+ (see :class:`MixedIntegerLinearProgram`)
363
+
364
+ - ``None`` -- (default) the default solver is used, see
365
+ :func:`default_mip_solver`
366
+
367
+ - or a callable (such as a class), see
368
+ :func:`sage.numerical.backends.generic_backend.get_solver`
369
+ for examples
370
+
371
+ - ``maximization``
372
+
373
+ - When set to ``True`` (default), the ``MixedIntegerLinearProgram``
374
+ is defined as a maximization.
375
+ - When set to ``False``, the ``MixedIntegerLinearProgram`` is
376
+ defined as a minimization.
377
+
378
+ - ``constraint_generation`` -- only used when ``solver=None``:
379
+
380
+ - When set to ``True``, after solving the
381
+ ``MixedIntegerLinearProgram``, it is possible to add a constraint,
382
+ and then solve it again. The effect is that solvers that do not
383
+ support this feature will not be used.
384
+
385
+ - Defaults to ``False``.
386
+
387
+ - ``check_redundant`` -- whether to check that constraints added to the
388
+ program are redundant with constraints already in the program.
389
+ Only obvious redundancies are checked: to be considered redundant,
390
+ either a constraint is equal to another constraint in the program,
391
+ or it is a constant multiple of the other. To make this search
392
+ effective and efficient, constraints are normalized; thus, the
393
+ constraint `-x_1 < 0` will be stored as `x_1 > 0`.
394
+
395
+ - ``names`` -- list/tuple/iterable of string. Default names of
396
+ the MIP variables. Used to enable the ``MIP.<x> =
397
+ MixedIntegerLinearProgram()`` syntax.
398
+
399
+ .. SEEALSO::
400
+
401
+ - :meth:`default_mip_solver` -- returns/sets the default MIP solver
402
+
403
+ EXAMPLES::
404
+
405
+ sage: p = MixedIntegerLinearProgram(maximization=True)
406
+
407
+ TESTS:
408
+
409
+ Checks that the objects are deallocated without invoking the cyclic garbage
410
+ collector (cf. :issue:`12616`)::
411
+
412
+ sage: del p
413
+ sage: def just_create_variables():
414
+ ....: p = MixedIntegerLinearProgram(solver='GLPK')
415
+ ....: b = p.new_variable(nonnegative=True)
416
+ ....: p.add_constraint(b[3] + b[6] <= 2)
417
+ ....: p.solve()
418
+ sage: C = sage.numerical.mip.MixedIntegerLinearProgram
419
+ sage: import gc
420
+ sage: _ = gc.collect() # avoid side effects of other doc tests
421
+ sage: sum([1 for x in gc.get_objects() if isinstance(x,C)])
422
+ 0
423
+
424
+ We now disable the cyclic garbage collector. Since :issue:`12616` avoids
425
+ a reference cycle, the mixed integer linear program created in
426
+ ``just_create_variables()`` is removed even without the cyclic garbage
427
+ collection::
428
+
429
+ sage: gc.disable()
430
+ sage: just_create_variables()
431
+ sage: sum([1 for x in gc.get_objects() if isinstance(x,C)])
432
+ 0
433
+ sage: gc.enable()
434
+ """
435
+ self.__BINARY = 0
436
+ self.__REAL = -1
437
+ self.__INTEGER = 1
438
+ self._first_variable_names = list(names)
439
+ from sage.numerical.backends.generic_backend import get_solver
440
+ self._backend = get_solver(solver=solver,
441
+ constraint_generation=constraint_generation,
442
+ base_ring=base_ring)
443
+ if not maximization:
444
+ self._backend.set_sense(-1)
445
+
446
+ # Associates an index to the variables
447
+ self._variables = {}
448
+
449
+ # Check for redundant constraints
450
+ self._check_redundant = check_redundant
451
+ if check_redundant:
452
+ self._constraints = list()
453
+
454
+ def linear_functions_parent(self):
455
+ """
456
+ Return the parent for all linear functions.
457
+
458
+ EXAMPLES::
459
+
460
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
461
+ sage: p.linear_functions_parent()
462
+ Linear functions over Real Double Field
463
+ """
464
+ if self._linear_functions_parent is None:
465
+ base_ring = self._backend.base_ring()
466
+ from sage.numerical.linear_functions import LinearFunctionsParent
467
+ self._linear_functions_parent = LinearFunctionsParent(base_ring)
468
+ return self._linear_functions_parent
469
+
470
+ def linear_constraints_parent(self):
471
+ """
472
+ Return the parent for all linear constraints.
473
+
474
+ See :mod:`~sage.numerical.linear_functions` for more
475
+ details.
476
+
477
+ EXAMPLES::
478
+
479
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
480
+ sage: p.linear_constraints_parent()
481
+ Linear constraints over Real Double Field
482
+ """
483
+ if self._linear_constraints_parent is None:
484
+ from sage.numerical.linear_functions import LinearConstraintsParent
485
+ LF = self.linear_functions_parent()
486
+ self._linear_constraints_parent = LinearConstraintsParent(LF)
487
+ return self._linear_constraints_parent
488
+
489
+ def _repr_(self):
490
+ r"""
491
+ Return a short description of the ``MixedIntegerLinearProgram``.
492
+
493
+ EXAMPLES::
494
+
495
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
496
+ sage: v = p.new_variable(binary=True)
497
+ sage: p.add_constraint(v[1] + v[2], max=1)
498
+ sage: p
499
+ Boolean Program (no objective, 2 variables, 1 constraint)
500
+ sage: p.set_objective(1); p
501
+ Boolean Program (constant objective, 2 variables, 1 constraint)
502
+ sage: p.set_objective(v[1]); p
503
+ Boolean Program (maximization, 2 variables, 1 constraint)
504
+ """
505
+ cdef GenericBackend b = self._backend
506
+
507
+ cdef int nvars = b.ncols()
508
+ cdef int i
509
+
510
+ cdef int have_int = 0, have_bool = 0, have_cont = 0
511
+ for i in range(nvars):
512
+ if b.is_variable_binary(i):
513
+ have_bool = 1
514
+ elif b.is_variable_integer(i):
515
+ have_int = 1
516
+ else:
517
+ have_cont = 1
518
+
519
+ if have_cont:
520
+ if have_int or have_bool:
521
+ kind = "Mixed Integer Program"
522
+ else:
523
+ kind = "Linear Program"
524
+ elif have_int:
525
+ kind = "Integer Program"
526
+ elif have_bool:
527
+ kind = "Boolean Program"
528
+ else:
529
+ # We have no variables...
530
+ kind = "Mixed Integer Program"
531
+
532
+ if all(b.objective_coefficient(i) == 0 for i in range(nvars)):
533
+ if b.objective_constant_term():
534
+ minmax = "constant objective"
535
+ else:
536
+ minmax = "no objective"
537
+ elif b.is_maximization():
538
+ minmax = "maximization"
539
+ else:
540
+ minmax = "minimization"
541
+
542
+ def plural(num, noun):
543
+ if num != 1:
544
+ noun = noun + "s"
545
+ return f"{num} {noun}"
546
+
547
+ name = b.problem_name()
548
+ if name:
549
+ kind += f' "{name}"'
550
+
551
+ return f"{kind} ({minmax}, {plural(b.ncols(), 'variable')}, {plural(b.nrows(), 'constraint')})"
552
+
553
+ def __copy__(self):
554
+ r"""
555
+ Return a copy of the current ``MixedIntegerLinearProgram`` instance.
556
+
557
+ EXAMPLES::
558
+
559
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
560
+ sage: v = p.new_variable(nonnegative=True)
561
+ sage: p.add_constraint(v[0] + v[1], max=10)
562
+ sage: q = copy(p)
563
+ sage: q.number_of_constraints()
564
+ 1
565
+
566
+ TESTS:
567
+
568
+ Test that the default MIP variables are independent after copying::
569
+
570
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
571
+ sage: p[0]
572
+ x_0
573
+ sage: q = copy(p)
574
+ sage: q[0]
575
+ x_0
576
+ sage: q[1]
577
+ x_1
578
+ sage: p.number_of_variables()
579
+ 1
580
+ sage: q.number_of_variables()
581
+ 2
582
+ """
583
+ def copying_solver(**kwdargs):
584
+ return (<GenericBackend> self._backend).copy()
585
+
586
+ cdef MixedIntegerLinearProgram p = \
587
+ MixedIntegerLinearProgram(solver=copying_solver)
588
+
589
+ p._variables = copy(self._variables)
590
+
591
+ if self._default_mipvariable is not None:
592
+ p._default_mipvariable = self._default_mipvariable.copy_for_mip(p)
593
+
594
+ p._check_redundant = self._check_redundant
595
+ p._constraints = copy(self._constraints)
596
+
597
+ return p
598
+
599
+ def __deepcopy__(self, memo={}):
600
+ """
601
+ Return a deep copy of ``self``.
602
+
603
+ EXAMPLES::
604
+
605
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
606
+ sage: b = p.new_variable()
607
+ sage: p.add_constraint(b[1] + b[2] <= 6)
608
+ sage: p.set_objective(b[1] + b[2])
609
+ sage: cp = deepcopy(p)
610
+ sage: cp.solve()
611
+ 6.0
612
+
613
+ TESTS:
614
+
615
+ Test that `deepcopy` makes actual copies but preserves identities::
616
+
617
+ sage: mip = MixedIntegerLinearProgram(solver='GLPK')
618
+ sage: ll = [mip, mip]
619
+ sage: dcll=deepcopy(ll)
620
+ sage: ll[0] is dcll[0]
621
+ False
622
+ sage: dcll[0] is dcll[1]
623
+ True
624
+ """
625
+ return self.__copy__()
626
+
627
+ def __getitem__(self, v):
628
+ r"""
629
+ Return the symbolic variable corresponding to the key
630
+ from the default :class:`MIPVariable` instance.
631
+
632
+ It returns the element asked, creating it if necessary.
633
+ If necessary, it also creates the default :class:`MIPVariable` instance.
634
+
635
+ See :meth:`new_variable` for a way to have separate :class:`MIPVariable`s
636
+ each of which have their own key space.
637
+
638
+ EXAMPLES::
639
+
640
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
641
+ sage: p.set_objective(p['x'] + p['z'])
642
+ sage: p['x']
643
+ x_0
644
+ """
645
+ return self.default_variable()[v]
646
+
647
+ def base_ring(self):
648
+ """
649
+ Return the base ring.
650
+
651
+ OUTPUT: a ring. The coefficients that the chosen solver supports
652
+
653
+ EXAMPLES::
654
+
655
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
656
+ sage: p.base_ring()
657
+ Real Double Field
658
+ sage: p = MixedIntegerLinearProgram(solver='ppl')
659
+ sage: p.base_ring()
660
+ Rational Field
661
+ sage: from sage.rings.qqbar import AA # needs sage.rings.number_field
662
+ sage: p = MixedIntegerLinearProgram(solver='InteractiveLP', base_ring=AA) # needs sage.rings.number_field
663
+ sage: p.base_ring() # needs sage.rings.number_field
664
+ Algebraic Real Field
665
+
666
+ sage: # needs sage.groups sage.rings.number_field
667
+ sage: d = polytopes.dodecahedron()
668
+ sage: p = MixedIntegerLinearProgram(base_ring=d.base_ring())
669
+ sage: p.base_ring()
670
+ Number Field in sqrt5 with defining polynomial x^2 - 5 with sqrt5 = 2.236067977499790?
671
+ """
672
+ return self._backend.base_ring()
673
+
674
+ def set_problem_name(self, name):
675
+ r"""
676
+ Set the name of the ``MixedIntegerLinearProgram``.
677
+
678
+ INPUT:
679
+
680
+ - ``name`` -- string representing the name of the
681
+ ``MixedIntegerLinearProgram``
682
+
683
+ EXAMPLES::
684
+
685
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
686
+ sage: p.set_problem_name("Test program")
687
+ sage: p
688
+ Mixed Integer Program "Test program" (no objective, 0 variables, 0 constraints)
689
+ """
690
+ self._backend.problem_name(name)
691
+
692
+ def new_variable(self, real=False, binary=False, integer=False, nonnegative=False, name='',
693
+ indices=None):
694
+ r"""
695
+ Return a new :class:`MIPVariable` instance.
696
+
697
+ A new variable ``x`` is defined by::
698
+
699
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
700
+ sage: x = p.new_variable(nonnegative=True)
701
+
702
+ It behaves exactly as a usual dictionary would. It can use any key
703
+ argument you may like, as ``x[5]`` or ``x["b"]``, and has methods
704
+ ``items()`` and ``keys()``.
705
+
706
+ .. SEEALSO::
707
+
708
+ - :meth:`set_min`, :meth:`get_min` -- set/get the lower bound of a
709
+ variable
710
+
711
+ - :meth:`set_max`, :meth:`get_max` -- set/get the upper bound of a
712
+ variable
713
+
714
+ INPUT:
715
+
716
+ - ``binary``, ``integer``, ``real`` -- boolean. Set one of these
717
+ arguments to ``True`` to ensure that the variable gets the
718
+ corresponding type.
719
+
720
+ - ``nonnegative`` -- boolean (default: ``False``); whether the
721
+ variable should be assumed to be nonnegative. Rather useless
722
+ for the binary type
723
+
724
+ - ``name`` -- string; associates a name to the variable. This
725
+ is only useful when exporting the linear program to a file
726
+ using ``write_mps`` or ``write_lp``, and has no other
727
+ effect.
728
+
729
+ - ``indices`` -- (optional) an iterable of keys; components
730
+ corresponding to these keys are created in order,
731
+ and access to components with other keys will raise an
732
+ error; otherwise components of this variable can be
733
+ indexed by arbitrary keys and are created dynamically
734
+ on access
735
+
736
+ OUTPUT:
737
+
738
+ A new instance of :class:`MIPVariable` associated to the
739
+ current :class:`MixedIntegerLinearProgram`.
740
+
741
+ EXAMPLES::
742
+
743
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
744
+ sage: x = p.new_variable(); x
745
+ MIPVariable with 0 real components
746
+ sage: x0 = x[0]; x0
747
+ x_0
748
+
749
+ By default, variables are unbounded::
750
+
751
+ sage: print(p.get_min(x0))
752
+ None
753
+ sage: print(p.get_max(x0))
754
+ None
755
+
756
+ To define two dictionaries of variables, the first being
757
+ of real type, and the second of integer type ::
758
+
759
+ sage: x = p.new_variable(real=True, nonnegative=True)
760
+ sage: y = p.new_variable(integer=True, nonnegative=True)
761
+ sage: p.add_constraint(x[2] + y[3,5], max=2)
762
+ sage: p.is_integer(x[2])
763
+ False
764
+ sage: p.is_integer(y[3,5])
765
+ True
766
+
767
+ An exception is raised when two types are supplied ::
768
+
769
+ sage: z = p.new_variable(real=True, integer=True)
770
+ Traceback (most recent call last):
771
+ ...
772
+ ValueError: Exactly one of the available types has to be True
773
+
774
+ Unbounded variables::
775
+
776
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
777
+ sage: x = p.new_variable(real=True)
778
+ sage: y = p.new_variable(integer=True)
779
+ sage: p.add_constraint(x[0] + x[3] <= 8)
780
+ sage: p.add_constraint(y[0] >= y[1])
781
+ sage: p.show()
782
+ Maximization:
783
+ <BLANKLINE>
784
+ Constraints:
785
+ x_0 + x_1 <= 8.0
786
+ - x_2 + x_3 <= 0.0
787
+ Variables:
788
+ x_0 is a continuous variable (min=-oo, max=+oo)
789
+ x_1 is a continuous variable (min=-oo, max=+oo)
790
+ x_2 is an integer variable (min=-oo, max=+oo)
791
+ x_3 is an integer variable (min=-oo, max=+oo)
792
+
793
+ On the Sage command line, generator syntax is accepted as a
794
+ shorthand for generating new variables with default settings::
795
+
796
+ sage: mip.<x, y, z> = MixedIntegerLinearProgram(solver='GLPK')
797
+ sage: mip.add_constraint(x[0] + y[1] + z[2] <= 10)
798
+ sage: mip.show()
799
+ Maximization:
800
+ <BLANKLINE>
801
+ Constraints:
802
+ x[0] + y[1] + z[2] <= 10.0
803
+ Variables:
804
+ x[0] = x_0 is a continuous variable (min=-oo, max=+oo)
805
+ y[1] = x_1 is a continuous variable (min=-oo, max=+oo)
806
+ z[2] = x_2 is a continuous variable (min=-oo, max=+oo)
807
+ """
808
+ if sum([real, binary, integer]) > 1:
809
+ raise ValueError("Exactly one of the available types has to be True")
810
+
811
+ if binary:
812
+ vtype = self.__BINARY
813
+ elif integer:
814
+ vtype = self.__INTEGER
815
+ else:
816
+ vtype = self.__REAL
817
+
818
+ if not name and self._first_variable_names:
819
+ name = self._first_variable_names.pop(0)
820
+
821
+ return MIPVariable(self,
822
+ vtype,
823
+ name=name,
824
+ lower_bound=0 if (nonnegative or binary) else None,
825
+ upper_bound=1 if binary else None,
826
+ indices=indices)
827
+
828
+ def default_variable(self):
829
+ """
830
+ Return the default :class:`MIPVariable` of ``self``.
831
+
832
+ EXAMPLES::
833
+
834
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
835
+ sage: p.default_variable()
836
+ MIPVariable with 0 real components
837
+ """
838
+ if self._default_mipvariable is None:
839
+ self._default_mipvariable = self.new_variable()
840
+ return self._default_mipvariable
841
+
842
+ def _first_ngens(self, n):
843
+ """
844
+ Construct the first `n` :class:`MIPVariable`s.
845
+
846
+ This method is used for the generator syntax (see below). You
847
+ probably shouldn't use it for anything else.
848
+
849
+ INPUT:
850
+
851
+ - ``n`` -- integer; the number of variables to construct
852
+
853
+ OUTPUT:
854
+
855
+ A tuple of :class:`MIPVariable` instances.
856
+ They are created as free continuous variables.
857
+
858
+ EXAMPLES::
859
+
860
+ sage: mip.<a,b> = MixedIntegerLinearProgram(solver='GLPK') # indirect doctest
861
+ sage: a[0] + b[2]
862
+ x_0 + x_1
863
+ sage: mip.show()
864
+ Maximization:
865
+ <BLANKLINE>
866
+ Constraints:
867
+ Variables:
868
+ a[0] = x_0 is a continuous variable (min=-oo, max=+oo)
869
+ b[2] = x_1 is a continuous variable (min=-oo, max=+oo)
870
+ """
871
+ return tuple(self.new_variable() for i in range(n))
872
+
873
+ cpdef int number_of_constraints(self) noexcept:
874
+ r"""
875
+ Return the number of constraints assigned so far.
876
+
877
+ EXAMPLES::
878
+
879
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
880
+ sage: p.add_constraint(p[0] - p[2], min=1, max=4)
881
+ sage: p.add_constraint(p[0] - 2*p[1], min=1)
882
+ sage: p.number_of_constraints()
883
+ 2
884
+ """
885
+ return self._backend.nrows()
886
+
887
+ cpdef int number_of_variables(self) noexcept:
888
+ r"""
889
+ Return the number of variables used so far.
890
+
891
+ Note that this is backend-dependent, i.e. we count solver's
892
+ variables rather than user's variables. An example of the latter
893
+ can be seen below: Gurobi converts double inequalities,
894
+ i.e. inequalities like `m <= c^T x <= M`, with `m<M`, into
895
+ equations, by adding extra variables: `c^T x + y = M`, `0 <= y
896
+ <= M-m`.
897
+
898
+ EXAMPLES::
899
+
900
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
901
+ sage: p.add_constraint(p[0] - p[2], max=4)
902
+ sage: p.number_of_variables()
903
+ 2
904
+ sage: p.add_constraint(p[0] - 2*p[1], min=1)
905
+ sage: p.number_of_variables()
906
+ 3
907
+ sage: p = MixedIntegerLinearProgram(solver='glpk')
908
+ sage: p.add_constraint(p[0] - p[2], min=1, max=4)
909
+ sage: p.number_of_variables()
910
+ 2
911
+ sage: p = MixedIntegerLinearProgram(solver='gurobi') # optional - Gurobi
912
+ sage: p.add_constraint(p[0] - p[2], min=1, max=4) # optional - Gurobi
913
+ sage: p.number_of_variables() # optional - Gurobi
914
+ 3
915
+ """
916
+ return self._backend.ncols()
917
+
918
+ def constraints(self, indices=None):
919
+ r"""
920
+ Return a list of constraints, as 3-tuples.
921
+
922
+ INPUT:
923
+
924
+ - ``indices`` -- select which constraint(s) to return
925
+
926
+ - If ``indices = None``, the method returns the list of all the
927
+ constraints.
928
+
929
+ - If ``indices`` is an integer `i`, the method returns constraint
930
+ `i`.
931
+
932
+ - If ``indices`` is a list of integers, the method returns the list
933
+ of the corresponding constraints.
934
+
935
+ OUTPUT:
936
+
937
+ Each constraint is returned as a triple ``lower_bound, (indices,
938
+ coefficients), upper_bound``. For each of those entries, the
939
+ corresponding linear function is the one associating to variable
940
+ ``indices[i]`` the coefficient ``coefficients[i]``, and `0` to all the
941
+ others.
942
+
943
+ ``lower_bound`` and ``upper_bound`` are numerical values.
944
+
945
+ EXAMPLES:
946
+
947
+ First, let us define a small LP::
948
+
949
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
950
+ sage: p.add_constraint(p[0] - p[2], min=1, max=4)
951
+ sage: p.add_constraint(p[0] - 2*p[1], min=1)
952
+
953
+ To obtain the list of all constraints::
954
+
955
+ sage: p.constraints() # not tested
956
+ [(1.0, ([1, 0], [-1.0, 1.0]), 4.0), (1.0, ([2, 0], [-2.0, 1.0]), None)]
957
+
958
+ Or constraint `0` only::
959
+
960
+ sage: p.constraints(0) # not tested
961
+ (1.0, ([1, 0], [-1.0, 1.0]), 4.0)
962
+
963
+ A list of constraints containing only `1`::
964
+
965
+ sage: p.constraints([1]) # not tested
966
+ [(1.0, ([2, 0], [-2.0, 1.0]), None)]
967
+
968
+ TESTS:
969
+
970
+ As the ordering of the variables in each constraint depends on the
971
+ solver used, we define a short function reordering it before it is
972
+ printed. The output would look the same without this function applied::
973
+
974
+ sage: def reorder_constraint(lb, indcoef, ub):
975
+ ....: ind, coef = indcoef
976
+ ....: d = dict(zip(ind, coef))
977
+ ....: ind.sort()
978
+ ....: return (lb, (ind, [d[i] for i in ind]), ub)
979
+
980
+ Running the examples from above, reordering applied::
981
+
982
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
983
+ sage: p.add_constraint(p[0] - p[2], min=1, max=4)
984
+ sage: p.add_constraint(p[0] - 2*p[1], min=1)
985
+ sage: sorted(reorder_constraint(*c) for c in p.constraints())
986
+ [(1.0, ([0, 1], [1.0, -1.0]), 4.0), (1.0, ([0, 2], [1.0, -2.0]), None)]
987
+ sage: reorder_constraint(*p.constraints(0))
988
+ (1.0, ([0, 1], [1.0, -1.0]), 4.0)
989
+ sage: sorted(reorder_constraint(*c) for c in p.constraints([1]))
990
+ [(1.0, ([0, 2], [1.0, -2.0]), None)]
991
+ """
992
+ from sage.rings.integer import Integer as Integer
993
+ cdef int i
994
+ cdef GenericBackend b = self._backend
995
+
996
+ result = []
997
+
998
+ # If indices is None, we actually want to return all constraints
999
+ if indices is None:
1000
+ indices = list(range(b.nrows()))
1001
+
1002
+ # Only one constraint
1003
+ if isinstance(indices, (int, Integer)):
1004
+ lb, ub = b.row_bounds(indices)
1005
+ return (lb, b.row(indices), ub)
1006
+
1007
+ # List of constraints
1008
+ if isinstance(indices, list):
1009
+ for i in indices:
1010
+ lb, ub = b.row_bounds(i)
1011
+ result.append((lb, b.row(i), ub))
1012
+
1013
+ return result
1014
+
1015
+ # Weird Input
1016
+ raise ValueError("constraints() requires a list of integers, though it will accommodate None or an integer.")
1017
+
1018
+ def polyhedron(self, **kwds):
1019
+ r"""
1020
+ Return the polyhedron defined by the Linear Program.
1021
+
1022
+ INPUT:
1023
+
1024
+ All arguments given to this method are forwarded to the constructor of
1025
+ the :func:`Polyhedron` class.
1026
+
1027
+ OUTPUT:
1028
+
1029
+ A :func:`Polyhedron` object whose `i`-th variable represents the `i`-th
1030
+ variable of ``self``.
1031
+
1032
+ .. warning::
1033
+
1034
+ The polyhedron is built from the variables stored by the LP solver
1035
+ (i.e. the output of :meth:`show`). While they usually match the ones
1036
+ created explicitly when defining the LP, a solver like Gurobi has
1037
+ been known to introduce additional variables to store constraints of
1038
+ the type ``lower_bound <= linear_function <= upper bound``. You
1039
+ should be fine if you did not install Gurobi or if you do not use it
1040
+ as a solver, but keep an eye on the number of variables in the
1041
+ polyhedron, or on the output of :meth:`show`. Just in case.
1042
+
1043
+ .. SEEALSO::
1044
+
1045
+ :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.to_linear_program`
1046
+ -- return the :class:`MixedIntegerLinearProgram` object associated
1047
+ with a :func:`Polyhedron` object.
1048
+
1049
+ EXAMPLES:
1050
+
1051
+ A LP on two variables::
1052
+
1053
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
1054
+ sage: p.add_constraint(0 <= 2*p['x'] + p['y'] <= 1)
1055
+ sage: p.add_constraint(0 <= 3*p['y'] + p['x'] <= 2)
1056
+ sage: P = p.polyhedron(); P # needs cddexec
1057
+ A 2-dimensional polyhedron in RDF^2 defined as the convex hull of 4 vertices
1058
+
1059
+ 3-D Polyhedron::
1060
+
1061
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
1062
+ sage: p.add_constraint(0 <= 2*p['x'] + p['y'] + 3*p['z'] <= 1)
1063
+ sage: p.add_constraint(0 <= 2*p['y'] + p['z'] + 3*p['x'] <= 1)
1064
+ sage: p.add_constraint(0 <= 2*p['z'] + p['x'] + 3*p['y'] <= 1)
1065
+ sage: P = p.polyhedron(); P # needs cddexec
1066
+ A 3-dimensional polyhedron in RDF^3 defined as the convex hull of 8 vertices
1067
+
1068
+ An empty polyhedron::
1069
+
1070
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
1071
+ sage: v = p.new_variable(nonnegative=True)
1072
+ sage: p.add_constraint(2*v['x'] + v['y'] + 3*v['z'] <= 1)
1073
+ sage: p.add_constraint(2*v['y'] + v['z'] + 3*v['x'] <= 1)
1074
+ sage: p.add_constraint(2*v['z'] + v['x'] + 3*v['y'] >= 2)
1075
+ sage: P = p.polyhedron(); P # needs cddexec
1076
+ The empty polyhedron in RDF^3
1077
+
1078
+ An unbounded polyhedron::
1079
+
1080
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
1081
+ sage: p.add_constraint(2*p['x'] + p['y'] - p['z'] <= 1)
1082
+ sage: P = p.polyhedron(); P # needs cddexec
1083
+ A 3-dimensional polyhedron in RDF^3 defined as the convex hull of 1 vertex, 1 ray, 2 lines
1084
+
1085
+ A square (see :issue:`14395`) ::
1086
+
1087
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
1088
+ sage: x,y = p['x'], p['y']
1089
+ sage: p.add_constraint(x <= 1)
1090
+ sage: p.add_constraint(x >= -1)
1091
+ sage: p.add_constraint(y <= 1)
1092
+ sage: p.add_constraint(y >= -1)
1093
+ sage: p.polyhedron() # needs cddexec
1094
+ A 2-dimensional polyhedron in RDF^2 defined as the convex hull of 4 vertices
1095
+
1096
+ We can also use a backend that supports exact arithmetic::
1097
+
1098
+ sage: p = MixedIntegerLinearProgram(solver='PPL')
1099
+ sage: x,y = p['x'], p['y']
1100
+ sage: p.add_constraint(x <= 1)
1101
+ sage: p.add_constraint(x >= -1)
1102
+ sage: p.add_constraint(y <= 1)
1103
+ sage: p.add_constraint(y >= -1)
1104
+ sage: p.polyhedron() # needs cddexec
1105
+ A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 4 vertices
1106
+
1107
+ TESTS:
1108
+
1109
+ Check if :issue:`23326` is fixed::
1110
+
1111
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
1112
+ sage: x, y = p['x'], p['y']
1113
+ sage: p.set_min(x, 0); p.set_min(y, 0)
1114
+ sage: p.set_objective(3.5*x + 2.5*y)
1115
+ sage: p.add_constraint(x + y <= 10)
1116
+ sage: p.add_constraint(18.5*x + 5.1*y <= 110.3)
1117
+ sage: p.polyhedron() # needs cddexec
1118
+ A 2-dimensional polyhedron in RDF^2 defined as the convex hull of 4 vertices
1119
+ """
1120
+ from sage.geometry.polyhedron.constructor import Polyhedron
1121
+ cdef GenericBackend b = self._backend
1122
+ cdef int i
1123
+
1124
+ # Constraints
1125
+ inequalities = []
1126
+ equalities = []
1127
+ nvar = self.number_of_variables()
1128
+ for lb, (indices, values), ub in self.constraints():
1129
+ coeffs = dict(zip(indices, values))
1130
+ # Equalities
1131
+ if (lb is not None) and lb == ub:
1132
+ linear_function = []
1133
+ linear_function = [coeffs.get(i,0) for i in range(nvar)]
1134
+ linear_function.insert(0,-lb)
1135
+ equalities.append(linear_function)
1136
+ continue
1137
+ # Lower Bound
1138
+ if lb is not None:
1139
+ linear_function = []
1140
+ linear_function = [coeffs.get(i,0) for i in range(nvar)]
1141
+ linear_function.insert(0,-lb)
1142
+ inequalities.append(linear_function)
1143
+ # Upper Bound
1144
+ if ub is not None:
1145
+ linear_function = []
1146
+ linear_function = [-coeffs.get(i,0) for i in range(nvar)]
1147
+ linear_function.insert(0,ub)
1148
+ inequalities.append(linear_function)
1149
+
1150
+ # Variable bounds
1151
+ zero = [0] * nvar
1152
+ for 0<= i < nvar:
1153
+ lb, ub = b.col_bounds(i)
1154
+ # Fixed variable
1155
+ if (lb is not None) and lb == ub:
1156
+ linear_function = copy(zero)
1157
+ linear_function[i] = 1
1158
+ linear_function.insert(0,-lb)
1159
+ equalities.append(linear_function)
1160
+ continue
1161
+ # Lower bound
1162
+ if lb is not None:
1163
+ linear_function = copy(zero)
1164
+ linear_function[i] = 1
1165
+ linear_function.insert(0,-lb)
1166
+ inequalities.append(linear_function)
1167
+ # Upper bound
1168
+ if ub is not None:
1169
+ linear_function = copy(zero)
1170
+ linear_function[i] = -1
1171
+ linear_function.insert(0,ub)
1172
+ inequalities.append(linear_function)
1173
+ return Polyhedron(ieqs=inequalities, eqns=equalities, **kwds)
1174
+
1175
+ def show(self):
1176
+ r"""
1177
+ Displays the ``MixedIntegerLinearProgram`` in a human-readable
1178
+ way.
1179
+
1180
+ EXAMPLES:
1181
+
1182
+ When constraints and variables have names ::
1183
+
1184
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
1185
+ sage: x = p.new_variable(name='Hey')
1186
+ sage: p.set_objective(x[1] + x[2])
1187
+ sage: p.add_constraint(-3*x[1] + 2*x[2], max=2, name='Constraint_1')
1188
+ sage: p.show()
1189
+ Maximization:
1190
+ Hey[1] + Hey[2]
1191
+ Constraints:
1192
+ Constraint_1: -3.0 Hey[1] + 2.0 Hey[2] <= 2.0
1193
+ Variables:
1194
+ Hey[1] = x_0 is a continuous variable (min=-oo, max=+oo)
1195
+ Hey[2] = x_1 is a continuous variable (min=-oo, max=+oo)
1196
+
1197
+ Without any names ::
1198
+
1199
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
1200
+ sage: x = p.new_variable(nonnegative=True)
1201
+ sage: p.set_objective(x[1] + x[2])
1202
+ sage: p.add_constraint(-3*x[1] + 2*x[2], max=2)
1203
+ sage: p.show()
1204
+ Maximization:
1205
+ x_0 + x_1
1206
+ Constraints:
1207
+ -3.0 x_0 + 2.0 x_1 <= 2.0
1208
+ Variables:
1209
+ x_0 is a continuous variable (min=0.0, max=+oo)
1210
+ x_1 is a continuous variable (min=0.0, max=+oo)
1211
+
1212
+ With `\QQ` coefficients::
1213
+
1214
+ sage: p = MixedIntegerLinearProgram(solver='ppl')
1215
+ sage: x = p.new_variable(nonnegative=True)
1216
+ sage: p.set_objective(x[1] + 1/2*x[2])
1217
+ sage: p.add_constraint(-3/5*x[1] + 2/7*x[2], max=2/5)
1218
+ sage: p.show()
1219
+ Maximization:
1220
+ x_0 + 1/2 x_1
1221
+ Constraints:
1222
+ constraint_0: -3/5 x_0 + 2/7 x_1 <= 2/5
1223
+ Variables:
1224
+ x_0 is a continuous variable (min=0, max=+oo)
1225
+ x_1 is a continuous variable (min=0, max=+oo)
1226
+
1227
+ With a constant term in the objective::
1228
+
1229
+ sage: p = MixedIntegerLinearProgram(solver='ppl')
1230
+ sage: x = p.new_variable(nonnegative=True)
1231
+ sage: p.set_objective(x[0] + 42)
1232
+ sage: p.show()
1233
+ Maximization:
1234
+ x_0 + 42
1235
+ Constraints:
1236
+ Variables:
1237
+ x_0 is a continuous variable (min=0, max=+oo)
1238
+ """
1239
+ cdef int i, j
1240
+ cdef GenericBackend b = self._backend
1241
+
1242
+ # varid_name associates variables id to names
1243
+ varid_name = {}
1244
+ varid_explainer = {}
1245
+ for 0<= i < b.ncols():
1246
+ s = b.col_name(i)
1247
+ default_name = str(self.linear_functions_parent()({i: 1}))
1248
+ if s and s != default_name:
1249
+ varid_name[i] = s
1250
+ varid_explainer[i] = '{0} = {1}'.format(s, default_name)
1251
+ else:
1252
+ varid_explainer[i] = varid_name[i] = default_name
1253
+
1254
+ ##### Sense and objective function
1255
+ print("Maximization:" if b.is_maximization() else "Minimization:")
1256
+ print(" ", end=" ")
1257
+ first = True
1258
+ for 0<= i< b.ncols():
1259
+ c = b.objective_coefficient(i)
1260
+ if c == 0:
1261
+ continue
1262
+ print((("+ " if (not first and c>0) else "") +
1263
+ ("" if c == 1 else ("- " if c == -1 else str(c)+" "))+varid_name[i]
1264
+ ), end=" ")
1265
+ first = False
1266
+ d = b.objective_constant_term()
1267
+ if d > self._backend.zero():
1268
+ print("+ {} ".format(d))
1269
+ elif d < self._backend.zero():
1270
+ print("- {} ".format(-d))
1271
+ print("\n")
1272
+
1273
+ ##### Constraints
1274
+ print("Constraints:")
1275
+ for 0<= i < b.nrows():
1276
+ indices, values = b.row(i)
1277
+ lb, ub = b.row_bounds(i)
1278
+ print(" ", end=" ")
1279
+ # Constraint's name
1280
+ if b.row_name(i):
1281
+ print(b.row_name(i)+":", end=" ")
1282
+ # Lower bound
1283
+ if lb is not None:
1284
+ print(str(lb)+" <=", end=" ")
1285
+ first = True
1286
+ for j, c in sorted(zip(indices, values)):
1287
+ if c == 0:
1288
+ continue
1289
+ print((("+ " if (not first and c>0) else "") +
1290
+ ("" if c == 1 else ("- " if c == -1 else (str(c) + " " if first and c < 0 else ("- " + str(abs(c)) + " " if c < 0 else str(c) + " "))))+varid_name[j]
1291
+ ), end=" ")
1292
+ first = False
1293
+ # Upper bound
1294
+ print("<= "+str(ub) if ub is not None else "")
1295
+
1296
+ ##### Variables
1297
+ print("Variables:")
1298
+ for 0<= i < b.ncols():
1299
+ if b.is_variable_integer(i):
1300
+ var_type = 'an integer'
1301
+ elif b.is_variable_binary(i):
1302
+ var_type = 'a boolean'
1303
+ else:
1304
+ var_type = 'a continuous'
1305
+ name = varid_explainer[i]
1306
+ lb, ub = b.col_bounds(i)
1307
+ print(' {0} is {1} variable (min={2}, max={3})'.format(
1308
+ name, var_type,
1309
+ lb if lb is not None else "-oo",
1310
+ ub if ub is not None else "+oo"))
1311
+
1312
+ def write_mps(self, filename, modern=True):
1313
+ r"""
1314
+ Write the linear program as a MPS file.
1315
+
1316
+ This function export the problem as a MPS file.
1317
+
1318
+ INPUT:
1319
+
1320
+ - ``filename`` -- the file in which you want the problem
1321
+ to be written
1322
+
1323
+ - ``modern`` -- lets you choose between Fixed MPS and Free MPS:
1324
+
1325
+ - ``True`` -- outputs the problem in Free MPS
1326
+ - ``False`` -- outputs the problem in Fixed MPS
1327
+
1328
+ EXAMPLES::
1329
+
1330
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
1331
+ sage: x = p.new_variable(nonnegative=True)
1332
+ sage: p.set_objective(x[1] + x[2])
1333
+ sage: p.add_constraint(-3*x[1] + 2*x[2], max=2, name='OneConstraint')
1334
+ sage: import tempfile
1335
+ sage: with tempfile.NamedTemporaryFile(suffix='.mps') as f:
1336
+ ....: p.write_mps(f.name)
1337
+ Writing problem data to ...
1338
+ 17 records were written
1339
+
1340
+ For information about the MPS file format, see
1341
+ :wikipedia:`MPS_(format)`
1342
+ """
1343
+ self._backend.write_mps(filename, modern)
1344
+
1345
+ def write_lp(self, filename):
1346
+ r"""
1347
+ Write the linear program as a LP file.
1348
+
1349
+ This function export the problem as a LP file.
1350
+
1351
+ INPUT:
1352
+
1353
+ - ``filename`` -- the file in which you want the problem
1354
+ to be written
1355
+
1356
+ EXAMPLES::
1357
+
1358
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
1359
+ sage: x = p.new_variable(nonnegative=True)
1360
+ sage: p.set_objective(x[1] + x[2])
1361
+ sage: p.add_constraint(-3*x[1] + 2*x[2], max=2)
1362
+ sage: import tempfile
1363
+ sage: with tempfile.NamedTemporaryFile(suffix='.lp') as f:
1364
+ ....: p.write_lp(f.name)
1365
+ Writing problem data to ...
1366
+ 9 lines were written
1367
+
1368
+ For more information about the LP file format :
1369
+ http://lpsolve.sourceforge.net/5.5/lp-format.htm
1370
+ """
1371
+ self._backend.write_lp(filename)
1372
+
1373
+ def _backend_variable_value(self, v, tolerance):
1374
+ """
1375
+ Return the value of a variable component in the backend.
1376
+
1377
+ INPUT:
1378
+
1379
+ - ``v`` -- a variable component
1380
+
1381
+ - ``tolerance`` -- ignored
1382
+
1383
+ EXAMPLES::
1384
+
1385
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
1386
+ sage: x = p.new_variable(nonnegative=True)
1387
+ sage: p.set_objective(x[3] + 3*x[4] + x[5])
1388
+ sage: p.add_constraint(x[3] + x[4] + 2*x[5], max=2)
1389
+ sage: p.solve()
1390
+ 6.0
1391
+ sage: p._backend_variable_value(x[4], 1)
1392
+ 2.0
1393
+ sage: type(_)
1394
+ <class 'float'>
1395
+ """
1396
+ return self._backend.get_variable_value(self._variables[v])
1397
+
1398
+ def _backend_variable_value_ZZ(self, v, tolerance):
1399
+ """
1400
+ Return the value of a variable component in the backend as an integer.
1401
+
1402
+ The value is rounded to an integer, and if the difference to the
1403
+ original value is greater than ``tolerance``, raise a :exc:`RuntimeError`.
1404
+
1405
+ INPUT:
1406
+
1407
+ - ``v`` -- a variable component
1408
+
1409
+ - ``tolerance`` -- a nonnegative real number
1410
+
1411
+ OUTPUT: an element of ``ZZ``
1412
+
1413
+ EXAMPLES::
1414
+
1415
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
1416
+ sage: x = p.new_variable(nonnegative=True)
1417
+ sage: p.set_objective(x[3] + 3*x[4] + x[5])
1418
+ sage: p.add_constraint(x[3] + x[4] + 2*x[5], max=2)
1419
+ sage: p.solve()
1420
+ 6.0
1421
+ sage: p._backend_variable_value_ZZ(x[4], 0.01)
1422
+ 2
1423
+ sage: _.parent()
1424
+ Integer Ring
1425
+ sage: p.add_constraint(3*x[4] <= 5)
1426
+ sage: p.solve()
1427
+ 5.3333...
1428
+ sage: p._backend_variable_value_ZZ(x[4], 0.01)
1429
+ Traceback (most recent call last):
1430
+ ...
1431
+ RuntimeError: variable x_1 exceeds integrality tolerance 0.0100...
1432
+ """
1433
+ if tolerance is None:
1434
+ raise TypeError('for converting to integers, a tolerance must be provided')
1435
+ value = self._backend_variable_value(v, tolerance)
1436
+ value_ZZ = ZZ(round(value))
1437
+ if abs(value - value_ZZ) > tolerance:
1438
+ raise RuntimeError(f'variable {v} exceeds integrality tolerance {tolerance}')
1439
+ return value_ZZ
1440
+
1441
+ def _backend_variable_value_bool(self, v, tolerance):
1442
+ """
1443
+ Return the value of a variable component in the backend as a boolean.
1444
+
1445
+ The value is rounded to an integer, and if the difference to the
1446
+ original value is greater than ``tolerance``, raise a :exc:`RuntimeError`.
1447
+
1448
+ If the rounded value is anything other than 0 or 1, also a
1449
+ :exc:`RuntimeError` is raised.
1450
+
1451
+ INPUT:
1452
+
1453
+ - ``v`` -- a variable component
1454
+
1455
+ - ``tolerance`` -- a nonnegative real number
1456
+
1457
+ OUTPUT: a ``bool``
1458
+
1459
+ EXAMPLES::
1460
+
1461
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
1462
+ sage: x = p.new_variable(binary=True)
1463
+ sage: p.set_objective(x[3] + 3*x[4] + x[5])
1464
+ sage: p.add_constraint(x[3] + x[4] + 2*x[5], max=2)
1465
+ sage: p.solve()
1466
+ 4.0
1467
+ sage: p._backend_variable_value_bool(x[4], 0.01)
1468
+ True
1469
+ sage: p._backend_variable_value_bool(x[5], 0.01)
1470
+ False
1471
+ """
1472
+ value_ZZ = self._backend_variable_value_ZZ(v, tolerance)
1473
+ if value_ZZ not in (0, 1):
1474
+ raise RuntimeError(f'variable {v} is {value_ZZ} but should be 0 or 1')
1475
+ return bool(value_ZZ)
1476
+
1477
+ def _backend_variable_value_True(self, v, tolerance):
1478
+ """
1479
+ Return the value of a variable component converted to the base ring or
1480
+ ``ZZ``.
1481
+
1482
+ INPUT:
1483
+
1484
+ - ``v`` -- a variable component
1485
+
1486
+ - ``tolerance`` -- if ``v`` is declared integer or binary, a
1487
+ nonnegative real number; otherwise, ignored
1488
+
1489
+ OUTPUT:
1490
+
1491
+ An element of ``ZZ`` for MIP variables declared integer or binary, or
1492
+ an element of the :meth:`base_ring` otherwise.
1493
+
1494
+ EXAMPLES::
1495
+
1496
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
1497
+ sage: x = p.new_variable(binary=True)
1498
+ sage: p.set_objective(x[3] + 3*x[4] + x[5])
1499
+ sage: p.add_constraint(x[3] + x[4] + 2*x[5], max=2)
1500
+ sage: p.solve()
1501
+ 4.0
1502
+ sage: p._backend_variable_value_True(x[4], 0.01)
1503
+ 1
1504
+
1505
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
1506
+ sage: x = p.new_variable(nonnegative=True)
1507
+ sage: p.set_objective(x[3] + 3*x[4] + x[5])
1508
+ sage: p.add_constraint(x[3] + x[4] + 2*x[5], max=2)
1509
+ sage: p.add_constraint(3*x[4] <= 5)
1510
+ sage: p.solve()
1511
+ 5.3333...
1512
+ sage: p._backend_variable_value_True(x[4], 0.01)
1513
+ 1.6666...
1514
+ sage: _.parent()
1515
+ Real Double Field
1516
+ """
1517
+ if self.is_binary(v) or self.is_integer(v):
1518
+ return self._backend_variable_value_ZZ(v, tolerance)
1519
+ return self.base_ring()(self._backend_variable_value(v, tolerance))
1520
+
1521
+ def get_values(self, *lists, convert=None, tolerance=None):
1522
+ r"""
1523
+ Return values found by the previous call to ``solve()``.
1524
+
1525
+ INPUT:
1526
+
1527
+ - ``*lists`` -- any instance of ``MIPVariable`` (or one of its
1528
+ elements), or lists of them
1529
+
1530
+ - ``convert`` -- (default: ``None``) ``ZZ``, ``bool``, or ``True``:
1531
+
1532
+ - if ``convert=None`` (default), return all variable values as the
1533
+ backend provides them, i.e., as an element of :meth:`base_ring` or a
1534
+ ``float``.
1535
+
1536
+ - if ``convert=ZZ``, convert all variable values from the
1537
+ :meth:`base_ring` by rounding to the nearest integer.
1538
+
1539
+ - if ``convert=bool``, convert all variable values from the
1540
+ :meth:`base_ring` by rounding to 0/1 and converting to ``bool``.
1541
+
1542
+ - if ``convert=True``, use ``ZZ`` for MIP variables declared integer
1543
+ or binary, and convert the values of all other variables to the
1544
+ :meth:`base_ring`.
1545
+
1546
+ - ``tolerance`` -- ``None``, a positive real number, or ``0`` (if
1547
+ :meth:`base_ring` is an exact ring). Required if ``convert`` is not
1548
+ ``None`` and any integer conversion is to be done. If the variable
1549
+ value differs from the nearest integer by more than ``tolerance``,
1550
+ raise a :exc:`RuntimeError`.
1551
+
1552
+ OUTPUT:
1553
+
1554
+ - Each instance of ``MIPVariable`` is replaced by a dictionary
1555
+ containing the numerical values found for each
1556
+ corresponding variable in the instance.
1557
+ - Each element of an instance of a ``MIPVariable`` is replaced
1558
+ by its corresponding numerical value.
1559
+
1560
+ .. NOTE::
1561
+
1562
+ While a variable may be declared as binary or integer, its value is
1563
+ an element of the :meth:`base_ring`, or for the numerical solvers,
1564
+ a ``float``.
1565
+
1566
+ For the numerical solvers, :meth:`base_ring` is ``RDF``, an inexact
1567
+ ring. Code using ``get_values`` should always account for possible
1568
+ numerical errors.
1569
+
1570
+ Even for variables declared as binary or integer, or known to be an
1571
+ integer because of the mathematical properties of the model, the
1572
+ returned values cannot be expected to be exact integers. This is
1573
+ normal behavior of the numerical solvers.
1574
+
1575
+ For correct operation, any user code needs to avoid exact
1576
+ comparisons (``==``, ``!=``) and instead allow for numerical
1577
+ tolerances. The magnitude of the numerical tolerances depends on
1578
+ both the model and the solver.
1579
+
1580
+ The arguments ``convert`` and ``tolerance`` facilitate writing
1581
+ correct code. See examples below.
1582
+
1583
+ EXAMPLES::
1584
+
1585
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
1586
+ sage: x = p.new_variable(nonnegative=True)
1587
+ sage: y = p.new_variable(nonnegative=True)
1588
+ sage: p.set_objective(x[3] + 3*y[2,9] + x[5])
1589
+ sage: p.add_constraint(x[3] + y[2,9] + 2*x[5], max=2)
1590
+ sage: p.solve()
1591
+ 6.0
1592
+
1593
+ To return the value of ``y[2,9]`` in the optimal solution::
1594
+
1595
+ sage: p.get_values(y[2,9])
1596
+ 2.0
1597
+ sage: type(_)
1598
+ <class 'float'>
1599
+
1600
+ To convert the value to the :meth:`base_ring`::
1601
+
1602
+ sage: p.get_values(y[2,9], convert=True)
1603
+ 2.0
1604
+ sage: _.parent()
1605
+ Real Double Field
1606
+
1607
+ To get a dictionary identical to ``x`` containing the
1608
+ values for the corresponding variables in the optimal solution::
1609
+
1610
+ sage: x_sol = p.get_values(x)
1611
+ sage: sorted(x_sol)
1612
+ [3, 5]
1613
+
1614
+ Obviously, it also works with variables of higher dimension::
1615
+
1616
+ sage: y_sol = p.get_values(y)
1617
+
1618
+ We could also have tried ::
1619
+
1620
+ sage: [x_sol, y_sol] = p.get_values(x, y)
1621
+
1622
+ Or::
1623
+
1624
+ sage: [x_sol, y_sol] = p.get_values([x, y])
1625
+
1626
+ Using ``convert`` and ``tolerance``. First, a binary knapsack::
1627
+
1628
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
1629
+ sage: x = p.new_variable(binary=True)
1630
+ sage: p.set_objective(3*x[1] + 4*x[2] + 5*x[3])
1631
+ sage: p.add_constraint(2*x[1] + 3*x[2] + 4*x[3] <= 6)
1632
+ sage: p.solve()
1633
+ 8.0
1634
+ sage: x_opt = p.get_values(x); x_opt
1635
+ {1: 1.0, 2: 0.0, 3: 1.0}
1636
+ sage: type(x_opt[1])
1637
+ <class 'float'>
1638
+ sage: x_opt_ZZ = p.get_values(x, convert=True, tolerance=1e-6); x_opt_ZZ
1639
+ {1: 1, 2: 0, 3: 1}
1640
+ sage: x_opt_ZZ[1].parent()
1641
+ Integer Ring
1642
+ sage: x_opt_bool = p.get_values(x, convert=bool, tolerance=1e-6); x_opt_bool
1643
+ {1: True, 2: False, 3: True}
1644
+
1645
+ Thanks to total unimodularity, single-commodity network flow problems
1646
+ with integer capacities and integer supplies/demands have integer vertex
1647
+ solutions. Hence the integrality of solutions is mathematically
1648
+ guaranteed in an optimal solution if we use the simplex algorithm. A
1649
+ numerical LP solver based on the simplex method such as GLPK will return
1650
+ an integer solution only up to a numerical error. Hence, for correct
1651
+ operation, we should use ``tolerance``::
1652
+
1653
+ sage: p = MixedIntegerLinearProgram(solver='GLPK', maximization=False)
1654
+ sage: x = p.new_variable(nonnegative=True)
1655
+ sage: x.set_max(1)
1656
+ sage: p.add_constraint(x['sa'] + x['sb'] == 1)
1657
+ sage: p.add_constraint(x['sa'] + x['ba'] - x['ab'] - x['at'] == 0)
1658
+ sage: p.add_constraint(x['sb'] + x['ab'] - x['ba'] - x['bt'] == 0)
1659
+ sage: p.set_objective(10*x['sa'] + 10*x['bt'])
1660
+ sage: p.solve()
1661
+ 0.0
1662
+ sage: x_opt = p.get_values(x); x_opt
1663
+ {'ab': 0.0, 'at': 1.0, 'ba': 1.0, 'bt': -0.0, 'sa': 0.0, 'sb': 1.0}
1664
+ sage: x_opt_ZZ = p.get_values(x, convert=ZZ, tolerance=1e-6); x_opt_ZZ
1665
+ {'ab': 0, 'at': 1, 'ba': 1, 'bt': 0, 'sa': 0, 'sb': 1}
1666
+
1667
+ TESTS:
1668
+
1669
+ Test that an error is reported when we try to get the value
1670
+ of something that is not a variable in this problem::
1671
+
1672
+ sage: p.get_values("Something strange")
1673
+ Traceback (most recent call last):
1674
+ ...
1675
+ TypeError: Not a MIPVariable: ...
1676
+ sage: p.get_values("Something stranger", 4711)
1677
+ Traceback (most recent call last):
1678
+ ...
1679
+ TypeError: Not a MIPVariable: ...
1680
+ sage: M1 = MixedIntegerLinearProgram(solver='GLPK')
1681
+ sage: M2 = MixedIntegerLinearProgram(solver='GLPK')
1682
+ sage: x = M1.new_variable()
1683
+ sage: y = M1.new_variable()
1684
+ sage: z = M2.new_variable()
1685
+ sage: M2.add_constraint(z[0] <= 5)
1686
+ sage: M2.solve()
1687
+ 0.0
1688
+ sage: M2.get_values(x)
1689
+ Traceback (most recent call last):
1690
+ ...
1691
+ ValueError: ...
1692
+ sage: M2.get_values(y)
1693
+ Traceback (most recent call last):
1694
+ ...
1695
+ ValueError: ...
1696
+
1697
+ Test input validation for ``convert`` and ``tolerance``::
1698
+
1699
+ sage: M_inexact = MixedIntegerLinearProgram(solver='GLPK')
1700
+ sage: x = M_inexact.new_variable(binary=True)
1701
+ sage: x[1]
1702
+ x_0
1703
+ sage: M_inexact.solve()
1704
+ 0.0
1705
+ sage: M_inexact.get_values(tolerance=0.01)
1706
+ Traceback (most recent call last):
1707
+ ...
1708
+ TypeError: cannot use tolerance if convert is None
1709
+ sage: M_inexact.get_values(x[1], convert=True)
1710
+ Traceback (most recent call last):
1711
+ ...
1712
+ TypeError: for converting to integers, a tolerance must be provided
1713
+ sage: M_inexact.get_values(convert=True, tolerance=0)
1714
+ Traceback (most recent call last):
1715
+ ...
1716
+ ValueError: for an inexact base_ring, tolerance must be positive
1717
+
1718
+ sage: M_exact = MixedIntegerLinearProgram(solver='ppl')
1719
+ sage: x = M_exact.new_variable(binary=True)
1720
+ sage: x[1]
1721
+ x_0
1722
+ sage: M_exact.solve()
1723
+ 0
1724
+ sage: M_exact.get_values(convert=True)
1725
+ []
1726
+ sage: M_exact.get_values(x[1], convert=True)
1727
+ Traceback (most recent call last):
1728
+ ...
1729
+ TypeError: for converting to integers, a tolerance must be provided
1730
+ sage: M_exact.get_values(x[1], convert=True, tolerance=0)
1731
+ 0
1732
+ sage: M_exact.get_values(convert=True, tolerance=-0.2)
1733
+ Traceback (most recent call last):
1734
+ ...
1735
+ ValueError: for an exact base_ring, tolerance must be nonnegative
1736
+ """
1737
+ if convert is None:
1738
+ if tolerance is not None:
1739
+ raise TypeError('cannot use tolerance if convert is None')
1740
+ get_backend_variable_value = self._backend_variable_value
1741
+ else:
1742
+ if tolerance is not None:
1743
+ if self.base_ring().is_exact():
1744
+ if tolerance < 0:
1745
+ raise ValueError('for an exact base_ring, tolerance must be nonnegative')
1746
+ else:
1747
+ if tolerance <= 0:
1748
+ raise ValueError('for an inexact base_ring, tolerance must be positive')
1749
+ if convert is ZZ:
1750
+ get_backend_variable_value = self._backend_variable_value_ZZ
1751
+ elif convert is bool:
1752
+ get_backend_variable_value = self._backend_variable_value_bool
1753
+ elif convert is True:
1754
+ get_backend_variable_value = self._backend_variable_value_True
1755
+ else:
1756
+ raise ValueError('convert should be one of None, ZZ, boolean, True')
1757
+
1758
+ val = []
1759
+ for l in lists:
1760
+ if isinstance(l, MIPVariable):
1761
+ if self != l.mip():
1762
+ raise ValueError("Variable {!r} is a variable from a different problem".format(l))
1763
+ c = {}
1764
+ for (k,v) in l.items():
1765
+ c[k] = get_backend_variable_value(v, tolerance)
1766
+ val.append(c)
1767
+ elif isinstance(l, list):
1768
+ if len(l) == 1:
1769
+ val.append([self.get_values(l[0], convert=convert, tolerance=tolerance)])
1770
+ else:
1771
+ c = []
1772
+ [c.append(self.get_values(ll, convert=convert, tolerance=tolerance)) for ll in l]
1773
+ val.append(c)
1774
+ elif l in self._variables:
1775
+ val.append(get_backend_variable_value(l, tolerance))
1776
+ else:
1777
+ raise TypeError("Not a MIPVariable: {!r}".format(l))
1778
+
1779
+ if len(lists) == 1:
1780
+ return val[0]
1781
+ else:
1782
+ return val
1783
+
1784
+ def set_objective(self, obj):
1785
+ r"""
1786
+ Set the objective of the ``MixedIntegerLinearProgram``.
1787
+
1788
+ INPUT:
1789
+
1790
+ - ``obj`` -- a linear function to be optimized
1791
+ ( can also be set to ``None`` or ``0`` or any number when just
1792
+ looking for a feasible solution )
1793
+
1794
+ EXAMPLES:
1795
+
1796
+ Let's solve the following linear program::
1797
+
1798
+ Maximize:
1799
+ x + 5 * y
1800
+ Constraints:
1801
+ x + 0.2 y <= 4
1802
+ 1.5 * x + 3 * y <= 4
1803
+ Variables:
1804
+ x is Real (min = 0, max = None)
1805
+ y is Real (min = 0, max = None)
1806
+
1807
+ This linear program can be solved as follows::
1808
+
1809
+ sage: p = MixedIntegerLinearProgram(maximization=True, solver='GLPK')
1810
+ sage: x = p.new_variable(nonnegative=True)
1811
+ sage: p.set_objective(x[1] + 5*x[2])
1812
+ sage: p.add_constraint(x[1] + 2/10*x[2], max=4)
1813
+ sage: p.add_constraint(1.5*x[1] + 3*x[2], max=4)
1814
+ sage: round(p.solve(),5)
1815
+ 6.66667
1816
+ sage: p.set_objective(None)
1817
+ sage: _ = p.solve()
1818
+
1819
+ TESTS:
1820
+
1821
+ Test whether numbers as constant objective functions are accepted::
1822
+
1823
+ sage: p = MixedIntegerLinearProgram(maximization=True, solver='GLPK')
1824
+ sage: x = p.new_variable(nonnegative=True)
1825
+ sage: p.set_objective(42)
1826
+ sage: p.solve() # tol 1e-8
1827
+ 42
1828
+ """
1829
+ cdef list values = []
1830
+
1831
+ # If the objective is None, or a constant, we want to remember
1832
+ # that the objective function has been defined ( the user did not
1833
+ # forget it ). In some LP problems, you just want a feasible solution
1834
+ # and do not care about any function being optimal.
1835
+ cdef int i
1836
+
1837
+ if obj is None:
1838
+ f = {-1: 0}
1839
+ else:
1840
+ # See if it is a constant
1841
+ R = self.base_ring()
1842
+ try:
1843
+ f = {-1: R(obj)}
1844
+ except TypeError:
1845
+ # Should be a linear function
1846
+ f = obj.dict()
1847
+ d = f.pop(-1,self._backend.zero())
1848
+
1849
+ for i in range(self._backend.ncols()):
1850
+ values.append(f.get(i,self._backend.zero()))
1851
+ self._backend.set_objective(values, d)
1852
+
1853
+ def add_constraint(self, linear_function, max=None, min=None, name=None,
1854
+ return_indices=False):
1855
+ r"""
1856
+ Add a constraint to the ``MixedIntegerLinearProgram``.
1857
+
1858
+ INPUT:
1859
+
1860
+ - ``linear_function`` -- four different types of arguments are
1861
+ admissible:
1862
+
1863
+ - A linear function. In this case, one of the arguments
1864
+ ``min`` or ``max`` has to be specified.
1865
+
1866
+ - A linear constraint of the form ``A <= B``, ``A >= B``,
1867
+ ``A <= B <= C``, ``A >= B >= C`` or ``A == B``.
1868
+
1869
+ - A vector-valued linear function, see
1870
+ :mod:`~sage.numerical.linear_tensor`. In this case, one
1871
+ of the arguments ``min`` or ``max`` has to be specified.
1872
+
1873
+ - An (in)equality of vector-valued linear functions, that
1874
+ is, elements of the space of linear functions tensored
1875
+ with a vector space. See
1876
+ :mod:`~sage.numerical.linear_tensor_constraints` for
1877
+ details.
1878
+
1879
+ - ``max`` -- constant or ``None`` (default). An upper bound on
1880
+ the linear function. This must be a numerical value for
1881
+ scalar linear functions, or a vector for vector-valued
1882
+ linear functions. Not allowed if the ``linear_function``
1883
+ argument is a symbolic (in)-equality.
1884
+
1885
+ - ``min`` -- constant or ``None`` (default). A lower bound on
1886
+ the linear function. This must be a numerical value for
1887
+ scalar linear functions, or a vector for vector-valued
1888
+ linear functions. Not allowed if the ``linear_function``
1889
+ argument is a symbolic (in)-equality.
1890
+
1891
+ - ``name`` -- a name for the constraint
1892
+
1893
+ - ``return_indices`` -- boolean (default: ``False``),
1894
+ whether to return the indices of the added constraints
1895
+
1896
+ OUTPUT:
1897
+
1898
+ The row indices of the constraints added, if
1899
+ ``return_indices`` is true and the backend guarantees that
1900
+ removing them again yields the original MIP, ``None``
1901
+ otherwise.
1902
+
1903
+ To set a lower and/or upper bound on the variables use the methods
1904
+ ``set_min`` and/or ``set_max`` of ``MixedIntegerLinearProgram``.
1905
+
1906
+ EXAMPLES:
1907
+
1908
+ Consider the following linear program::
1909
+
1910
+ Maximize:
1911
+ x + 5 * y
1912
+ Constraints:
1913
+ x + 0.2 y <= 4
1914
+ 1.5 * x + 3 * y <= 4
1915
+ Variables:
1916
+ x is Real (min = 0, max = None)
1917
+ y is Real (min = 0, max = None)
1918
+
1919
+ It can be solved as follows::
1920
+
1921
+ sage: p = MixedIntegerLinearProgram(maximization=True, solver='GLPK')
1922
+ sage: x = p.new_variable(nonnegative=True)
1923
+ sage: p.set_objective(x[0] + 5*x[1])
1924
+ sage: p.add_constraint(x[0] + 0.2*x[1], max=4)
1925
+ sage: p.add_constraint(1.5*x[0] + 3*x[1], max=4)
1926
+ sage: p.solve() # rel tol 1e-15
1927
+ 6.666666666666666
1928
+
1929
+ There are two different ways to add the constraint
1930
+ ``x[5] + 3*x[7] <= x[6] + 3`` to a ``MixedIntegerLinearProgram``.
1931
+
1932
+ The first one consists in giving ``add_constraint`` this
1933
+ very expression::
1934
+
1935
+ sage: p.add_constraint(x[5] + 3*x[7] <= x[6] + 3)
1936
+
1937
+ The second (slightly more efficient) one is to use the
1938
+ arguments ``min`` or ``max``, which can only be numerical
1939
+ values::
1940
+
1941
+ sage: p.add_constraint(x[5] + 3*x[7] - x[6], max=3)
1942
+
1943
+ One can also define double-bounds or equality using symbols
1944
+ ``<=``, ``>=`` and ``==``::
1945
+
1946
+ sage: p.add_constraint(x[5] + 3*x[7] == x[6] + 3)
1947
+ sage: p.add_constraint(x[5] + 3*x[7] <= x[6] + 3 <= x[8] + 27)
1948
+
1949
+ Using this notation, the previous program can be written as::
1950
+
1951
+ sage: p = MixedIntegerLinearProgram(maximization=True, solver='GLPK')
1952
+ sage: x = p.new_variable(nonnegative=True)
1953
+ sage: p.set_objective(x[0] + 5*x[1])
1954
+ sage: p.add_constraint(x[0] + 0.2*x[1] <= 4)
1955
+ sage: p.add_constraint(1.5*x[0] + 3*x[1] <= 4)
1956
+ sage: p.solve() # rel tol 1e-15
1957
+ 6.666666666666666
1958
+
1959
+ The two constraints can also be combined into a single
1960
+ vector-valued constraint::
1961
+
1962
+ sage: p = MixedIntegerLinearProgram(maximization=True, solver='GLPK')
1963
+ sage: x = p.new_variable(nonnegative=True)
1964
+ sage: p.set_objective(x[0] + 5*x[1])
1965
+ sage: f_vec = vector([1, 1.5]) * x[0] + vector([0.2, 3]) * x[1]; f_vec
1966
+ (1.0, 1.5)*x_0 + (0.2, 3.0)*x_1
1967
+ sage: p.add_constraint(f_vec, max=vector([4, 4]))
1968
+ sage: p.solve() # rel tol 1e-15
1969
+ 6.666666666666666
1970
+
1971
+ Instead of specifying the maximum in the optional ``max``
1972
+ argument, we can also use (in)equality notation for
1973
+ vector-valued linear functions::
1974
+
1975
+ sage: f_vec <= 4 # constant rhs becomes vector
1976
+ (1.0, 1.5)*x_0 + (0.2, 3.0)*x_1 <= (4.0, 4.0)
1977
+ sage: p = MixedIntegerLinearProgram(maximization=True, solver='GLPK')
1978
+ sage: x = p.new_variable(nonnegative=True)
1979
+ sage: p.set_objective(x[0] + 5*x[1])
1980
+ sage: p.add_constraint(f_vec <= 4)
1981
+ sage: p.solve() # rel tol 1e-15
1982
+ 6.666666666666666
1983
+
1984
+ Finally, one can use the matrix * :class:`MIPVariable`
1985
+ notation to write vector-valued linear functions::
1986
+
1987
+ sage: m = matrix([[1.0, 0.2], [1.5, 3.0]]); m
1988
+ [ 1.00000000000000 0.200000000000000]
1989
+ [ 1.50000000000000 3.00000000000000]
1990
+ sage: p = MixedIntegerLinearProgram(maximization=True, solver='GLPK')
1991
+ sage: x = p.new_variable(nonnegative=True)
1992
+ sage: p.set_objective(x[0] + 5*x[1])
1993
+ sage: p.add_constraint(m * x <= 4)
1994
+ sage: p.solve() # rel tol 1e-15
1995
+ 6.666666666666666
1996
+
1997
+ TESTS:
1998
+
1999
+ Complex constraints::
2000
+
2001
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
2002
+ sage: b = p.new_variable(nonnegative=True)
2003
+ sage: p.add_constraint(b[8] - b[15] <= 3*b[8] + 9)
2004
+ sage: p.show()
2005
+ Maximization:
2006
+ <BLANKLINE>
2007
+ Constraints:
2008
+ -2.0 x_0 - x_1 <= 9.0
2009
+ Variables:
2010
+ x_0 is a continuous variable (min=0.0, max=+oo)
2011
+ x_1 is a continuous variable (min=0.0, max=+oo)
2012
+
2013
+ Trivially true empty constraint:
2014
+
2015
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
2016
+ sage: p.add_constraint(sum([]), max=2)
2017
+ sage: p.solve()
2018
+ 0.0
2019
+
2020
+ Infeasible empty constraint::
2021
+
2022
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
2023
+ sage: p.add_constraint(sum([]), min=2)
2024
+ sage: p.solve()
2025
+ Traceback (most recent call last):
2026
+ ...
2027
+ MIPSolverException: GLPK: Problem has no feasible solution
2028
+
2029
+ Min/Max are numerical ::
2030
+
2031
+ sage: v = p.new_variable(nonnegative=True)
2032
+ sage: p.add_constraint(v[3] + v[5], min = v[6])
2033
+ Traceback (most recent call last):
2034
+ ...
2035
+ ValueError: min and max arguments are required to be constants
2036
+ sage: p.add_constraint(v[3] + v[5], max = v[6])
2037
+ Traceback (most recent call last):
2038
+ ...
2039
+ ValueError: min and max arguments are required to be constants
2040
+
2041
+ Do not add redundant elements (notice only one copy of each constraint is added)::
2042
+
2043
+ sage: lp = MixedIntegerLinearProgram(solver='GLPK', check_redundant=True)
2044
+ sage: for each in range(10):
2045
+ ....: lp.add_constraint(lp[0]-lp[1], min=1)
2046
+ sage: lp.show()
2047
+ Maximization:
2048
+ <BLANKLINE>
2049
+ Constraints:
2050
+ 1.0 <= x_0 - x_1
2051
+ Variables:
2052
+ x_0 is a continuous variable (min=-oo, max=+oo)
2053
+ x_1 is a continuous variable (min=-oo, max=+oo)
2054
+
2055
+ We check for constant multiples of constraints as well::
2056
+
2057
+ sage: for each in range(10):
2058
+ ....: lp.add_constraint(2*lp[0] - 2*lp[1], min=2)
2059
+ sage: lp.show()
2060
+ Maximization:
2061
+ <BLANKLINE>
2062
+ Constraints:
2063
+ 1.0 <= x_0 - x_1
2064
+ Variables:
2065
+ x_0 is a continuous variable (min=-oo, max=+oo)
2066
+ x_1 is a continuous variable (min=-oo, max=+oo)
2067
+
2068
+ But if the constant multiple is negative, we should add it anyway (once)::
2069
+
2070
+ sage: for each in range(10):
2071
+ ....: lp.add_constraint(-2*lp[0] + 2*lp[1], min=-2)
2072
+ sage: lp.show()
2073
+ Maximization:
2074
+ <BLANKLINE>
2075
+ Constraints:
2076
+ 1.0 <= x_0 - x_1
2077
+ -2.0 <= -2.0 x_0 + 2.0 x_1
2078
+ Variables:
2079
+ x_0 is a continuous variable (min=-oo, max=+oo)
2080
+ x_1 is a continuous variable (min=-oo, max=+oo)
2081
+
2082
+ Catch ``True`` / ``False`` as INPUT (:issue:`13646`)::
2083
+
2084
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
2085
+ sage: x = p.new_variable(nonnegative=True)
2086
+ sage: p.add_constraint(True)
2087
+ Traceback (most recent call last):
2088
+ ...
2089
+ ValueError: argument must be a linear function or constraint, got True
2090
+
2091
+ Check that adding and removing constraints works::
2092
+
2093
+ sage: p = MixedIntegerLinearProgram(check_redundant=True)
2094
+ sage: x = p.new_variable(nonnegative=True)
2095
+ sage: p.add_constraint(x[0] + x[1] <= 3)
2096
+ sage: p.set_objective(x[0] + 2*x[1])
2097
+ sage: p.solve()
2098
+ 6.0
2099
+
2100
+ We add (non-trivially) redundant constraints::
2101
+
2102
+ sage: c1 = p.add_constraint(0 <= x[0] <= 3, return_indices=True); c1
2103
+ [1, 2]
2104
+ sage: p.solve()
2105
+ 6.0
2106
+
2107
+ We add a non-redundant constraint::
2108
+
2109
+ sage: c2 = p.add_constraint(1 <= x[1] <= 2, return_indices=True); c2
2110
+ [3, 4]
2111
+ sage: p.solve()
2112
+ 5.0
2113
+
2114
+ We remove the redundant constraints `1` and `2`, keep in mind
2115
+ that indices change when removing constraints::
2116
+
2117
+ sage: p.remove_constraint(1)
2118
+ sage: p.remove_constraint(1)
2119
+ sage: p.solve()
2120
+ 5.0
2121
+
2122
+ We remove another constraint::
2123
+
2124
+ sage: p.remove_constraint(2)
2125
+ sage: p.solve()
2126
+ 6.0
2127
+ """
2128
+ from sage.numerical.linear_functions import LinearFunction, LinearConstraint
2129
+ from sage.numerical.linear_tensor import LinearTensor
2130
+ from sage.numerical.linear_tensor_constraints import LinearTensorConstraint
2131
+ if isinstance(linear_function, (LinearFunction, LinearTensor)):
2132
+ # Find the parent for the coefficients
2133
+ if isinstance(linear_function, LinearFunction):
2134
+ M = linear_function.parent().base_ring()
2135
+ elif isinstance(linear_function, LinearTensor):
2136
+ if not linear_function.parent().is_vector_space():
2137
+ raise ValueError('the linear function must be vector-valued')
2138
+ M = linear_function.parent().free_module()
2139
+ else:
2140
+ assert False, 'unreachable'
2141
+ # Normalize min/max
2142
+ try:
2143
+ min = None if min is None else M(min)
2144
+ max = None if max is None else M(max)
2145
+ except (ValueError, TypeError):
2146
+ raise ValueError("min and max arguments are required to be constants")
2147
+ if min is None and max is None:
2148
+ raise ValueError('at least one of "max" or "min" must be set')
2149
+ # Shift constant away
2150
+ constraint = copy(linear_function.dict())
2151
+ try:
2152
+ constant_coefficient = constraint.pop(-1)
2153
+ max = (max - constant_coefficient) if max is not None else None
2154
+ min = (min - constant_coefficient) if min is not None else None
2155
+ except KeyError:
2156
+ pass
2157
+ # Send to backend
2158
+ if isinstance(linear_function, LinearFunction):
2159
+ if self._check_redundant and self._is_redundant_constraint(constraint, min, max):
2160
+ if return_indices:
2161
+ return []
2162
+ return
2163
+ nrows_before = self._backend.nrows()
2164
+ self._backend.add_linear_constraint(constraint.items(), min, max, name)
2165
+ elif isinstance(linear_function, LinearTensor):
2166
+ nrows_before = self._backend.nrows()
2167
+ self._backend.add_linear_constraint_vector(M.degree(), constraint.items(), min, max, name)
2168
+ else:
2169
+ assert False, 'unreachable'
2170
+ if return_indices:
2171
+ return list(range(nrows_before, self._backend.nrows()))
2172
+ return
2173
+ elif isinstance(linear_function, LinearConstraint):
2174
+ if not (min is None and max is None):
2175
+ raise ValueError('min and max must not be specified for (in)equalities')
2176
+ relation = linear_function
2177
+ if return_indices:
2178
+ row_indices = []
2179
+ else:
2180
+ row_indices = None
2181
+ for lhs, rhs in relation.equations():
2182
+ new_indices = self.add_constraint(lhs-rhs, min=0, max=0,
2183
+ name=name,
2184
+ return_indices=return_indices)
2185
+ if new_indices is not None:
2186
+ row_indices.extend(new_indices)
2187
+ for lhs, rhs in relation.inequalities():
2188
+ new_indices = self.add_constraint(lhs-rhs, max=0,
2189
+ name=name,
2190
+ return_indices=return_indices)
2191
+ if new_indices is not None:
2192
+ row_indices.extend(new_indices)
2193
+ return row_indices
2194
+ elif isinstance(linear_function, LinearTensorConstraint):
2195
+ if not (min is None and max is None):
2196
+ raise ValueError('min and max must not be specified for (in)equalities')
2197
+ relation = linear_function
2198
+ M = relation.parent().linear_tensors().free_module()
2199
+ return self.add_constraint(relation.lhs() - relation.rhs(),
2200
+ min=M(0) if relation.is_equation() else None,
2201
+ max=M(0),
2202
+ name=name, return_indices=return_indices)
2203
+ elif isinstance(linear_function, bool):
2204
+ raise ValueError('argument must be a linear function or constraint, got ' + str(linear_function))
2205
+ else:
2206
+ try:
2207
+ linear_function = self.linear_functions_parent()(linear_function)
2208
+ except (TypeError, ValueError):
2209
+ raise ValueError('argument must be a linear function or constraint, got ' + str(linear_function))
2210
+ return self.add_constraint(linear_function, max=max, min=min,
2211
+ name=name, return_indices=return_indices)
2212
+
2213
+ def _is_redundant_constraint(self, constraint, min_bound, max_bound):
2214
+ """
2215
+ Check whether the (scalar) constraint is redundant.
2216
+
2217
+ INPUT:
2218
+
2219
+ - ``constraint`` -- dictionary of a nonzero linear function
2220
+ without constant term
2221
+
2222
+ - ``min_bound``, ``max_bound`` -- base ring elements or
2223
+ ``None``; the lower and upper bound
2224
+
2225
+ OUTPUT: boolean; whether the (normalized) constraint has already been added
2226
+
2227
+ EXAMPLES::
2228
+
2229
+ sage: mip.<x> = MixedIntegerLinearProgram(check_redundant=True, solver='GLPK')
2230
+ sage: mip.add_constraint(x[0], min=1)
2231
+ sage: mip._is_redundant_constraint((x[0]).dict(), 1, None)
2232
+ True
2233
+ sage: mip._is_redundant_constraint((-2*x[0]).dict(), None, -2)
2234
+ True
2235
+ sage: mip._is_redundant_constraint((x[1]).dict(), 1, None)
2236
+ False
2237
+ """
2238
+ assert self._constraints is not None, 'must be initialized with check_redundant=True'
2239
+ assert -1 not in constraint, 'no constant term allowed'
2240
+ i0 = min([i for i, c in constraint.items() if c != 0])
2241
+ rescale = constraint[i0]
2242
+ constraint = tuple((i, c/rescale) for i, c in constraint.items())
2243
+ if rescale > 0:
2244
+ min_scaled = min_bound/rescale if min_bound is not None else None
2245
+ max_scaled = max_bound/rescale if max_bound is not None else None
2246
+ else:
2247
+ min_scaled = max_bound/rescale if max_bound is not None else None
2248
+ max_scaled = min_bound/rescale if min_bound is not None else None
2249
+ key = (constraint, min_scaled, max_scaled)
2250
+ if key in self._constraints:
2251
+ return True
2252
+ else:
2253
+ self._constraints.append(key)
2254
+ return False
2255
+
2256
+ def remove_constraint(self, int i):
2257
+ r"""
2258
+ Removes a constraint from ``self``.
2259
+
2260
+ INPUT:
2261
+
2262
+ - ``i`` -- index of the constraint to remove
2263
+
2264
+ EXAMPLES::
2265
+
2266
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
2267
+ sage: x, y = p[0], p[1]
2268
+ sage: p.add_constraint(x + y, max=10)
2269
+ sage: p.add_constraint(x - y, max=0)
2270
+ sage: p.add_constraint(x, max=4)
2271
+ sage: p.show()
2272
+ Maximization:
2273
+ <BLANKLINE>
2274
+ Constraints:
2275
+ x_0 + x_1 <= 10.0
2276
+ x_0 - x_1 <= 0.0
2277
+ x_0 <= 4.0
2278
+ ...
2279
+ sage: p.remove_constraint(1)
2280
+ sage: p.show()
2281
+ Maximization:
2282
+ <BLANKLINE>
2283
+ Constraints:
2284
+ x_0 + x_1 <= 10.0
2285
+ x_0 <= 4.0
2286
+ ...
2287
+ sage: p.number_of_constraints()
2288
+ 2
2289
+ """
2290
+ if self._check_redundant:
2291
+ self._constraints.pop(i)
2292
+ self._backend.remove_constraint(i)
2293
+
2294
+ def remove_constraints(self, constraints):
2295
+ r"""
2296
+ Remove several constraints.
2297
+
2298
+ INPUT:
2299
+
2300
+ - ``constraints`` -- an iterable containing the indices of the rows to remove
2301
+
2302
+ EXAMPLES::
2303
+
2304
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
2305
+ sage: x, y = p[0], p[1]
2306
+ sage: p.add_constraint(x + y, max=10)
2307
+ sage: p.add_constraint(x - y, max=0)
2308
+ sage: p.add_constraint(x, max=4)
2309
+ sage: p.show()
2310
+ Maximization:
2311
+ <BLANKLINE>
2312
+ Constraints:
2313
+ x_0 + x_1 <= 10.0
2314
+ x_0 - x_1 <= 0.0
2315
+ x_0 <= 4.0
2316
+ ...
2317
+ sage: p.remove_constraints([0, 1])
2318
+ sage: p.show()
2319
+ Maximization:
2320
+ <BLANKLINE>
2321
+ Constraints:
2322
+ x_0 <= 4.0
2323
+ ...
2324
+ sage: p.number_of_constraints()
2325
+ 1
2326
+
2327
+ When checking for redundant constraints, make sure you remove only
2328
+ the constraints that were actually added. Problems could arise if
2329
+ you have a function that builds lps non-interactively, but it fails
2330
+ to check whether adding a constraint actually increases the number of
2331
+ constraints. The function might later try to remove constraints that
2332
+ are not actually there::
2333
+
2334
+ sage: p = MixedIntegerLinearProgram(check_redundant=True, solver='GLPK')
2335
+ sage: x, y = p[0], p[1]
2336
+ sage: p.add_constraint(x + y, max=10)
2337
+ sage: for each in range(10):
2338
+ ....: p.add_constraint(x - y, max=10)
2339
+ sage: p.add_constraint(x, max=4)
2340
+ sage: p.number_of_constraints()
2341
+ 3
2342
+ sage: p.remove_constraints(range(1, 9))
2343
+ Traceback (most recent call last):
2344
+ ...
2345
+ IndexError: pop index out of range
2346
+ sage: p.remove_constraint(1)
2347
+ sage: p.number_of_constraints()
2348
+ 2
2349
+
2350
+ We should now be able to add the old constraint back in::
2351
+
2352
+ sage: for each in range(10):
2353
+ ....: p.add_constraint(x - y, max=10)
2354
+ sage: p.number_of_constraints()
2355
+ 3
2356
+
2357
+ TESTS:
2358
+
2359
+ Removing no constraints does not make Sage crash, see
2360
+ :issue:`34881`::
2361
+
2362
+ sage: MixedIntegerLinearProgram().remove_constraints([])
2363
+ """
2364
+ if self._check_redundant:
2365
+ for i in sorted(constraints, reverse=True):
2366
+ self._constraints.pop(i)
2367
+ if constraints:
2368
+ self._backend.remove_constraints(constraints)
2369
+
2370
+ def set_binary(self, ee):
2371
+ r"""
2372
+ Set a variable or a ``MIPVariable`` as binary.
2373
+
2374
+ INPUT:
2375
+
2376
+ - ``ee`` -- an instance of ``MIPVariable`` or one of
2377
+ its elements
2378
+
2379
+ EXAMPLES::
2380
+
2381
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
2382
+ sage: x = p.new_variable(nonnegative=True)
2383
+
2384
+ With the following instruction, all the variables
2385
+ from x will be binary::
2386
+
2387
+ sage: p.set_binary(x)
2388
+ sage: p.set_objective(x[0] + x[1])
2389
+ sage: p.add_constraint(-3*x[0] + 2*x[1], max=2)
2390
+
2391
+ It is still possible, though, to set one of these
2392
+ variables as integer while keeping the others as they are::
2393
+
2394
+ sage: p.set_integer(x[3])
2395
+ """
2396
+ cdef MIPVariable e
2397
+ e = <MIPVariable> ee
2398
+
2399
+ if isinstance(e, MIPVariable):
2400
+ e._vtype = self.__BINARY
2401
+ for v in e.values():
2402
+ self._backend.set_variable_type(self._variables[v],self.__BINARY)
2403
+ elif e in self._variables:
2404
+ self._backend.set_variable_type(self._variables[e],self.__BINARY)
2405
+ else:
2406
+ raise ValueError("e must be an instance of MIPVariable or one of its elements.")
2407
+
2408
+ def is_binary(self, e):
2409
+ r"""
2410
+ Test whether the variable ``e`` is binary. Variables are real by
2411
+ default.
2412
+
2413
+ INPUT:
2414
+
2415
+ - ``e`` -- a variable (not a ``MIPVariable``, but one of its elements)
2416
+
2417
+ OUTPUT: ``True`` if the variable ``e`` is binary; ``False`` otherwise
2418
+
2419
+ EXAMPLES::
2420
+
2421
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
2422
+ sage: v = p.new_variable(nonnegative=True)
2423
+ sage: p.set_objective(v[1])
2424
+ sage: p.is_binary(v[1])
2425
+ False
2426
+ sage: p.set_binary(v[1])
2427
+ sage: p.is_binary(v[1])
2428
+ True
2429
+ """
2430
+ return self._backend.is_variable_binary(self._variables[e])
2431
+
2432
+ def set_integer(self, ee):
2433
+ r"""
2434
+ Set a variable or a ``MIPVariable`` as integer.
2435
+
2436
+ INPUT:
2437
+
2438
+ - ``ee`` -- an instance of ``MIPVariable`` or one of
2439
+ its elements
2440
+
2441
+ EXAMPLES::
2442
+
2443
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
2444
+ sage: x = p.new_variable(nonnegative=True)
2445
+
2446
+ With the following instruction, all the variables
2447
+ from x will be integers::
2448
+
2449
+ sage: p.set_integer(x)
2450
+ sage: p.set_objective(x[0] + x[1])
2451
+ sage: p.add_constraint(-3*x[0] + 2*x[1], max=2)
2452
+
2453
+ It is still possible, though, to set one of these
2454
+ variables as binary while keeping the others as they are::
2455
+
2456
+ sage: p.set_binary(x[3])
2457
+ """
2458
+ cdef MIPVariable e
2459
+ e = <MIPVariable> ee
2460
+
2461
+ if isinstance(e, MIPVariable):
2462
+ e._vtype = self.__INTEGER
2463
+ for v in e.values():
2464
+ self._backend.set_variable_type(self._variables[v],self.__INTEGER)
2465
+ elif e in self._variables:
2466
+ self._backend.set_variable_type(self._variables[e],self.__INTEGER)
2467
+ else:
2468
+ raise ValueError("e must be an instance of MIPVariable or one of its elements.")
2469
+
2470
+ def is_integer(self, e):
2471
+ r"""
2472
+ Test whether the variable is an integer. Variables are real by
2473
+ default.
2474
+
2475
+ INPUT:
2476
+
2477
+ - ``e`` -- a variable (not a ``MIPVariable``, but one of its elements)
2478
+
2479
+ OUTPUT: ``True`` if the variable ``e`` is an integer; ``False`` otherwise
2480
+
2481
+ EXAMPLES::
2482
+
2483
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
2484
+ sage: v = p.new_variable(nonnegative=True)
2485
+ sage: p.set_objective(v[1])
2486
+ sage: p.is_integer(v[1])
2487
+ False
2488
+ sage: p.set_integer(v[1])
2489
+ sage: p.is_integer(v[1])
2490
+ True
2491
+ """
2492
+ return self._backend.is_variable_integer(self._variables[e])
2493
+
2494
+ def set_real(self, ee):
2495
+ r"""
2496
+ Set a variable or a ``MIPVariable`` as real.
2497
+
2498
+ INPUT:
2499
+
2500
+ - ``ee`` -- an instance of ``MIPVariable`` or one of
2501
+ its elements
2502
+
2503
+ EXAMPLES::
2504
+
2505
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
2506
+ sage: x = p.new_variable(nonnegative=True)
2507
+
2508
+ With the following instruction, all the variables
2509
+ from x will be real::
2510
+
2511
+ sage: p.set_real(x)
2512
+ sage: p.set_objective(x[0] + x[1])
2513
+ sage: p.add_constraint(-3*x[0] + 2*x[1], max=2)
2514
+
2515
+ It is still possible, though, to set one of these
2516
+ variables as binary while keeping the others as they are::
2517
+
2518
+ sage: p.set_binary(x[3])
2519
+ """
2520
+ cdef MIPVariable e
2521
+ e = <MIPVariable> ee
2522
+
2523
+ if isinstance(e, MIPVariable):
2524
+ e._vtype = self.__REAL
2525
+ for v in e.values():
2526
+ self._backend.set_variable_type(self._variables[v],self.__REAL)
2527
+ self._backend.variable_lower_bound(self._variables[v], 0)
2528
+ elif e in self._variables:
2529
+ self._backend.set_variable_type(self._variables[e],self.__REAL)
2530
+ self._backend.variable_lower_bound(self._variables[e], 0)
2531
+ else:
2532
+ raise ValueError("e must be an instance of MIPVariable or one of its elements.")
2533
+
2534
+ def is_real(self, e):
2535
+ r"""
2536
+ Test whether the variable is real.
2537
+
2538
+ INPUT:
2539
+
2540
+ - ``e`` -- a variable (not a ``MIPVariable``, but one of its elements)
2541
+
2542
+ OUTPUT: ``True`` if the variable is real; ``False`` otherwise
2543
+
2544
+ EXAMPLES::
2545
+
2546
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
2547
+ sage: v = p.new_variable(nonnegative=True)
2548
+ sage: p.set_objective(v[1])
2549
+ sage: p.is_real(v[1])
2550
+ True
2551
+ sage: p.set_binary(v[1])
2552
+ sage: p.is_real(v[1])
2553
+ False
2554
+ sage: p.set_real(v[1])
2555
+ sage: p.is_real(v[1])
2556
+ True
2557
+ """
2558
+ return self._backend.is_variable_continuous(self._variables[e])
2559
+
2560
+ def solve(self, log=None, objective_only=False):
2561
+ r"""
2562
+ Solve the ``MixedIntegerLinearProgram``.
2563
+
2564
+ INPUT:
2565
+
2566
+ - ``log`` -- integer (default: ``None``); the verbosity level. Indicates
2567
+ whether progress should be printed during computation. The solver is
2568
+ initialized to report no progress.
2569
+
2570
+ - ``objective_only`` -- boolean:
2571
+
2572
+ - When set to ``True``, only the objective function is returned.
2573
+ - When set to ``False`` (default), the optimal numerical values
2574
+ are stored (takes computational time).
2575
+
2576
+ OUTPUT: the optimal value taken by the objective function
2577
+
2578
+ .. WARNING::
2579
+
2580
+ By default, no additional assumption is made on the domain of an LP
2581
+ variable. See :meth:`set_min` and :meth:`set_max` to change it.
2582
+
2583
+ EXAMPLES:
2584
+
2585
+ Consider the following linear program::
2586
+
2587
+ Maximize:
2588
+ x + 5 * y
2589
+ Constraints:
2590
+ x + 0.2 y <= 4
2591
+ 1.5 * x + 3 * y <= 4
2592
+ Variables:
2593
+ x is Real (min = 0, max = None)
2594
+ y is Real (min = 0, max = None)
2595
+
2596
+ This linear program can be solved as follows::
2597
+
2598
+ sage: p = MixedIntegerLinearProgram(maximization=True, solver='GLPK')
2599
+ sage: x = p.new_variable(nonnegative=True)
2600
+ sage: p.set_objective(x[1] + 5*x[2])
2601
+ sage: p.add_constraint(x[1] + 0.2*x[2], max=4)
2602
+ sage: p.add_constraint(1.5*x[1] + 3*x[2], max=4)
2603
+ sage: round(p.solve(),6)
2604
+ 6.666667
2605
+ sage: x = p.get_values(x)
2606
+ sage: round(x[1],6) # abs tol 1e-15
2607
+ 0.0
2608
+ sage: round(x[2],6)
2609
+ 1.333333
2610
+
2611
+ Computation of a maximum stable set in Petersen's graph::
2612
+
2613
+ sage: # needs sage.graphs
2614
+ sage: g = graphs.PetersenGraph()
2615
+ sage: p = MixedIntegerLinearProgram(maximization=True, solver='GLPK')
2616
+ sage: b = p.new_variable(nonnegative=True)
2617
+ sage: p.set_objective(sum([b[v] for v in g]))
2618
+ sage: for (u,v) in g.edges(sort=False, labels=None):
2619
+ ....: p.add_constraint(b[u] + b[v], max=1)
2620
+ sage: p.set_binary(b)
2621
+ sage: p.solve(objective_only=True)
2622
+ 4.0
2623
+
2624
+ Constraints in the objective function are respected::
2625
+
2626
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
2627
+ sage: x, y = p[0], p[1]
2628
+ sage: p.add_constraint(2*x + 3*y, max=6)
2629
+ sage: p.add_constraint(3*x + 2*y, max=6)
2630
+ sage: p.set_objective(x + y + 7)
2631
+ sage: p.set_integer(x); p.set_integer(y)
2632
+ sage: p.solve()
2633
+ 9.0
2634
+ """
2635
+ if log is not None:
2636
+ self._backend.set_verbosity(log)
2637
+ self._backend.solve()
2638
+ return self._backend.get_objective_value()
2639
+
2640
+ def set_min(self, v, min):
2641
+ r"""
2642
+ Set the minimum value of a variable.
2643
+
2644
+ INPUT:
2645
+
2646
+ - ``v`` -- a variable
2647
+
2648
+ - ``min`` -- the minimum value the variable can take; when
2649
+ ``min=None``, the variable has no lower bound
2650
+
2651
+ .. SEEALSO::
2652
+
2653
+ - :meth:`get_min` -- get the minimum value of a variable
2654
+
2655
+ EXAMPLES::
2656
+
2657
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
2658
+ sage: v = p.new_variable(nonnegative=True)
2659
+ sage: p.set_objective(v[1])
2660
+ sage: p.get_min(v[1])
2661
+ 0.0
2662
+ sage: p.set_min(v[1],6)
2663
+ sage: p.get_min(v[1])
2664
+ 6.0
2665
+ sage: p.set_min(v[1], None)
2666
+ sage: p.get_min(v[1])
2667
+
2668
+ With a :class:`MIPVariable` as an argument::
2669
+
2670
+ sage: vv = p.new_variable(real=True)
2671
+ sage: p.get_min(vv)
2672
+ sage: p.get_min(vv[0])
2673
+ sage: p.set_min(vv,5)
2674
+ sage: p.get_min(vv[0])
2675
+ 5.0
2676
+ sage: p.get_min(vv[9])
2677
+ 5.0
2678
+ """
2679
+ try:
2680
+ v.set_min(min)
2681
+ except AttributeError:
2682
+ self._backend.variable_lower_bound(self._variables[v], min)
2683
+
2684
+ def set_max(self, v, max):
2685
+ r"""
2686
+ Set the maximum value of a variable.
2687
+
2688
+ INPUT:
2689
+
2690
+ - ``v`` -- a variable
2691
+
2692
+ - ``max`` -- the maximum value the variable can take; when
2693
+ ``max=None``, the variable has no upper bound
2694
+
2695
+ EXAMPLES::
2696
+
2697
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
2698
+ sage: v = p.new_variable(nonnegative=True)
2699
+ sage: p.set_objective(v[1])
2700
+ sage: p.get_max(v[1])
2701
+ sage: p.set_max(v[1],6)
2702
+ sage: p.get_max(v[1])
2703
+ 6.0
2704
+
2705
+ With a :class:`MIPVariable` as an argument::
2706
+
2707
+ sage: vv = p.new_variable(real=True)
2708
+ sage: p.get_max(vv)
2709
+ sage: p.get_max(vv[0])
2710
+ sage: p.set_max(vv,5)
2711
+ sage: p.get_max(vv[0])
2712
+ 5.0
2713
+ sage: p.get_max(vv[9])
2714
+ 5.0
2715
+ """
2716
+ try:
2717
+ v.set_max(max)
2718
+ except AttributeError:
2719
+ self._backend.variable_upper_bound(self._variables[v], max)
2720
+
2721
+ def get_min(self, v):
2722
+ r"""
2723
+ Return the minimum value of a variable.
2724
+
2725
+ INPUT:
2726
+
2727
+ - ``v`` -- a variable
2728
+
2729
+ OUTPUT:
2730
+
2731
+ Minimum value of the variable, or ``None`` if the variable has no lower
2732
+ bound.
2733
+
2734
+ EXAMPLES::
2735
+
2736
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
2737
+ sage: v = p.new_variable(nonnegative=True)
2738
+ sage: p.set_objective(v[1])
2739
+ sage: p.get_min(v[1])
2740
+ 0.0
2741
+ sage: p.set_min(v[1],6)
2742
+ sage: p.get_min(v[1])
2743
+ 6.0
2744
+ sage: p.set_min(v[1], None)
2745
+ sage: p.get_min(v[1])
2746
+ """
2747
+ try:
2748
+ return (<MIPVariable?>v)._lower_bound
2749
+ except TypeError:
2750
+ return self._backend.variable_lower_bound(self._variables[v])
2751
+
2752
+ def get_max(self, v):
2753
+ r"""
2754
+ Return the maximum value of a variable.
2755
+
2756
+ INPUT:
2757
+
2758
+ - ``v`` -- a variable
2759
+
2760
+ OUTPUT:
2761
+
2762
+ Maximum value of the variable, or ``None`` if the variable has no upper
2763
+ bound.
2764
+
2765
+ EXAMPLES::
2766
+
2767
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
2768
+ sage: v = p.new_variable(nonnegative=True)
2769
+ sage: p.set_objective(v[1])
2770
+ sage: p.get_max(v[1])
2771
+ sage: p.set_max(v[1],6)
2772
+ sage: p.get_max(v[1])
2773
+ 6.0
2774
+ """
2775
+ try:
2776
+ return (<MIPVariable?>v)._upper_bound
2777
+ except TypeError:
2778
+ return self._backend.variable_upper_bound(self._variables[v])
2779
+
2780
+ def solver_parameter(self, name, value=None):
2781
+ """
2782
+ Return or define a solver parameter.
2783
+
2784
+ The solver parameters are by essence solver-specific, which means their
2785
+ meaning heavily depends on the solver used.
2786
+
2787
+ (If you do not know which solver you are using, then you use GLPK).
2788
+
2789
+ Aliases:
2790
+
2791
+ Very common parameters have aliases making them solver-independent. For
2792
+ example, the following::
2793
+
2794
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
2795
+ sage: p.solver_parameter("timelimit", 60)
2796
+
2797
+ Sets the solver to stop its computations after 60 seconds, and works
2798
+ with GLPK, CPLEX , SCIP, and Gurobi.
2799
+
2800
+ - ``'timelimit'`` -- defines the maximum time spent on a
2801
+ computation (measured in seconds)
2802
+
2803
+ Another example is the ``'logfile'`` parameter, which is used to specify
2804
+ the file in which computation logs are recorded. By default, the logs
2805
+ are not recorded, and we can disable this feature providing an empty
2806
+ filename. This is currently working with CPLEX and Gurobi::
2807
+
2808
+ sage: # optional - cplex
2809
+ sage: p = MixedIntegerLinearProgram(solver='CPLEX')
2810
+ sage: p.solver_parameter("logfile")
2811
+ ''
2812
+ sage: p.solver_parameter("logfile", "/dev/null")
2813
+ sage: p.solver_parameter("logfile")
2814
+ '/dev/null'
2815
+ sage: p.solver_parameter("logfile", '')
2816
+ sage: p.solver_parameter("logfile")
2817
+ ''
2818
+
2819
+ Solver-specific parameters:
2820
+
2821
+ - GLPK : We have implemented very close to comprehensive coverage of
2822
+ the GLPK solver parameters for the simplex and integer
2823
+ optimization methods. For details, see the documentation of
2824
+ :meth:`GLPKBackend.solver_parameter
2825
+ <sage.numerical.backends.glpk_backend.GLPKBackend.solver_parameter>`.
2826
+
2827
+ - CPLEX's parameters are identified by a string. Their
2828
+ list is available `on ILOG's website
2829
+ <http://publib.boulder.ibm.com/infocenter/odmeinfo/v3r4/index.jsp?topic=/ilog.odms.ide.odme.help/Content/Optimization/Documentation/ODME/_pubskel/ODME_pubskels/startall_ODME34_Eclipse1590.html>`_.
2830
+
2831
+ The command ::
2832
+
2833
+ sage: p = MixedIntegerLinearProgram(solver='CPLEX') # optional - CPLEX
2834
+ sage: p.solver_parameter("CPX_PARAM_TILIM", 60) # optional - CPLEX
2835
+
2836
+ works as intended.
2837
+
2838
+ - Gurobi's parameters should all be available through this
2839
+ method. Their list is available on Gurobi's website
2840
+ `<http://www.gurobi.com/documentation/5.5/reference-manual/node798>`_.
2841
+
2842
+ SCIP's parameter can be found here:
2843
+ `<http://scip.zib.de/doc-5.0.1/html/PARAMETERS.php>`_.
2844
+
2845
+ INPUT:
2846
+
2847
+ - ``name`` -- string; the parameter
2848
+
2849
+ - ``value`` -- the parameter's value if it is to be defined,
2850
+ or ``None`` (default) to obtain its current value
2851
+
2852
+ EXAMPLES::
2853
+
2854
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
2855
+ sage: p.solver_parameter("timelimit", 60)
2856
+ sage: p.solver_parameter("timelimit")
2857
+ 60.0
2858
+ """
2859
+ if value is None:
2860
+ return self._backend.solver_parameter(name)
2861
+ else:
2862
+ self._backend.solver_parameter(name, value)
2863
+
2864
+ cpdef sum(self, L):
2865
+ r"""
2866
+ Efficiently compute the sum of a sequence of
2867
+ :class:`~sage.numerical.linear_functions.LinearFunction` elements
2868
+
2869
+ INPUT:
2870
+
2871
+ - ``mip`` -- the :class:`MixedIntegerLinearProgram` parent
2872
+
2873
+ - ``L`` -- list of
2874
+ :class:`~sage.numerical.linear_functions.LinearFunction` instances
2875
+
2876
+ .. NOTE::
2877
+
2878
+ The use of the regular ``sum`` function is not recommended
2879
+ as it is much less efficient than this one
2880
+
2881
+ EXAMPLES::
2882
+
2883
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
2884
+ sage: v = p.new_variable(nonnegative=True)
2885
+
2886
+ The following command::
2887
+
2888
+ sage: s = p.sum(v[i] for i in range(90))
2889
+
2890
+ is much more efficient than::
2891
+
2892
+ sage: s = sum(v[i] for i in range(90))
2893
+ """
2894
+ d = {}
2895
+ for v in L:
2896
+ for id, coeff in v.iteritems():
2897
+ d[id] = coeff + d.get(id, 0)
2898
+ return self.linear_functions_parent()(d)
2899
+
2900
+ def get_backend(self):
2901
+ r"""
2902
+ Return the backend instance used.
2903
+
2904
+ This might be useful when access to additional functions provided by
2905
+ the backend is needed.
2906
+
2907
+ EXAMPLES:
2908
+
2909
+ This example uses the simplex algorithm and prints information::
2910
+
2911
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
2912
+ sage: x, y = p[0], p[1]
2913
+ sage: p.add_constraint(2*x + 3*y, max=6)
2914
+ sage: p.add_constraint(3*x + 2*y, max=6)
2915
+ sage: p.set_objective(x + y + 7)
2916
+ sage: b = p.get_backend()
2917
+ sage: b.solver_parameter("simplex_or_intopt", "simplex_only")
2918
+ sage: b.solver_parameter("verbosity_simplex", "GLP_MSG_ALL")
2919
+ sage: ans = p.solve()
2920
+ GLPK Simplex Optimizer...
2921
+ 2 rows, 2 columns, 4 non-zeros
2922
+ * 0: obj = 7.000000000e+00 inf = 0.000e+00 (2)
2923
+ * 2: obj = 9.400000000e+00 inf = 0.000e+00 (0)
2924
+ OPTIMAL LP SOLUTION FOUND
2925
+ sage: ans # rel tol 1e-5
2926
+ 9.4
2927
+ """
2928
+ return self._backend
2929
+
2930
+ def get_objective_value(self):
2931
+ """
2932
+ Return the value of the objective function.
2933
+
2934
+ .. NOTE::
2935
+
2936
+ Behaviour is undefined unless ``solve`` has been called before.
2937
+
2938
+ EXAMPLES::
2939
+
2940
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
2941
+ sage: x, y = p[0], p[1]
2942
+ sage: p.add_constraint(2*x + 3*y, max=6)
2943
+ sage: p.add_constraint(3*x + 2*y, max=6)
2944
+ sage: p.set_objective(x + y + 7)
2945
+ sage: p.solve() # rel tol 1e-5
2946
+ 9.4
2947
+ sage: p.get_objective_value() # rel tol 1e-5
2948
+ 9.4
2949
+ """
2950
+ return self._backend.get_objective_value()
2951
+
2952
+ def best_known_objective_bound(self):
2953
+ r"""
2954
+ Return the value of the currently best known bound.
2955
+
2956
+ This method returns the current best upper (resp. lower) bound
2957
+ on the optimal value of the objective function in a
2958
+ maximization (resp. minimization) problem. It is equal to the
2959
+ output of :meth:`get_objective_value` if the MILP found an
2960
+ optimal solution, but it can differ if it was interrupted
2961
+ manually or after a time limit (cf :meth:`solver_parameter`).
2962
+
2963
+ .. NOTE::
2964
+
2965
+ Has no meaning unless ``solve`` has been called before.
2966
+
2967
+ EXAMPLES::
2968
+
2969
+ sage: # needs sage.graphs
2970
+ sage: g = graphs.CubeGraph(9)
2971
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
2972
+ sage: p.solver_parameter("mip_gap_tolerance",100)
2973
+ sage: b = p.new_variable(binary=True)
2974
+ sage: p.set_objective(p.sum(b[v] for v in g))
2975
+ sage: for v in g:
2976
+ ....: p.add_constraint(b[v] + p.sum(b[u] for u in g.neighbors(v)) <= 1)
2977
+ sage: p.add_constraint(b[v] == 1) # Force an easy non-0 solution
2978
+ sage: p.solve() # rel tol 100
2979
+ 1.0
2980
+ sage: p.best_known_objective_bound() # random
2981
+ 48.0
2982
+ """
2983
+ return self._backend.best_known_objective_bound()
2984
+
2985
+ def get_relative_objective_gap(self):
2986
+ r"""
2987
+ Return the relative objective gap of the best known solution.
2988
+
2989
+ For a minimization problem, this value is computed by
2990
+ `(\texttt{bestinteger} - \texttt{bestobjective}) / (1e-10 +
2991
+ |\texttt{bestobjective}|)`, where ``bestinteger`` is the value returned
2992
+ by :meth:`~MixedIntegerLinearProgram.get_objective_value` and
2993
+ ``bestobjective`` is the value returned by
2994
+ :meth:`~MixedIntegerLinearProgram.best_known_objective_bound`. For a
2995
+ maximization problem, the value is computed by `(\texttt{bestobjective}
2996
+ - \texttt{bestinteger}) / (1e-10 + |\texttt{bestobjective}|)`.
2997
+
2998
+ .. NOTE::
2999
+
3000
+ Has no meaning unless ``solve`` has been called before.
3001
+
3002
+ EXAMPLES::
3003
+
3004
+ sage: # needs sage.graphs
3005
+ sage: g = graphs.CubeGraph(9)
3006
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
3007
+ sage: p.solver_parameter("mip_gap_tolerance",100)
3008
+ sage: b = p.new_variable(binary=True)
3009
+ sage: p.set_objective(p.sum(b[v] for v in g))
3010
+ sage: for v in g:
3011
+ ....: p.add_constraint(b[v] + p.sum(b[u] for u in g.neighbors(v)) <= 1)
3012
+ sage: p.add_constraint(b[v] == 1) # Force an easy non-0 solution
3013
+ sage: p.solve() # rel tol 100
3014
+ 1.0
3015
+ sage: p.get_relative_objective_gap() # random
3016
+ 46.99999999999999
3017
+
3018
+ TESTS:
3019
+
3020
+ Just make sure that the variable *has* been defined, and is not just
3021
+ undefined::
3022
+
3023
+ sage: p.get_relative_objective_gap() > 1 # needs sage.graphs
3024
+ True
3025
+ """
3026
+ return self._backend.get_relative_objective_gap()
3027
+
3028
+ def interactive_lp_problem(self, form='standard'):
3029
+ r"""
3030
+ Return an InteractiveLPProblem and, if available, a basis.
3031
+
3032
+ INPUT:
3033
+
3034
+ - ``form`` -- (default: ``'standard'``) a string specifying return type: either
3035
+ ``None``, or ``'std'`` or ``'standard'``, respectively returns an instance of
3036
+ :class:`InteractiveLPProblem` or of :class:`InteractiveLPProblemStandardForm`
3037
+
3038
+ OUTPUT:
3039
+
3040
+ A 2-tuple consists of an instance of class :class:`InteractiveLPProblem` or
3041
+ :class:`InteractiveLPProblemStandardForm` that is constructed based on a given
3042
+ :class:`MixedIntegerLinearProgram`, and a list of basic
3043
+ variables (the basis) if standard form is chosen (by default), otherwise ``None``.
3044
+
3045
+ All variables must have 0 as lower bound and no upper bound.
3046
+
3047
+ EXAMPLES::
3048
+
3049
+ sage: p = MixedIntegerLinearProgram(names=['m'], solver='GLPK')
3050
+ sage: x = p.new_variable(nonnegative=True)
3051
+ sage: y = p.new_variable(nonnegative=True, name='n')
3052
+ sage: v = p.new_variable(nonnegative=True)
3053
+ sage: p.add_constraint(x[0] + x[1] - 7*y[0] + v[0] <= 2, name='K')
3054
+ sage: p.add_constraint(x[1] + 2*y[0] - v[0] <= 3)
3055
+ sage: p.add_constraint(5*x[0] + y[0] <= 21, name='L')
3056
+ sage: p.set_objective(2*x[0] + 3*x[1] + 4*y[0] + 5*v[0])
3057
+ sage: lp, basis = p.interactive_lp_problem()
3058
+ sage: basis
3059
+ ['K', 'w_1', 'L']
3060
+ sage: lp.constraint_coefficients()
3061
+ [ 1.0 1.0 -7.0 1.0]
3062
+ [ 0.0 1.0 2.0 -1.0]
3063
+ [ 5.0 0.0 1.0 0.0]
3064
+ sage: lp.b()
3065
+ (2.0, 3.0, 21.0)
3066
+ sage: lp.objective_coefficients()
3067
+ (2.0, 3.0, 4.0, 5.0)
3068
+ sage: lp.decision_variables()
3069
+ (m_0, m_1, n_0, x_3)
3070
+ sage: view(lp) #not tested
3071
+ sage: d = lp.dictionary(*basis)
3072
+ sage: view(d) #not tested
3073
+
3074
+ TESTS::
3075
+
3076
+ sage: b = p.get_backend()
3077
+ sage: import sage.numerical.backends.glpk_backend as backend
3078
+ sage: b.solver_parameter(backend.glp_simplex_or_intopt, backend.glp_simplex_only)
3079
+ sage: b.solve()
3080
+ 0
3081
+ sage: lp2, basis2 = p.interactive_lp_problem()
3082
+ sage: set(basis2)
3083
+ {'n_0', 'w_1', 'x_3'}
3084
+ sage: d2 = lp2.dictionary(*basis2)
3085
+ sage: d2.is_optimal()
3086
+ True
3087
+ sage: view(d2) #not tested
3088
+
3089
+ sage: lp3, _ = p.interactive_lp_problem(form=None)
3090
+ sage: lp3.constraint_coefficients()
3091
+ [ 1.0 1.0 -7.0 1.0]
3092
+ [ 0.0 1.0 2.0 -1.0]
3093
+ [ 5.0 0.0 1.0 0.0]
3094
+ sage: lp3.b()
3095
+ (2.0, 3.0, 21.0)
3096
+ sage: lp3.objective_coefficients()
3097
+ (2.0, 3.0, 4.0, 5.0)
3098
+ sage: lp3.decision_variables()
3099
+ (m_0, m_1, n_0, x_3)
3100
+ sage: view(lp3) #not tested
3101
+ """
3102
+ back_end = self.get_backend()
3103
+ for i in range(self.number_of_variables()):
3104
+ if back_end.variable_lower_bound(i) != 0:
3105
+ raise ValueError('Problem variables must have 0 as lower bound')
3106
+ if back_end.variable_upper_bound(i) is not None:
3107
+ raise ValueError('Problem variables must not have upper bound')
3108
+
3109
+ # Construct 'A'
3110
+ coef_matrix = []
3111
+ for constraint in self.constraints():
3112
+ coef_row = [0] * self.number_of_variables()
3113
+ for index, value in zip(constraint[1][0],constraint[1][1]):
3114
+ coef_row[index] = value
3115
+ coef_matrix.append(coef_row)
3116
+
3117
+ # Construct 'b'
3118
+ upper_bound_vector = [c[2] for c in self.constraints()]
3119
+
3120
+ # Raise exception if exist lower bound
3121
+ for constraint in self.constraints():
3122
+ if constraint[0] is not None:
3123
+ raise ValueError('Problem constraints cannot have lower bounds')
3124
+
3125
+ # Construct 'c'
3126
+ def get_obj_coef(i):
3127
+ return back_end.objective_coefficient(i)
3128
+ objective_coefs_vector = [get_obj_coef(i) for i in range(self.number_of_variables())]
3129
+
3130
+ def format(name, prefix, index):
3131
+ if name:
3132
+ return name.replace('[','_').strip(']')
3133
+ else:
3134
+ return prefix + '_' + str(index)
3135
+
3136
+ # Construct 'x'
3137
+ var_names = [format(back_end.col_name(i), 'x', i) for i in range(back_end.ncols())]
3138
+
3139
+ A = coef_matrix
3140
+ b = upper_bound_vector
3141
+ c = objective_coefs_vector
3142
+ x = var_names
3143
+
3144
+ if form is None:
3145
+ from sage.numerical.interactive_simplex_method import InteractiveLPProblem
3146
+ return InteractiveLPProblem(A, b, c, x), None
3147
+ elif form == 'standard' or form == 'std':
3148
+ # Construct slack names
3149
+ slack_names = [format(back_end.row_name(i), 'w', i) for i in range(back_end.nrows())]
3150
+ w = slack_names
3151
+ from sage.numerical.interactive_simplex_method import InteractiveLPProblemStandardForm
3152
+ lp = InteractiveLPProblemStandardForm(A, b, c, x, slack_variables=w)
3153
+ basic_variables = []
3154
+ for i, e in enumerate(lp.x()):
3155
+ if back_end.is_variable_basic(i):
3156
+ basic_variables.append(str(e))
3157
+ elif not back_end.is_variable_nonbasic_at_lower_bound(i):
3158
+ raise ValueError('Invalid column status')
3159
+ for i, e in enumerate(lp.slack_variables()):
3160
+ if back_end.is_slack_variable_basic(i):
3161
+ basic_variables.append(str(e))
3162
+ elif not back_end.is_slack_variable_nonbasic_at_lower_bound(i):
3163
+ raise ValueError('Invalid row status')
3164
+ return lp, basic_variables
3165
+ else:
3166
+ raise ValueError('Form of interactive_lp_problem must be either None or \'standard\'')
3167
+
3168
+
3169
+ class MIPSolverException(RuntimeError):
3170
+ r"""
3171
+ Exception raised when the solver fails.
3172
+
3173
+ EXAMPLES::
3174
+
3175
+ sage: from sage.numerical.mip import MIPSolverException
3176
+ sage: e = MIPSolverException("Error")
3177
+ sage: e
3178
+ MIPSolverException('Error'...)
3179
+ sage: print(e)
3180
+ Error
3181
+
3182
+ TESTS:
3183
+
3184
+ No continuous solution::
3185
+
3186
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
3187
+ sage: v = p.new_variable(nonnegative=True)
3188
+ sage: p.add_constraint(v[0], max=5.5)
3189
+ sage: p.add_constraint(v[0], min=7.6)
3190
+ sage: p.set_objective(v[0])
3191
+
3192
+ Tests of GLPK's Exceptions::
3193
+
3194
+ sage: p.solve()
3195
+ Traceback (most recent call last):
3196
+ ...
3197
+ MIPSolverException: GLPK: Problem has no feasible solution
3198
+
3199
+ No integer solution::
3200
+
3201
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
3202
+ sage: v = p.new_variable(nonnegative=True)
3203
+ sage: p.add_constraint(v[0], max=5.6)
3204
+ sage: p.add_constraint(v[0], min=5.2)
3205
+ sage: p.set_objective(v[0])
3206
+ sage: p.set_integer(v)
3207
+
3208
+ Tests of GLPK's Exceptions::
3209
+
3210
+ sage: p.solve()
3211
+ Traceback (most recent call last):
3212
+ ...
3213
+ MIPSolverException: GLPK: Problem has no feasible solution
3214
+ """
3215
+ pass
3216
+
3217
+
3218
+ cdef class MIPVariable(FiniteFamily):
3219
+ r"""
3220
+ ``MIPVariable`` is a variable used by the class
3221
+ ``MixedIntegerLinearProgram``.
3222
+
3223
+ .. WARNING::
3224
+
3225
+ You should not instantiate this class directly. Instead, use
3226
+ :meth:`MixedIntegerLinearProgram.new_variable`.
3227
+ """
3228
+ def __init__(self, mip, vtype, name='', lower_bound=0, upper_bound=None,
3229
+ indices=None):
3230
+ r"""
3231
+ Constructor for ``MIPVariable``.
3232
+
3233
+ INPUT:
3234
+
3235
+ - ``parent`` -- :class:`MIPVariableParent`; the parent of the
3236
+ MIP variable
3237
+
3238
+ - ``mip`` -- :class:`MixedIntegerLinearProgram`; the
3239
+ underlying linear program
3240
+
3241
+ - ``vtype`` -- integer; defines the type of the variables
3242
+ (default: ``REAL``, i.e., ``vtype=-1``)
3243
+
3244
+ - ``name`` -- a name for the ``MIPVariable``
3245
+
3246
+ - ``lower_bound``, ``upper_bound`` -- lower bound and upper
3247
+ bound on the variable. Set to ``None`` to indicate that the
3248
+ variable is unbounded.
3249
+
3250
+ - ``indices`` -- (optional) an iterable of keys; components
3251
+ corresponding to these keys are created in order,
3252
+ and access to components with other keys will raise an
3253
+ error; otherwise components of this variable can be
3254
+ indexed by arbitrary keys and are created dynamically
3255
+ on access
3256
+
3257
+ For more information, see the method
3258
+ ``MixedIntegerLinearProgram.new_variable``.
3259
+
3260
+ EXAMPLES::
3261
+
3262
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
3263
+ sage: p.new_variable(nonnegative=True)
3264
+ MIPVariable with 0 real components, >= 0
3265
+ """
3266
+ super().__init__({})
3267
+ self._p = mip
3268
+ self._vtype = vtype
3269
+ self._lower_bound = lower_bound
3270
+ self._upper_bound = upper_bound
3271
+ self._name = name
3272
+ if indices is not None:
3273
+ for i in indices:
3274
+ self[i] # creates component
3275
+ self._keys = indices
3276
+
3277
+ def __copy__(self):
3278
+ r"""
3279
+ Return a copy of ``self``.
3280
+
3281
+ EXAMPLES::
3282
+
3283
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
3284
+ sage: pv = p.new_variable(nonnegative=True)
3285
+ sage: pv[0]
3286
+ x_0
3287
+ sage: pvc = copy(pv)
3288
+ sage: pvc[0]
3289
+ x_0
3290
+ sage: pv[1]
3291
+ x_1
3292
+ sage: pvc[1]
3293
+ x_2
3294
+ sage: p.number_of_variables()
3295
+ 3
3296
+ """
3297
+ return self.copy_for_mip(self.mip())
3298
+
3299
+ def __deepcopy__(self, memo={}):
3300
+ r"""
3301
+ Return a copy of ``self``.
3302
+
3303
+ EXAMPLES::
3304
+
3305
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
3306
+ sage: pv = p.new_variable(nonnegative=True)
3307
+ sage: pv[0]
3308
+ x_0
3309
+ sage: pvc = deepcopy(pv)
3310
+ sage: pvc[0]
3311
+ x_0
3312
+ sage: pv[1]
3313
+ x_1
3314
+ sage: pvc[1]
3315
+ x_2
3316
+ sage: p.number_of_variables()
3317
+ 3
3318
+ """
3319
+ return self.copy_for_mip(self.mip())
3320
+
3321
+ def __getitem__(self, i):
3322
+ r"""
3323
+ Return the variable component corresponding to the key.
3324
+
3325
+ Returns the component asked.
3326
+
3327
+ Otherwise, if ``self`` was created with indices=None,
3328
+ creates the component.
3329
+
3330
+ EXAMPLES:
3331
+
3332
+ Dynamic indices::
3333
+
3334
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
3335
+ sage: v = p.new_variable(nonnegative=True)
3336
+ sage: p.set_objective(v[0] + v[1])
3337
+ sage: v[0]
3338
+ x_0
3339
+
3340
+ Static indices::
3341
+
3342
+ sage: p = MixedIntegerLinearProgram()
3343
+ sage: x = p.new_variable(indices=range(7))
3344
+ sage: p.number_of_variables()
3345
+ 7
3346
+ sage: x[3]
3347
+ x_3
3348
+ sage: x[11]
3349
+ Traceback (most recent call last):
3350
+ ...
3351
+ IndexError: 11 does not index a component of MIPVariable with 7 real components
3352
+
3353
+ Indices can be more than just integers::
3354
+
3355
+ sage: p = MixedIntegerLinearProgram()
3356
+ sage: indices = ( (i,j) for i in range(6) for j in range(4) )
3357
+ sage: x = p.new_variable(indices=indices)
3358
+ sage: p.number_of_variables()
3359
+ 24
3360
+ sage: x[(2, 3)]
3361
+ x_11
3362
+
3363
+ TESTS:
3364
+
3365
+ An empty list of static indices gives an error on every component access;
3366
+ it is different from passing ``indices=None`` (the default) on init. ::
3367
+
3368
+ sage: p = MixedIntegerLinearProgram()
3369
+ sage: x = p.new_variable(indices=[])
3370
+ sage: x[0]
3371
+ Traceback (most recent call last):
3372
+ ...
3373
+ IndexError: 0 does not index a component of MIPVariable with 0 real components
3374
+ """
3375
+ cdef int j
3376
+ if i in self._dictionary:
3377
+ return self._dictionary[i]
3378
+ if self._keys is not None:
3379
+ raise IndexError("{} does not index a component of {}".format(i, self))
3380
+ zero = self._p._backend.zero()
3381
+ name = self._name + "[" + str(i) + "]" if self._name else None
3382
+
3383
+ j = self._p._backend.add_variable(
3384
+ lower_bound=self._lower_bound,
3385
+ upper_bound=self._upper_bound,
3386
+ binary=(self._vtype == self._p.__BINARY),
3387
+ continuous=(self._vtype == self._p.__REAL),
3388
+ integer=(self._vtype == self._p.__INTEGER),
3389
+ obj=zero,
3390
+ name=name)
3391
+ v = self._p.linear_functions_parent()({j: 1})
3392
+ self._p._variables[v] = j
3393
+ self._dictionary[i] = v
3394
+ return v
3395
+
3396
+ def copy_for_mip(self, mip):
3397
+ r"""
3398
+ Return a copy of ``self`` suitable for a new :class:`MixedIntegerLinearProgram`
3399
+ instance ``mip``.
3400
+
3401
+ For this to make sense, ``mip`` should have been obtained as a copy of
3402
+ ``self.mip()``.
3403
+
3404
+ EXAMPLES::
3405
+
3406
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
3407
+ sage: pv = p.new_variable(nonnegative=True)
3408
+ sage: pv[0]
3409
+ x_0
3410
+ sage: q = copy(p)
3411
+ sage: qv = pv.copy_for_mip(q)
3412
+ sage: pv[77]
3413
+ x_1
3414
+ sage: p.number_of_variables()
3415
+ 2
3416
+ sage: q.number_of_variables()
3417
+ 1
3418
+ sage: qv[33]
3419
+ x_1
3420
+ sage: p.number_of_variables()
3421
+ 2
3422
+ sage: q.number_of_variables()
3423
+ 2
3424
+
3425
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
3426
+ sage: pv = p.new_variable(indices=[3, 7])
3427
+ sage: q = copy(p)
3428
+ sage: qv = pv.copy_for_mip(q)
3429
+ sage: qv[3]
3430
+ x_0
3431
+ sage: qv[5]
3432
+ Traceback (most recent call last):
3433
+ ...
3434
+ IndexError: 5 does not index a component of MIPVariable with 2 real components
3435
+ """
3436
+ cdef MIPVariable cp = type(self)(mip, self._vtype, self._name,
3437
+ self._lower_bound, self._upper_bound)
3438
+ cp._dictionary = copy(self._dictionary)
3439
+ cp._keys = self._keys
3440
+ return cp
3441
+
3442
+ def set_min(self, min):
3443
+ r"""
3444
+ Set a lower bound on the variable.
3445
+
3446
+ INPUT:
3447
+
3448
+ - ``min`` -- a lower bound, or ``None`` to mean that the variable is
3449
+ unbounded
3450
+
3451
+ EXAMPLES::
3452
+
3453
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
3454
+ sage: v = p.new_variable(real=True, nonnegative=True)
3455
+ sage: p.get_min(v)
3456
+ 0
3457
+ sage: p.get_min(v[0])
3458
+ 0.0
3459
+ sage: p.set_min(v,4)
3460
+ sage: p.get_min(v)
3461
+ 4
3462
+ sage: p.get_min(v[0])
3463
+ 4.0
3464
+
3465
+ TESTS:
3466
+
3467
+ Test that :issue:`20462` is fixed::
3468
+
3469
+ sage: p.<x,y> = MixedIntegerLinearProgram()
3470
+ sage: x[0], y[0]
3471
+ (x_0, x_1)
3472
+ sage: x.set_min(42)
3473
+ sage: p.get_min(y[0]) is None
3474
+ True
3475
+ """
3476
+ self._lower_bound = min
3477
+ for v in self._dictionary.values():
3478
+ self._p.set_min(v,min)
3479
+
3480
+ def set_max(self, max):
3481
+ r"""
3482
+ Set an upper bound on the variable.
3483
+
3484
+ INPUT:
3485
+
3486
+ - ``max`` -- an upper bound, or ``None`` to mean that the variable is
3487
+ unbounded
3488
+
3489
+ EXAMPLES::
3490
+
3491
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
3492
+ sage: v = p.new_variable(real=True, nonnegative=True)
3493
+ sage: p.get_max(v)
3494
+ sage: p.get_max(v[0])
3495
+ sage: p.set_max(v,4)
3496
+ sage: p.get_max(v)
3497
+ 4
3498
+ sage: p.get_max(v[0])
3499
+ 4.0
3500
+
3501
+ TESTS:
3502
+
3503
+ Test that :issue:`20462` is fixed::
3504
+
3505
+ sage: p.<x,y> = MixedIntegerLinearProgram()
3506
+ sage: x[0], y[0]
3507
+ (x_0, x_1)
3508
+ sage: x.set_max(42)
3509
+ sage: p.get_max(y[0]) is None
3510
+ True
3511
+ """
3512
+ self._upper_bound = max
3513
+ for v in self._dictionary.values():
3514
+ self._p.set_max(v,max)
3515
+
3516
+ def _repr_(self):
3517
+ r"""
3518
+ Return a representation of ``self``.
3519
+
3520
+ EXAMPLES::
3521
+
3522
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
3523
+ sage: v = p.new_variable()
3524
+ sage: v
3525
+ MIPVariable with 0 real components
3526
+ sage: x = p.new_variable(integer=True, nonnegative=True, name='x')
3527
+ sage: x[0]
3528
+ x_0
3529
+ sage: x
3530
+ MIPVariable x with 1 integer component, >= 0
3531
+ sage: x[1]
3532
+ x_1
3533
+ sage: x
3534
+ MIPVariable x with 2 integer components, >= 0
3535
+ sage: y = p.new_variable(real=True, name='y', indices=range(5))
3536
+ sage: y.set_min(0)
3537
+ sage: y.set_max(17)
3538
+ sage: y
3539
+ MIPVariable y with 5 real components, >= 0, <= 17
3540
+ sage: z = p.new_variable(binary=True, name='z', indices=range(7))
3541
+ sage: z
3542
+ MIPVariable z with 7 binary components
3543
+ """
3544
+ s = 'MIPVariable{0} with {1} {2} component{3}'.format(
3545
+ " " + self._name if self._name else "",
3546
+ len(self._dictionary),
3547
+ {0:"binary", -1:"real", 1:"integer"}[self._vtype],
3548
+ "s" if len(self._dictionary) != 1 else "")
3549
+ if (self._vtype != 0) and (self._lower_bound is not None):
3550
+ s += ', >= {0}'.format(self._lower_bound)
3551
+ if (self._vtype != 0) and (self._upper_bound is not None):
3552
+ s += ', <= {0}'.format(self._upper_bound)
3553
+ return s
3554
+
3555
+ def keys(self):
3556
+ r"""
3557
+ Return the keys already defined in the dictionary.
3558
+
3559
+ EXAMPLES::
3560
+
3561
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
3562
+ sage: v = p.new_variable(nonnegative=True)
3563
+ sage: p.set_objective(v[0] + v[1])
3564
+ sage: sorted(v.keys())
3565
+ [0, 1]
3566
+ """
3567
+ return self._dictionary.keys()
3568
+
3569
+ def items(self):
3570
+ r"""
3571
+ Return the pairs (keys, value) contained in the dictionary.
3572
+
3573
+ EXAMPLES::
3574
+
3575
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
3576
+ sage: v = p.new_variable(nonnegative=True)
3577
+ sage: p.set_objective(v[0] + v[1])
3578
+ sage: sorted(v.items())
3579
+ [(0, x_0), (1, x_1)]
3580
+ """
3581
+ return self._dictionary.items()
3582
+
3583
+ def values(self):
3584
+ r"""
3585
+ Return the symbolic variables associated to the current dictionary.
3586
+
3587
+ EXAMPLES::
3588
+
3589
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
3590
+ sage: v = p.new_variable(nonnegative=True)
3591
+ sage: p.set_objective(v[0] + v[1])
3592
+ sage: sorted(v.values(), key=str)
3593
+ [x_0, x_1]
3594
+ """
3595
+ return self._dictionary.values()
3596
+
3597
+ def mip(self):
3598
+ r"""
3599
+ Return the :class:`MixedIntegerLinearProgram` in which ``self`` is a variable.
3600
+
3601
+ EXAMPLES::
3602
+
3603
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
3604
+ sage: v = p.new_variable(nonnegative=True)
3605
+ sage: p == v.mip()
3606
+ True
3607
+ """
3608
+ return self._p
3609
+
3610
+ def __mul__(left, right):
3611
+ """
3612
+ Multiply ``left`` with ``right``.
3613
+
3614
+ EXAMPLES::
3615
+
3616
+ sage: p = MixedIntegerLinearProgram(solver='GLPK')
3617
+ sage: v = p.new_variable()
3618
+ sage: m = matrix([[1,2], [3,4]])
3619
+ sage: v * m
3620
+ (1.0, 2.0)*x_0 + (3.0, 4.0)*x_1
3621
+ sage: m * v
3622
+ (1.0, 3.0)*x_0 + (2.0, 4.0)*x_1
3623
+
3624
+ sage: p = MixedIntegerLinearProgram(solver='PPL')
3625
+ sage: v = p.new_variable()
3626
+ sage: m = matrix([[1,1/2], [2/3,3/4]])
3627
+ sage: v * m
3628
+ (1, 1/2)*x_0 + (2/3, 3/4)*x_1
3629
+ sage: m * v
3630
+ (1, 2/3)*x_0 + (1/2, 3/4)*x_1
3631
+ """
3632
+ if isinstance(left, MIPVariable):
3633
+ if not isinstance(right, Matrix):
3634
+ return NotImplemented
3635
+ return (<MIPVariable> left)._matrix_rmul_impl(right)
3636
+ else:
3637
+ if not isinstance(left, Matrix):
3638
+ return NotImplemented
3639
+ return (<MIPVariable> right)._matrix_lmul_impl(left)
3640
+
3641
+ cdef _matrix_rmul_impl(self, m):
3642
+ """
3643
+ Implement the action of a matrix multiplying from the right.
3644
+ """
3645
+ result = dict()
3646
+ for i, row in enumerate(m.rows()):
3647
+ x = self[i]
3648
+ x_index, = x.dict().keys()
3649
+ result[x_index] = row
3650
+ from sage.modules.free_module import FreeModule
3651
+ V = FreeModule(self._p.base_ring(), m.ncols())
3652
+ T = self._p.linear_functions_parent().tensor(V)
3653
+ return T(result)
3654
+
3655
+ cdef _matrix_lmul_impl(self, m):
3656
+ """
3657
+ Implement the action of a matrix multiplying from the left.
3658
+ """
3659
+ result = {}
3660
+ for i, col in enumerate(m.columns()):
3661
+ x = self[i]
3662
+ x_index, = x.dict().keys()
3663
+ result[x_index] = col
3664
+ from sage.modules.free_module import FreeModule
3665
+ V = FreeModule(self._p.base_ring(), m.nrows())
3666
+ T = self._p.linear_functions_parent().tensor(V)
3667
+ return T(result)