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.
- passagemath_polyhedra-10.6.31rc3.dist-info/METADATA +367 -0
- passagemath_polyhedra-10.6.31rc3.dist-info/METADATA.bak +369 -0
- passagemath_polyhedra-10.6.31rc3.dist-info/RECORD +206 -0
- passagemath_polyhedra-10.6.31rc3.dist-info/WHEEL +6 -0
- passagemath_polyhedra-10.6.31rc3.dist-info/top_level.txt +2 -0
- passagemath_polyhedra.libs/libgmp-6e109695.so.10.5.0 +0 -0
- passagemath_polyhedra.libs/libgomp-e985bcbb.so.1.0.0 +0 -0
- sage/all__sagemath_polyhedra.py +50 -0
- sage/game_theory/all.py +8 -0
- sage/game_theory/catalog.py +6 -0
- sage/game_theory/catalog_normal_form_games.py +923 -0
- sage/game_theory/cooperative_game.py +844 -0
- sage/game_theory/matching_game.py +1181 -0
- sage/game_theory/normal_form_game.py +2697 -0
- sage/game_theory/parser.py +275 -0
- sage/geometry/all__sagemath_polyhedra.py +22 -0
- sage/geometry/cone.py +6940 -0
- sage/geometry/cone_catalog.py +847 -0
- sage/geometry/cone_critical_angles.py +1027 -0
- sage/geometry/convex_set.py +1119 -0
- sage/geometry/fan.py +3743 -0
- sage/geometry/fan_isomorphism.py +389 -0
- sage/geometry/fan_morphism.py +1884 -0
- sage/geometry/hasse_diagram.py +202 -0
- sage/geometry/hyperplane_arrangement/affine_subspace.py +390 -0
- sage/geometry/hyperplane_arrangement/all.py +1 -0
- sage/geometry/hyperplane_arrangement/arrangement.py +3895 -0
- sage/geometry/hyperplane_arrangement/check_freeness.py +145 -0
- sage/geometry/hyperplane_arrangement/hyperplane.py +773 -0
- sage/geometry/hyperplane_arrangement/library.py +825 -0
- sage/geometry/hyperplane_arrangement/ordered_arrangement.py +642 -0
- sage/geometry/hyperplane_arrangement/plot.py +520 -0
- sage/geometry/integral_points.py +35 -0
- sage/geometry/integral_points_generic_dense.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/geometry/integral_points_generic_dense.pyx +7 -0
- sage/geometry/lattice_polytope.py +5894 -0
- sage/geometry/linear_expression.py +773 -0
- sage/geometry/newton_polygon.py +767 -0
- sage/geometry/point_collection.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/geometry/point_collection.pyx +1008 -0
- sage/geometry/polyhedral_complex.py +2616 -0
- sage/geometry/polyhedron/all.py +8 -0
- sage/geometry/polyhedron/backend_cdd.py +460 -0
- sage/geometry/polyhedron/backend_cdd_rdf.py +231 -0
- sage/geometry/polyhedron/backend_field.py +347 -0
- sage/geometry/polyhedron/backend_normaliz.py +2503 -0
- sage/geometry/polyhedron/backend_number_field.py +168 -0
- sage/geometry/polyhedron/backend_polymake.py +765 -0
- sage/geometry/polyhedron/backend_ppl.py +582 -0
- sage/geometry/polyhedron/base.py +1206 -0
- sage/geometry/polyhedron/base0.py +1444 -0
- sage/geometry/polyhedron/base1.py +886 -0
- sage/geometry/polyhedron/base2.py +812 -0
- sage/geometry/polyhedron/base3.py +1845 -0
- sage/geometry/polyhedron/base4.py +1262 -0
- sage/geometry/polyhedron/base5.py +2700 -0
- sage/geometry/polyhedron/base6.py +1741 -0
- sage/geometry/polyhedron/base7.py +997 -0
- sage/geometry/polyhedron/base_QQ.py +1258 -0
- sage/geometry/polyhedron/base_RDF.py +98 -0
- sage/geometry/polyhedron/base_ZZ.py +934 -0
- sage/geometry/polyhedron/base_mutable.py +215 -0
- sage/geometry/polyhedron/base_number_field.py +122 -0
- sage/geometry/polyhedron/cdd_file_format.py +155 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/all.py +1 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/base.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd +76 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +3859 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd +39 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +1038 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/conversions.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pxd +9 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx +501 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd +207 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +102 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +2274 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd +370 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pyx +84 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd +31 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx +587 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd +52 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx +560 -0
- sage/geometry/polyhedron/constructor.py +773 -0
- sage/geometry/polyhedron/double_description.py +753 -0
- sage/geometry/polyhedron/double_description_inhomogeneous.py +564 -0
- sage/geometry/polyhedron/face.py +1060 -0
- sage/geometry/polyhedron/generating_function.py +1810 -0
- sage/geometry/polyhedron/lattice_euclidean_group_element.py +178 -0
- sage/geometry/polyhedron/library.py +3502 -0
- sage/geometry/polyhedron/misc.py +121 -0
- sage/geometry/polyhedron/modules/all.py +1 -0
- sage/geometry/polyhedron/modules/formal_polyhedra_module.py +155 -0
- sage/geometry/polyhedron/palp_database.py +447 -0
- sage/geometry/polyhedron/parent.py +1279 -0
- sage/geometry/polyhedron/plot.py +1986 -0
- sage/geometry/polyhedron/ppl_lattice_polygon.py +556 -0
- sage/geometry/polyhedron/ppl_lattice_polytope.py +1257 -0
- sage/geometry/polyhedron/representation.py +1723 -0
- sage/geometry/pseudolines.py +515 -0
- sage/geometry/relative_interior.py +445 -0
- sage/geometry/toric_plotter.py +1103 -0
- sage/geometry/triangulation/all.py +2 -0
- sage/geometry/triangulation/base.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/geometry/triangulation/base.pyx +963 -0
- sage/geometry/triangulation/data.h +147 -0
- sage/geometry/triangulation/data.pxd +4 -0
- sage/geometry/triangulation/element.py +914 -0
- sage/geometry/triangulation/functions.h +10 -0
- sage/geometry/triangulation/functions.pxd +4 -0
- sage/geometry/triangulation/point_configuration.py +2256 -0
- sage/geometry/triangulation/triangulations.h +49 -0
- sage/geometry/triangulation/triangulations.pxd +7 -0
- sage/geometry/voronoi_diagram.py +319 -0
- sage/interfaces/all__sagemath_polyhedra.py +1 -0
- sage/interfaces/polymake.py +2028 -0
- sage/numerical/all.py +13 -0
- sage/numerical/all__sagemath_polyhedra.py +11 -0
- sage/numerical/backends/all.py +1 -0
- sage/numerical/backends/all__sagemath_polyhedra.py +1 -0
- sage/numerical/backends/cvxopt_backend.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/numerical/backends/cvxopt_backend.pyx +1006 -0
- sage/numerical/backends/cvxopt_backend_test.py +19 -0
- sage/numerical/backends/cvxopt_sdp_backend.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/numerical/backends/cvxopt_sdp_backend.pyx +382 -0
- sage/numerical/backends/cvxpy_backend.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/numerical/backends/cvxpy_backend.pxd +41 -0
- sage/numerical/backends/cvxpy_backend.pyx +934 -0
- sage/numerical/backends/cvxpy_backend_test.py +13 -0
- sage/numerical/backends/generic_backend_test.py +24 -0
- sage/numerical/backends/interactivelp_backend.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/numerical/backends/interactivelp_backend.pxd +36 -0
- sage/numerical/backends/interactivelp_backend.pyx +1231 -0
- sage/numerical/backends/interactivelp_backend_test.py +12 -0
- sage/numerical/backends/logging_backend.py +391 -0
- sage/numerical/backends/matrix_sdp_backend.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/numerical/backends/matrix_sdp_backend.pxd +15 -0
- sage/numerical/backends/matrix_sdp_backend.pyx +478 -0
- sage/numerical/backends/ppl_backend.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/numerical/backends/ppl_backend.pyx +1126 -0
- sage/numerical/backends/ppl_backend_test.py +13 -0
- sage/numerical/backends/scip_backend.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/numerical/backends/scip_backend.pxd +22 -0
- sage/numerical/backends/scip_backend.pyx +1289 -0
- sage/numerical/backends/scip_backend_test.py +13 -0
- sage/numerical/interactive_simplex_method.py +5338 -0
- sage/numerical/knapsack.py +665 -0
- sage/numerical/linear_functions.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/numerical/linear_functions.pxd +31 -0
- sage/numerical/linear_functions.pyx +1648 -0
- sage/numerical/linear_tensor.py +470 -0
- sage/numerical/linear_tensor_constraints.py +448 -0
- sage/numerical/linear_tensor_element.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/numerical/linear_tensor_element.pxd +6 -0
- sage/numerical/linear_tensor_element.pyx +459 -0
- sage/numerical/mip.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/numerical/mip.pxd +40 -0
- sage/numerical/mip.pyx +3667 -0
- sage/numerical/sdp.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/numerical/sdp.pxd +39 -0
- sage/numerical/sdp.pyx +1433 -0
- sage/rings/all__sagemath_polyhedra.py +3 -0
- sage/rings/polynomial/all__sagemath_polyhedra.py +10 -0
- sage/rings/polynomial/omega.py +982 -0
- sage/schemes/all__sagemath_polyhedra.py +2 -0
- sage/schemes/toric/all.py +10 -0
- sage/schemes/toric/chow_group.py +1248 -0
- sage/schemes/toric/divisor.py +2082 -0
- sage/schemes/toric/divisor_class.cpython-314-x86_64-linux-gnu.so +0 -0
- sage/schemes/toric/divisor_class.pyx +322 -0
- sage/schemes/toric/fano_variety.py +1606 -0
- sage/schemes/toric/homset.py +650 -0
- sage/schemes/toric/ideal.py +451 -0
- sage/schemes/toric/library.py +1322 -0
- sage/schemes/toric/morphism.py +1958 -0
- sage/schemes/toric/points.py +1032 -0
- sage/schemes/toric/sheaf/all.py +1 -0
- sage/schemes/toric/sheaf/constructor.py +302 -0
- sage/schemes/toric/sheaf/klyachko.py +921 -0
- sage/schemes/toric/toric_subscheme.py +905 -0
- sage/schemes/toric/variety.py +3460 -0
- sage/schemes/toric/weierstrass.py +1078 -0
- sage/schemes/toric/weierstrass_covering.py +457 -0
- sage/schemes/toric/weierstrass_higher.py +288 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.info +10 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v03 +0 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v04 +0 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v05 +1 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v06 +1 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.info +22 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v04 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v05 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v06 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v07 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v08 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v09 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v10 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v11 +1 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v12 +1 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v13 +1 -0
- sage_wheels/share/reflexive_polytopes/reflexive_polytopes_2d +80 -0
- sage_wheels/share/reflexive_polytopes/reflexive_polytopes_3d +37977 -0
|
@@ -0,0 +1,934 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-polyhedra
|
|
2
|
+
# sage.doctest: optional - cvxpy
|
|
3
|
+
r"""
|
|
4
|
+
CVXPY Backend
|
|
5
|
+
|
|
6
|
+
AUTHORS:
|
|
7
|
+
|
|
8
|
+
- Nathann Cohen (2010-10) : generic_backend template
|
|
9
|
+
- Matthias Koeppe (2022-03) : this backend
|
|
10
|
+
"""
|
|
11
|
+
# ****************************************************************************
|
|
12
|
+
# Copyright (C) 2010 Nathann Cohen <nathann.cohen@gmail.com>
|
|
13
|
+
# Copyright (C) 2022 Matthias Koeppe <mkoeppe@math.ucdavis.edu>
|
|
14
|
+
#
|
|
15
|
+
# This program is free software: you can redistribute it and/or modify
|
|
16
|
+
# it under the terms of the GNU General Public License as published by
|
|
17
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
18
|
+
# (at your option) any later version.
|
|
19
|
+
# https://www.gnu.org/licenses/
|
|
20
|
+
# ****************************************************************************
|
|
21
|
+
|
|
22
|
+
from sage.numerical.mip import MIPSolverException
|
|
23
|
+
from sage.rings.real_double import RDF
|
|
24
|
+
from copy import copy
|
|
25
|
+
import cvxpy
|
|
26
|
+
from cvxpy.atoms.affine.add_expr import AddExpression
|
|
27
|
+
from cvxpy.expressions.constants import Constant
|
|
28
|
+
from cvxpy.constraints.zero import Equality
|
|
29
|
+
|
|
30
|
+
cdef class CVXPYBackend:
|
|
31
|
+
"""
|
|
32
|
+
MIP Backend that delegates to CVXPY.
|
|
33
|
+
|
|
34
|
+
CVXPY interfaces to various solvers, see
|
|
35
|
+
https://www.cvxpy.org/install/index.html#install and
|
|
36
|
+
https://www.cvxpy.org/tutorial/advanced/index.html#choosing-a-solver
|
|
37
|
+
|
|
38
|
+
EXAMPLES::
|
|
39
|
+
|
|
40
|
+
sage: import cvxpy
|
|
41
|
+
sage: cvxpy.installed_solvers() # random
|
|
42
|
+
|
|
43
|
+
Using the default solver determined by CVXPY::
|
|
44
|
+
|
|
45
|
+
sage: p = MixedIntegerLinearProgram(solver='CVXPY'); p.solve()
|
|
46
|
+
0.0
|
|
47
|
+
|
|
48
|
+
Using a specific solver::
|
|
49
|
+
|
|
50
|
+
sage: p = MixedIntegerLinearProgram(solver='CVXPY/OSQP'); p.solve()
|
|
51
|
+
0.0
|
|
52
|
+
sage: p = MixedIntegerLinearProgram(solver='CVXPY/ECOS'); p.solve()
|
|
53
|
+
0.0
|
|
54
|
+
sage: p = MixedIntegerLinearProgram(solver='CVXPY/SCS'); p.solve()
|
|
55
|
+
0.0
|
|
56
|
+
sage: p = MixedIntegerLinearProgram(solver='CVXPY/SciPy/HiGHS'); p.solve()
|
|
57
|
+
0.0
|
|
58
|
+
|
|
59
|
+
Open-source solvers provided by optional packages::
|
|
60
|
+
|
|
61
|
+
sage: p = MixedIntegerLinearProgram(solver='CVXPY/GLPK'); p.solve() # needs cvxopt
|
|
62
|
+
0.0
|
|
63
|
+
sage: p = MixedIntegerLinearProgram(solver='CVXPY/GLPK_MI'); p.solve() # needs cvxopt
|
|
64
|
+
0.0
|
|
65
|
+
sage: p = MixedIntegerLinearProgram(solver='CVXPY/CVXOPT'); p.solve() # needs cvxopt
|
|
66
|
+
0.0
|
|
67
|
+
sage: p = MixedIntegerLinearProgram(solver='CVXPY/GLOP'); p.solve() # optional - ortools
|
|
68
|
+
0.0
|
|
69
|
+
sage: p = MixedIntegerLinearProgram(solver='CVXPY/PDLP'); p.solve() # optional - ortools
|
|
70
|
+
0.0
|
|
71
|
+
sage: p = MixedIntegerLinearProgram(solver='CVXPY/CBC'); p.solve() # optional - cylp
|
|
72
|
+
0.0
|
|
73
|
+
|
|
74
|
+
Non-free solvers::
|
|
75
|
+
|
|
76
|
+
sage: p = MixedIntegerLinearProgram(solver='CVXPY/Gurobi'); p.solve() # optional - gurobi
|
|
77
|
+
0.0
|
|
78
|
+
sage: p = MixedIntegerLinearProgram(solver='CVXPY/CPLEX'); p.solve() # optional - cplex
|
|
79
|
+
0.0
|
|
80
|
+
sage: p = MixedIntegerLinearProgram(solver='CVXPY/MOSEK'); p.solve() # optional - mosek
|
|
81
|
+
0.0
|
|
82
|
+
sage: p = MixedIntegerLinearProgram(solver='CVXPY/SCIP'); p.solve() # optional - pyscipopt
|
|
83
|
+
0.0
|
|
84
|
+
sage: p = MixedIntegerLinearProgram(solver='CVXPY/XPRESS'); p.solve() # optional - xpress
|
|
85
|
+
0.0
|
|
86
|
+
sage: p = MixedIntegerLinearProgram(solver="CVXPY/NAG"); p.solve() # optional - naginterfaces
|
|
87
|
+
0.0
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
def __cinit__(self, maximization=True, base_ring=None, cvxpy_solver=None, cvxpy_solver_args=None):
|
|
91
|
+
"""
|
|
92
|
+
Cython constructor.
|
|
93
|
+
|
|
94
|
+
INPUT:
|
|
95
|
+
|
|
96
|
+
- ``maximization``-- boolean (default: ``True``); whether this is a
|
|
97
|
+
maximization or minimization problem
|
|
98
|
+
|
|
99
|
+
- ``base_ring`` -- (optional) must be ``RDF`` if provided
|
|
100
|
+
|
|
101
|
+
- ``cvxpy_solver`` -- (optional) passed to :meth:`cvxpy.Problem.solve`
|
|
102
|
+
as the parameter ``solver``
|
|
103
|
+
|
|
104
|
+
- ``cvxpy_solver_args`` -- dictionary (optional); passed to
|
|
105
|
+
:meth:`cvxpy.Problem.solve` as additional keyword arguments
|
|
106
|
+
|
|
107
|
+
EXAMPLES::
|
|
108
|
+
|
|
109
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
110
|
+
sage: p = get_solver(solver='CVXPY')
|
|
111
|
+
"""
|
|
112
|
+
if base_ring is None:
|
|
113
|
+
base_ring = RDF
|
|
114
|
+
elif base_ring != RDF:
|
|
115
|
+
raise ValueError('base_ring must be RDF')
|
|
116
|
+
|
|
117
|
+
if cvxpy_solver_args is None:
|
|
118
|
+
cvxpy_solver_args = {}
|
|
119
|
+
|
|
120
|
+
if isinstance(cvxpy_solver, str):
|
|
121
|
+
cvxpy_solver = cvxpy_solver.upper()
|
|
122
|
+
if cvxpy_solver.startswith("SCIPY/"):
|
|
123
|
+
cvxpy_solver_args['scipy_options'] = {"method": cvxpy_solver[len("SCIPY/"):]}
|
|
124
|
+
cvxpy_solver = "SCIPY"
|
|
125
|
+
cvxpy_solver = getattr(cvxpy, cvxpy_solver)
|
|
126
|
+
self._cvxpy_solver = cvxpy_solver
|
|
127
|
+
self._cvxpy_solver_args = cvxpy_solver_args
|
|
128
|
+
|
|
129
|
+
self.set_verbosity(0)
|
|
130
|
+
self.variables = []
|
|
131
|
+
self.constraint_names = []
|
|
132
|
+
self.Matrix = []
|
|
133
|
+
self.row_lower_bound = []
|
|
134
|
+
self.row_upper_bound = []
|
|
135
|
+
self.col_lower_bound = []
|
|
136
|
+
self.col_upper_bound = []
|
|
137
|
+
self.objective_coefficients = []
|
|
138
|
+
self.obj_constant_term = 0.0
|
|
139
|
+
if maximization:
|
|
140
|
+
objective = cvxpy.Maximize(0)
|
|
141
|
+
else:
|
|
142
|
+
objective = cvxpy.Minimize(0)
|
|
143
|
+
self.problem = cvxpy.Problem(objective, ())
|
|
144
|
+
|
|
145
|
+
cpdef __copy__(self):
|
|
146
|
+
"""
|
|
147
|
+
Return a copy of ``self``.
|
|
148
|
+
|
|
149
|
+
EXAMPLES::
|
|
150
|
+
|
|
151
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
152
|
+
sage: p = MixedIntegerLinearProgram(solver='CVXPY')
|
|
153
|
+
sage: b = p.new_variable()
|
|
154
|
+
sage: p.add_constraint(b[1] + b[2] <= 6)
|
|
155
|
+
sage: p.set_objective(b[1] + b[2])
|
|
156
|
+
sage: cp = copy(p.get_backend())
|
|
157
|
+
sage: cp.solve()
|
|
158
|
+
doctest:warning...:
|
|
159
|
+
FutureWarning:...
|
|
160
|
+
0
|
|
161
|
+
sage: cp.get_objective_value() # abs tol 1e-7
|
|
162
|
+
6.0
|
|
163
|
+
"""
|
|
164
|
+
cdef CVXPYBackend cp = type(self)(base_ring=self.base_ring())
|
|
165
|
+
cp.problem = self.problem # it's considered immutable; so no need to copy.
|
|
166
|
+
cp.variables = copy(self.variables)
|
|
167
|
+
cp.constraint_names = copy(self.constraint_names)
|
|
168
|
+
cp.Matrix = [row[:] for row in self.Matrix]
|
|
169
|
+
cp.row_lower_bound = self.row_lower_bound[:]
|
|
170
|
+
cp.row_upper_bound = self.row_upper_bound[:]
|
|
171
|
+
cp.col_lower_bound = self.col_lower_bound[:]
|
|
172
|
+
cp.col_upper_bound = self.col_upper_bound[:]
|
|
173
|
+
cp.objective_coefficients = self.objective_coefficients[:]
|
|
174
|
+
cp.obj_constant_term = self.obj_constant_term
|
|
175
|
+
return cp
|
|
176
|
+
|
|
177
|
+
cpdef cvxpy_problem(self):
|
|
178
|
+
return self.problem
|
|
179
|
+
|
|
180
|
+
def cvxpy_variables(self):
|
|
181
|
+
return self.variables
|
|
182
|
+
|
|
183
|
+
cpdef int add_variable(self, lower_bound=0, upper_bound=None,
|
|
184
|
+
binary=False, continuous=True, integer=False,
|
|
185
|
+
obj=None, name=None, coefficients=None) except -1:
|
|
186
|
+
## coefficients is an extension in this backend,
|
|
187
|
+
## and a proposed addition to the interface, to unify this with add_col.
|
|
188
|
+
"""
|
|
189
|
+
Add a variable.
|
|
190
|
+
|
|
191
|
+
This amounts to adding a new column to the matrix. By default,
|
|
192
|
+
the variable is both nonnegative and real.
|
|
193
|
+
|
|
194
|
+
INPUT:
|
|
195
|
+
|
|
196
|
+
- ``lower_bound`` -- the lower bound of the variable (default: 0)
|
|
197
|
+
|
|
198
|
+
- ``upper_bound`` -- the upper bound of the variable (default: ``None``)
|
|
199
|
+
|
|
200
|
+
- ``binary`` -- ``True`` if the variable is binary (default: ``False``)
|
|
201
|
+
|
|
202
|
+
- ``continuous`` -- ``True`` if the variable is continuous (default: ``True``)
|
|
203
|
+
|
|
204
|
+
- ``integer`` -- ``True`` if the variable is integral (default: ``False``)
|
|
205
|
+
|
|
206
|
+
- ``obj`` -- (optional) coefficient of this variable in the objective function (default: 0)
|
|
207
|
+
|
|
208
|
+
- ``name`` -- an optional name for the newly added variable (default: ``None``)
|
|
209
|
+
|
|
210
|
+
- ``coefficients`` -- (optional) an iterable of pairs ``(i, v)``; in each
|
|
211
|
+
pair, ``i`` is a row index (integer) and ``v`` is a value (element of :meth:`base_ring`)
|
|
212
|
+
|
|
213
|
+
OUTPUT: the index of the newly created variable
|
|
214
|
+
|
|
215
|
+
EXAMPLES::
|
|
216
|
+
|
|
217
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
218
|
+
sage: p = get_solver(solver='CVXPY')
|
|
219
|
+
sage: p.ncols()
|
|
220
|
+
0
|
|
221
|
+
sage: p.add_variable()
|
|
222
|
+
0
|
|
223
|
+
sage: p.ncols()
|
|
224
|
+
1
|
|
225
|
+
sage: p.add_variable(continuous=True, integer=True)
|
|
226
|
+
Traceback (most recent call last):
|
|
227
|
+
...
|
|
228
|
+
ValueError: ...
|
|
229
|
+
sage: p.add_variable(name='x',obj=1)
|
|
230
|
+
1
|
|
231
|
+
sage: p.col_name(1)
|
|
232
|
+
'x'
|
|
233
|
+
sage: p.objective_coefficient(1)
|
|
234
|
+
1.0
|
|
235
|
+
"""
|
|
236
|
+
cdef int vtype = int(binary) + int(continuous) + int(integer)
|
|
237
|
+
if vtype == 0:
|
|
238
|
+
continuous = True
|
|
239
|
+
elif vtype != 1:
|
|
240
|
+
raise ValueError("Exactly one parameter of 'binary', 'integer' and 'continuous' must be 'True'.")
|
|
241
|
+
|
|
242
|
+
for i in range(len(self.Matrix)):
|
|
243
|
+
self.Matrix[i].append(0.0)
|
|
244
|
+
if lower_bound is not None:
|
|
245
|
+
lower_bound = float(lower_bound)
|
|
246
|
+
self.col_lower_bound.append(lower_bound)
|
|
247
|
+
if upper_bound is not None:
|
|
248
|
+
upper_bound = float(upper_bound)
|
|
249
|
+
self.col_upper_bound.append(upper_bound)
|
|
250
|
+
if obj is None:
|
|
251
|
+
obj = 0.0
|
|
252
|
+
else:
|
|
253
|
+
obj = float(obj)
|
|
254
|
+
self.objective_coefficients.append(obj)
|
|
255
|
+
|
|
256
|
+
if name is None:
|
|
257
|
+
name = f'x_{self.ncols()}'
|
|
258
|
+
|
|
259
|
+
if binary:
|
|
260
|
+
variable = cvxpy.Variable(name=name, boolean=True)
|
|
261
|
+
elif integer:
|
|
262
|
+
variable = cvxpy.Variable(name=name, integer=True)
|
|
263
|
+
else:
|
|
264
|
+
variable = cvxpy.Variable(name=name)
|
|
265
|
+
|
|
266
|
+
self.variables.append(variable)
|
|
267
|
+
index = self.ncols() - 1
|
|
268
|
+
|
|
269
|
+
if coefficients is not None:
|
|
270
|
+
constraints = list(self.problem.constraints)
|
|
271
|
+
for i, v in coefficients:
|
|
272
|
+
if not isinstance(constraints[i], Equality):
|
|
273
|
+
raise NotImplementedError('adding coefficients to inequalities is ambiguous '
|
|
274
|
+
'because cvxpy rewrites all inequalities as <=')
|
|
275
|
+
constraints[i] = type(constraints[i])(constraints[i].args[0] + float(v) * variable,
|
|
276
|
+
constraints[i].args[1])
|
|
277
|
+
self.problem = cvxpy.Problem(self.problem.objective, constraints)
|
|
278
|
+
|
|
279
|
+
self.add_linear_constraint([(index, 1)], lower_bound, upper_bound)
|
|
280
|
+
|
|
281
|
+
if obj:
|
|
282
|
+
objective = type(self.problem.objective)(self.problem.objective.args[0]
|
|
283
|
+
+ obj * variable)
|
|
284
|
+
self.problem = cvxpy.Problem(objective, self.problem.constraints)
|
|
285
|
+
|
|
286
|
+
return index
|
|
287
|
+
|
|
288
|
+
cpdef set_verbosity(self, int level):
|
|
289
|
+
"""
|
|
290
|
+
Set the log (verbosity) level.
|
|
291
|
+
|
|
292
|
+
This is currently ignored.
|
|
293
|
+
|
|
294
|
+
INPUT:
|
|
295
|
+
|
|
296
|
+
- ``level`` -- integer; from 0 (no verbosity) to 3
|
|
297
|
+
|
|
298
|
+
EXAMPLES::
|
|
299
|
+
|
|
300
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
301
|
+
sage: p = get_solver(solver='CVXPY')
|
|
302
|
+
sage: p.set_verbosity(2)
|
|
303
|
+
"""
|
|
304
|
+
pass
|
|
305
|
+
|
|
306
|
+
cpdef add_linear_constraint(self, coefficients, lower_bound, upper_bound, name=None):
|
|
307
|
+
"""
|
|
308
|
+
Add a linear constraint.
|
|
309
|
+
|
|
310
|
+
INPUT:
|
|
311
|
+
|
|
312
|
+
- ``coefficients`` -- an iterable of pairs ``(i, v)``. In each
|
|
313
|
+
pair, ``i`` is a variable index (integer) and ``v`` is a
|
|
314
|
+
value (element of :meth:`base_ring`).
|
|
315
|
+
|
|
316
|
+
- ``lower_bound`` -- element of :meth:`base_ring` or
|
|
317
|
+
``None``; the lower bound
|
|
318
|
+
|
|
319
|
+
- ``upper_bound`` -- element of :meth:`base_ring` or
|
|
320
|
+
``None``; the upper bound
|
|
321
|
+
|
|
322
|
+
- ``name`` -- string or ``None``; optional name for this row
|
|
323
|
+
|
|
324
|
+
EXAMPLES::
|
|
325
|
+
|
|
326
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
327
|
+
sage: p = get_solver(solver='CVXPY')
|
|
328
|
+
sage: p.add_variables(5)
|
|
329
|
+
4
|
|
330
|
+
sage: index = p.nrows()
|
|
331
|
+
sage: p.add_linear_constraint( zip(range(5), range(5)), 2, 2)
|
|
332
|
+
sage: p.row(index)
|
|
333
|
+
([1, 2, 3, 4], [1, 2, 3, 4])
|
|
334
|
+
sage: p.row_bounds(index)
|
|
335
|
+
(2, 2)
|
|
336
|
+
sage: p.add_linear_constraint( zip(range(5), range(5)), 1, 1, name='foo')
|
|
337
|
+
sage: p.row_name(1)
|
|
338
|
+
'constraint_1'
|
|
339
|
+
"""
|
|
340
|
+
last = len(self.Matrix)
|
|
341
|
+
self.Matrix.append([])
|
|
342
|
+
for i in range(len(self.objective_coefficients)):
|
|
343
|
+
self.Matrix[last].append(0.0)
|
|
344
|
+
for a in coefficients:
|
|
345
|
+
self.Matrix[last][a[0]] = a[1]
|
|
346
|
+
|
|
347
|
+
self.row_lower_bound.append(lower_bound)
|
|
348
|
+
self.row_upper_bound.append(upper_bound)
|
|
349
|
+
|
|
350
|
+
terms = [v * self.variables[i] for i, v in coefficients]
|
|
351
|
+
if terms:
|
|
352
|
+
expr = AddExpression(terms)
|
|
353
|
+
else:
|
|
354
|
+
expr = Constant(0)
|
|
355
|
+
constraints = list(self.problem.constraints)
|
|
356
|
+
if lower_bound is not None and lower_bound == upper_bound:
|
|
357
|
+
constraints.append(expr == upper_bound)
|
|
358
|
+
elif lower_bound is not None:
|
|
359
|
+
constraints.append(lower_bound <= expr)
|
|
360
|
+
elif upper_bound is not None:
|
|
361
|
+
constraints.append(expr <= upper_bound)
|
|
362
|
+
self.constraint_names.append(name)
|
|
363
|
+
self.problem = cvxpy.Problem(self.problem.objective, constraints)
|
|
364
|
+
|
|
365
|
+
cpdef add_col(self, indices, coeffs):
|
|
366
|
+
"""
|
|
367
|
+
Add a column.
|
|
368
|
+
|
|
369
|
+
INPUT:
|
|
370
|
+
|
|
371
|
+
- ``indices`` -- list of integers; this list contains the
|
|
372
|
+
indices of the constraints in which the variable's
|
|
373
|
+
coefficient is nonzero
|
|
374
|
+
|
|
375
|
+
- ``coeffs`` -- list of real values; associates a coefficient
|
|
376
|
+
to the variable in each of the constraints in which it
|
|
377
|
+
appears. Namely, the i-th entry of ``coeffs`` corresponds to
|
|
378
|
+
the coefficient of the variable in the constraint
|
|
379
|
+
represented by the i-th entry in ``indices``.
|
|
380
|
+
|
|
381
|
+
.. NOTE::
|
|
382
|
+
|
|
383
|
+
``indices`` and ``coeffs`` are expected to be of the same
|
|
384
|
+
length.
|
|
385
|
+
|
|
386
|
+
EXAMPLES::
|
|
387
|
+
|
|
388
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
389
|
+
sage: p = get_solver(solver='CVXPY')
|
|
390
|
+
sage: p.ncols()
|
|
391
|
+
0
|
|
392
|
+
sage: p.nrows()
|
|
393
|
+
0
|
|
394
|
+
sage: p.add_linear_constraints(5, 0, None)
|
|
395
|
+
sage: p.add_col(list(range(5)), list(range(5)))
|
|
396
|
+
Traceback (most recent call last):
|
|
397
|
+
...
|
|
398
|
+
NotImplementedError: adding coefficients to inequalities is ambiguous because cvxpy rewrites all inequalities as <=
|
|
399
|
+
sage: p.nrows()
|
|
400
|
+
5
|
|
401
|
+
"""
|
|
402
|
+
for i in range(len(self.Matrix)):
|
|
403
|
+
self.Matrix[i].append(0)
|
|
404
|
+
for i in range(len(indices)):
|
|
405
|
+
self.Matrix[indices[i]][len(self.Matrix[indices[i]]) - 1] = coeffs[i]
|
|
406
|
+
|
|
407
|
+
self.col_lower_bound.append(None)
|
|
408
|
+
self.col_upper_bound.append(None)
|
|
409
|
+
#self.objective_coefficients.append(0) goes to "self.add_variable"
|
|
410
|
+
self.add_variable(coefficients=zip(indices, coeffs))
|
|
411
|
+
|
|
412
|
+
cpdef set_objective(self, list coeff, d=0.0):
|
|
413
|
+
"""
|
|
414
|
+
Set the objective function.
|
|
415
|
+
|
|
416
|
+
INPUT:
|
|
417
|
+
|
|
418
|
+
- ``coeff`` -- list of real values, whose i-th element is the
|
|
419
|
+
coefficient of the i-th variable in the objective function
|
|
420
|
+
|
|
421
|
+
- ``d`` -- double; the constant term in the linear function (set to `0`
|
|
422
|
+
by default)
|
|
423
|
+
|
|
424
|
+
EXAMPLES::
|
|
425
|
+
|
|
426
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
427
|
+
sage: p = get_solver(solver='CVXPY')
|
|
428
|
+
sage: p.add_variables(5)
|
|
429
|
+
4
|
|
430
|
+
sage: p.set_objective([1, 1, 2, 1, 3])
|
|
431
|
+
sage: [p.objective_coefficient(x) for x in range(5)]
|
|
432
|
+
[1.0, 1.0, 2.0, 1.0, 3.0]
|
|
433
|
+
"""
|
|
434
|
+
if self.variables:
|
|
435
|
+
expr = AddExpression([c * x for c, x in zip(coeff, self.variables)])
|
|
436
|
+
for i in range(len(coeff)):
|
|
437
|
+
self.objective_coefficients[i] = float(coeff[i])
|
|
438
|
+
else:
|
|
439
|
+
expr = Constant(0)
|
|
440
|
+
objective = type(self.problem.objective)(expr)
|
|
441
|
+
constraints = list(self.problem.constraints)
|
|
442
|
+
self.problem = cvxpy.Problem(objective, constraints)
|
|
443
|
+
self.obj_constant_term = d
|
|
444
|
+
|
|
445
|
+
cpdef set_sense(self, int sense):
|
|
446
|
+
"""
|
|
447
|
+
Set the direction (maximization/minimization).
|
|
448
|
+
|
|
449
|
+
INPUT:
|
|
450
|
+
|
|
451
|
+
- ``sense`` -- integer:
|
|
452
|
+
|
|
453
|
+
* +1 => Maximization
|
|
454
|
+
* -1 => Minimization
|
|
455
|
+
|
|
456
|
+
EXAMPLES::
|
|
457
|
+
|
|
458
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
459
|
+
sage: p = get_solver(solver='CVXPY')
|
|
460
|
+
sage: p.is_maximization()
|
|
461
|
+
True
|
|
462
|
+
sage: p.set_sense(-1)
|
|
463
|
+
sage: p.is_maximization()
|
|
464
|
+
False
|
|
465
|
+
"""
|
|
466
|
+
expr = self.problem.objective.args[0]
|
|
467
|
+
if sense == 1:
|
|
468
|
+
objective = cvxpy.Maximize(expr)
|
|
469
|
+
else:
|
|
470
|
+
objective = cvxpy.Minimize(expr)
|
|
471
|
+
self.problem = cvxpy.Problem(objective, self.problem.constraints)
|
|
472
|
+
|
|
473
|
+
cpdef objective_coefficient(self, int variable, coeff=None):
|
|
474
|
+
"""
|
|
475
|
+
Set or get the coefficient of a variable in the objective function.
|
|
476
|
+
|
|
477
|
+
INPUT:
|
|
478
|
+
|
|
479
|
+
- ``variable`` -- integer; the variable's id
|
|
480
|
+
|
|
481
|
+
- ``coeff`` -- double; its coefficient or ``None`` for
|
|
482
|
+
reading (default: ``None``)
|
|
483
|
+
|
|
484
|
+
EXAMPLES::
|
|
485
|
+
|
|
486
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
487
|
+
sage: p = get_solver(solver='CVXPY')
|
|
488
|
+
sage: p.add_variable()
|
|
489
|
+
0
|
|
490
|
+
sage: p.objective_coefficient(0)
|
|
491
|
+
0.0
|
|
492
|
+
sage: p.objective_coefficient(0, 2)
|
|
493
|
+
sage: p.objective_coefficient(0)
|
|
494
|
+
2
|
|
495
|
+
"""
|
|
496
|
+
if coeff is not None:
|
|
497
|
+
self.objective_coefficients[variable] = coeff
|
|
498
|
+
expr = self.problem.objective.args[0] + coeff * self.variables[variable]
|
|
499
|
+
objective = type(self.problem.objective)(expr)
|
|
500
|
+
constraints = list(self.problem.constraints)
|
|
501
|
+
self.problem = cvxpy.Problem(objective, constraints)
|
|
502
|
+
else:
|
|
503
|
+
if variable < len(self.objective_coefficients):
|
|
504
|
+
return self.objective_coefficients[variable]
|
|
505
|
+
else:
|
|
506
|
+
return 0
|
|
507
|
+
|
|
508
|
+
cpdef int solve(self) except -1:
|
|
509
|
+
"""
|
|
510
|
+
Solve the problem.
|
|
511
|
+
|
|
512
|
+
.. NOTE::
|
|
513
|
+
|
|
514
|
+
This method raises ``MIPSolverException`` exceptions when
|
|
515
|
+
the solution cannot be computed for any reason (none
|
|
516
|
+
exists, or the LP solver was not able to find it, etc...)
|
|
517
|
+
|
|
518
|
+
EXAMPLES::
|
|
519
|
+
|
|
520
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
521
|
+
sage: p = get_solver(solver='CVXPY')
|
|
522
|
+
sage: p.add_linear_constraints(5, 0, None)
|
|
523
|
+
sage: p.add_col(list(range(5)), list(range(5)))
|
|
524
|
+
Traceback (most recent call last):
|
|
525
|
+
...
|
|
526
|
+
NotImplementedError: adding coefficients to inequalities is ambiguous because cvxpy rewrites all inequalities as <=
|
|
527
|
+
sage: p.solve()
|
|
528
|
+
0
|
|
529
|
+
sage: p.objective_coefficient(0,1)
|
|
530
|
+
sage: p.solve()
|
|
531
|
+
Traceback (most recent call last):
|
|
532
|
+
...
|
|
533
|
+
MIPSolverException: ...
|
|
534
|
+
"""
|
|
535
|
+
try:
|
|
536
|
+
self.problem.solve(solver=self._cvxpy_solver, **self._cvxpy_solver_args)
|
|
537
|
+
except Exception as e:
|
|
538
|
+
raise MIPSolverException(f"cvxpy.Problem.solve raised exception: {e}")
|
|
539
|
+
status = self.problem.status
|
|
540
|
+
if 'optimal' in status:
|
|
541
|
+
return 0
|
|
542
|
+
if 'infeasible' in status:
|
|
543
|
+
raise MIPSolverException(f"cvxpy.Problem.solve: Problem has no feasible solution")
|
|
544
|
+
if 'unbounded' in status:
|
|
545
|
+
raise MIPSolverException(f"cvxpy.Problem.solve: Problem is unbounded")
|
|
546
|
+
raise MIPSolverException(f"cvxpy.Problem.solve reported an unknown problem status: {status}")
|
|
547
|
+
|
|
548
|
+
cpdef get_objective_value(self):
|
|
549
|
+
"""
|
|
550
|
+
Return the value of the objective function.
|
|
551
|
+
|
|
552
|
+
.. NOTE::
|
|
553
|
+
|
|
554
|
+
Behavior is undefined unless ``solve`` has been called before.
|
|
555
|
+
|
|
556
|
+
EXAMPLES::
|
|
557
|
+
|
|
558
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
559
|
+
sage: p = get_solver(solver='CVXPY')
|
|
560
|
+
sage: p.add_variables(2)
|
|
561
|
+
1
|
|
562
|
+
sage: p.add_linear_constraint([(0,1), (1,2)], None, 3)
|
|
563
|
+
sage: p.set_objective([2, 5])
|
|
564
|
+
sage: p.solve()
|
|
565
|
+
0
|
|
566
|
+
sage: p.get_objective_value() # abs tol 1e-7
|
|
567
|
+
7.5
|
|
568
|
+
sage: p.get_variable_value(0) # abs tol 1e-7
|
|
569
|
+
0.0
|
|
570
|
+
sage: p.get_variable_value(1) # abs tol 1e-7
|
|
571
|
+
1.5
|
|
572
|
+
"""
|
|
573
|
+
return self.problem.value + self.obj_constant_term
|
|
574
|
+
|
|
575
|
+
cpdef get_variable_value(self, int variable):
|
|
576
|
+
"""
|
|
577
|
+
Return the value of a variable given by the solver.
|
|
578
|
+
|
|
579
|
+
.. NOTE::
|
|
580
|
+
|
|
581
|
+
Behavior is undefined unless ``solve`` has been called before.
|
|
582
|
+
|
|
583
|
+
EXAMPLES::
|
|
584
|
+
|
|
585
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
586
|
+
sage: p = get_solver(solver='CVXPY')
|
|
587
|
+
sage: p.add_variables(2)
|
|
588
|
+
1
|
|
589
|
+
sage: p.add_linear_constraint([(0,1), (1, 2)], None, 3)
|
|
590
|
+
sage: p.set_objective([2, 5])
|
|
591
|
+
sage: p.solve()
|
|
592
|
+
0
|
|
593
|
+
sage: p.get_objective_value() # abs tol 1e-7
|
|
594
|
+
7.5
|
|
595
|
+
sage: p.get_variable_value(0) # abs tol 1e-7
|
|
596
|
+
0.0
|
|
597
|
+
sage: p.get_variable_value(1) # abs tol 1e-7
|
|
598
|
+
1.5
|
|
599
|
+
"""
|
|
600
|
+
return float(self.variables[variable].value)
|
|
601
|
+
|
|
602
|
+
cpdef int ncols(self) noexcept:
|
|
603
|
+
"""
|
|
604
|
+
Return the number of columns/variables.
|
|
605
|
+
|
|
606
|
+
EXAMPLES::
|
|
607
|
+
|
|
608
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
609
|
+
sage: p = get_solver(solver='CVXPY')
|
|
610
|
+
sage: p.ncols()
|
|
611
|
+
0
|
|
612
|
+
sage: p.add_variables(2)
|
|
613
|
+
1
|
|
614
|
+
sage: p.ncols()
|
|
615
|
+
2
|
|
616
|
+
"""
|
|
617
|
+
return len(self.variables)
|
|
618
|
+
|
|
619
|
+
cpdef int nrows(self) noexcept:
|
|
620
|
+
"""
|
|
621
|
+
Return the number of rows/constraints.
|
|
622
|
+
|
|
623
|
+
EXAMPLES::
|
|
624
|
+
|
|
625
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
626
|
+
sage: p = get_solver(solver='CVXPY')
|
|
627
|
+
sage: p.nrows()
|
|
628
|
+
0
|
|
629
|
+
sage: p.add_linear_constraints(2, 0, None)
|
|
630
|
+
sage: p.nrows()
|
|
631
|
+
2
|
|
632
|
+
"""
|
|
633
|
+
return len(self.problem.constraints)
|
|
634
|
+
|
|
635
|
+
cpdef bint is_maximization(self) noexcept:
|
|
636
|
+
"""
|
|
637
|
+
Test whether the problem is a maximization
|
|
638
|
+
|
|
639
|
+
EXAMPLES::
|
|
640
|
+
|
|
641
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
642
|
+
sage: p = get_solver(solver='CVXPY')
|
|
643
|
+
sage: p.is_maximization()
|
|
644
|
+
True
|
|
645
|
+
sage: p.set_sense(-1)
|
|
646
|
+
sage: p.is_maximization()
|
|
647
|
+
False
|
|
648
|
+
"""
|
|
649
|
+
return isinstance(self.problem.objective, cvxpy.Maximize)
|
|
650
|
+
|
|
651
|
+
cpdef problem_name(self, name=None):
|
|
652
|
+
"""
|
|
653
|
+
Return or define the problem's name.
|
|
654
|
+
|
|
655
|
+
INPUT:
|
|
656
|
+
|
|
657
|
+
- ``name`` -- string; the problem's name. When set to
|
|
658
|
+
``None`` (default), the method returns the problem's name.
|
|
659
|
+
|
|
660
|
+
EXAMPLES::
|
|
661
|
+
|
|
662
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
663
|
+
sage: p = get_solver(solver='CVXPY')
|
|
664
|
+
sage: p.problem_name("There_once_was_a_french_fry")
|
|
665
|
+
sage: print(p.problem_name())
|
|
666
|
+
There_once_was_a_french_fry
|
|
667
|
+
"""
|
|
668
|
+
if name is None:
|
|
669
|
+
if self.prob_name is not None:
|
|
670
|
+
return self.prob_name
|
|
671
|
+
else:
|
|
672
|
+
return ""
|
|
673
|
+
else:
|
|
674
|
+
self.prob_name = str(name)
|
|
675
|
+
|
|
676
|
+
cpdef row(self, int i):
|
|
677
|
+
"""
|
|
678
|
+
Return a row.
|
|
679
|
+
|
|
680
|
+
INPUT:
|
|
681
|
+
|
|
682
|
+
- ``index`` -- integer; the constraint's id
|
|
683
|
+
|
|
684
|
+
OUTPUT:
|
|
685
|
+
|
|
686
|
+
A pair ``(indices, coeffs)`` where ``indices`` lists the
|
|
687
|
+
entries whose coefficient is nonzero, and to which ``coeffs``
|
|
688
|
+
associates their coefficient on the model of the
|
|
689
|
+
``add_linear_constraint`` method.
|
|
690
|
+
|
|
691
|
+
EXAMPLES::
|
|
692
|
+
|
|
693
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
694
|
+
sage: p = get_solver(solver='CVXPY')
|
|
695
|
+
sage: p.add_variables(5)
|
|
696
|
+
4
|
|
697
|
+
sage: index = p.nrows()
|
|
698
|
+
sage: p.add_linear_constraint(zip(range(5), range(5)), 2, 2)
|
|
699
|
+
sage: p.row(index)
|
|
700
|
+
([1, 2, 3, 4], [1, 2, 3, 4])
|
|
701
|
+
sage: p.row_bounds(index)
|
|
702
|
+
(2, 2)
|
|
703
|
+
"""
|
|
704
|
+
idx = []
|
|
705
|
+
coef = []
|
|
706
|
+
for j in range(len(self.Matrix[i])):
|
|
707
|
+
if self.Matrix[i][j] != 0:
|
|
708
|
+
idx.append(j)
|
|
709
|
+
coef.append(self.Matrix[i][j])
|
|
710
|
+
return (idx, coef)
|
|
711
|
+
|
|
712
|
+
cpdef row_bounds(self, int index):
|
|
713
|
+
"""
|
|
714
|
+
Return the bounds of a specific constraint.
|
|
715
|
+
|
|
716
|
+
INPUT:
|
|
717
|
+
|
|
718
|
+
- ``index`` -- integer; the constraint's id
|
|
719
|
+
|
|
720
|
+
OUTPUT:
|
|
721
|
+
|
|
722
|
+
A pair ``(lower_bound, upper_bound)``. Each of them can be set
|
|
723
|
+
to ``None`` if the constraint is not bounded in the
|
|
724
|
+
corresponding direction, and is a real value otherwise.
|
|
725
|
+
|
|
726
|
+
EXAMPLES::
|
|
727
|
+
|
|
728
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
729
|
+
sage: p = get_solver(solver='CVXPY')
|
|
730
|
+
sage: p.add_variables(5)
|
|
731
|
+
4
|
|
732
|
+
sage: index = p.nrows()
|
|
733
|
+
sage: p.add_linear_constraint(zip(range(5), range(5)), 2, 2)
|
|
734
|
+
sage: p.row(index)
|
|
735
|
+
([1, 2, 3, 4], [1, 2, 3, 4])
|
|
736
|
+
sage: p.row_bounds(index)
|
|
737
|
+
(2, 2)
|
|
738
|
+
"""
|
|
739
|
+
return (self.row_lower_bound[index], self.row_upper_bound[index])
|
|
740
|
+
|
|
741
|
+
cpdef col_bounds(self, int index):
|
|
742
|
+
"""
|
|
743
|
+
Return the bounds of a specific variable.
|
|
744
|
+
|
|
745
|
+
INPUT:
|
|
746
|
+
|
|
747
|
+
- ``index`` -- integer; the variable's id
|
|
748
|
+
|
|
749
|
+
OUTPUT:
|
|
750
|
+
|
|
751
|
+
A pair ``(lower_bound, upper_bound)``. Each of them can be set
|
|
752
|
+
to ``None`` if the variable is not bounded in the
|
|
753
|
+
corresponding direction, and is a real value otherwise.
|
|
754
|
+
|
|
755
|
+
EXAMPLES::
|
|
756
|
+
|
|
757
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
758
|
+
sage: p = get_solver(solver='CVXPY')
|
|
759
|
+
sage: p.add_variable()
|
|
760
|
+
0
|
|
761
|
+
sage: p.col_bounds(0)
|
|
762
|
+
(0.0, None)
|
|
763
|
+
sage: p.variable_upper_bound(0, 5)
|
|
764
|
+
sage: p.col_bounds(0)
|
|
765
|
+
(0.0, 5)
|
|
766
|
+
"""
|
|
767
|
+
return (self.col_lower_bound[index], self.col_upper_bound[index])
|
|
768
|
+
|
|
769
|
+
cpdef bint is_variable_binary(self, int index) noexcept:
|
|
770
|
+
"""
|
|
771
|
+
Test whether the given variable is of binary type.
|
|
772
|
+
|
|
773
|
+
INPUT:
|
|
774
|
+
|
|
775
|
+
- ``index`` -- integer; the variable's id
|
|
776
|
+
|
|
777
|
+
EXAMPLES::
|
|
778
|
+
|
|
779
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
780
|
+
sage: p = get_solver(solver='CVXPY')
|
|
781
|
+
sage: p.ncols()
|
|
782
|
+
0
|
|
783
|
+
sage: p.add_variable()
|
|
784
|
+
0
|
|
785
|
+
sage: p.is_variable_binary(0)
|
|
786
|
+
False
|
|
787
|
+
"""
|
|
788
|
+
return bool(self.variables[index].boolean_idx)
|
|
789
|
+
|
|
790
|
+
cpdef bint is_variable_integer(self, int index) noexcept:
|
|
791
|
+
"""
|
|
792
|
+
Test whether the given variable is of integer type.
|
|
793
|
+
|
|
794
|
+
INPUT:
|
|
795
|
+
|
|
796
|
+
- ``index`` -- integer; the variable's id
|
|
797
|
+
|
|
798
|
+
EXAMPLES::
|
|
799
|
+
|
|
800
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
801
|
+
sage: p = get_solver(solver='CVXPY')
|
|
802
|
+
sage: p.ncols()
|
|
803
|
+
0
|
|
804
|
+
sage: p.add_variable()
|
|
805
|
+
0
|
|
806
|
+
sage: p.is_variable_integer(0)
|
|
807
|
+
False
|
|
808
|
+
"""
|
|
809
|
+
return bool(self.variables[index].integer_idx)
|
|
810
|
+
|
|
811
|
+
cpdef bint is_variable_continuous(self, int index) noexcept:
|
|
812
|
+
"""
|
|
813
|
+
Test whether the given variable is of continuous/real type.
|
|
814
|
+
|
|
815
|
+
INPUT:
|
|
816
|
+
|
|
817
|
+
- ``index`` -- integer; the variable's id
|
|
818
|
+
|
|
819
|
+
EXAMPLES::
|
|
820
|
+
|
|
821
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
822
|
+
sage: p = get_solver(solver='CVXPY')
|
|
823
|
+
sage: p.ncols()
|
|
824
|
+
0
|
|
825
|
+
sage: p.add_variable()
|
|
826
|
+
0
|
|
827
|
+
sage: p.is_variable_continuous(0)
|
|
828
|
+
True
|
|
829
|
+
"""
|
|
830
|
+
return not self.is_variable_binary(index) and not self.is_variable_integer(index)
|
|
831
|
+
|
|
832
|
+
cpdef row_name(self, int index):
|
|
833
|
+
"""
|
|
834
|
+
Return the ``index``-th row name.
|
|
835
|
+
|
|
836
|
+
INPUT:
|
|
837
|
+
|
|
838
|
+
- ``index`` -- integer; the row's id
|
|
839
|
+
|
|
840
|
+
EXAMPLES::
|
|
841
|
+
|
|
842
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
843
|
+
sage: p = get_solver(solver='CVXPY')
|
|
844
|
+
sage: p.add_linear_constraint([], 2, 2)
|
|
845
|
+
sage: p.row_name(0)
|
|
846
|
+
'constraint_0'
|
|
847
|
+
"""
|
|
848
|
+
return self.constraint_names[index] or ("constraint_" + repr(index))
|
|
849
|
+
|
|
850
|
+
cpdef col_name(self, int index):
|
|
851
|
+
"""
|
|
852
|
+
Return the ``index``-th col name.
|
|
853
|
+
|
|
854
|
+
INPUT:
|
|
855
|
+
|
|
856
|
+
- ``index`` -- integer; the col's id
|
|
857
|
+
|
|
858
|
+
- ``name`` -- (``char *``) its name; when set to ``NULL``
|
|
859
|
+
(default), the method returns the current name
|
|
860
|
+
|
|
861
|
+
EXAMPLES::
|
|
862
|
+
|
|
863
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
864
|
+
sage: p = get_solver(solver='CVXPY')
|
|
865
|
+
sage: p.add_variable()
|
|
866
|
+
0
|
|
867
|
+
sage: p.col_name(0)
|
|
868
|
+
'x_0'
|
|
869
|
+
"""
|
|
870
|
+
return self.variables[index].name()
|
|
871
|
+
|
|
872
|
+
cpdef variable_upper_bound(self, int index, value=False):
|
|
873
|
+
"""
|
|
874
|
+
Return or define the upper bound on a variable.
|
|
875
|
+
|
|
876
|
+
INPUT:
|
|
877
|
+
|
|
878
|
+
- ``index`` -- integer; the variable's id
|
|
879
|
+
|
|
880
|
+
- ``value`` -- real value, or ``None`` to mean that the
|
|
881
|
+
variable has not upper bound. When set to ``None``
|
|
882
|
+
(default), the method returns the current value.
|
|
883
|
+
|
|
884
|
+
EXAMPLES::
|
|
885
|
+
|
|
886
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
887
|
+
sage: p = get_solver(solver='CVXPY')
|
|
888
|
+
sage: p.add_variable()
|
|
889
|
+
0
|
|
890
|
+
sage: p.col_bounds(0)
|
|
891
|
+
(0.0, None)
|
|
892
|
+
sage: p.variable_upper_bound(0, 5)
|
|
893
|
+
sage: p.col_bounds(0)
|
|
894
|
+
(0.0, 5)
|
|
895
|
+
sage: p.variable_upper_bound(0, None)
|
|
896
|
+
sage: p.col_bounds(0)
|
|
897
|
+
(0.0, None)
|
|
898
|
+
"""
|
|
899
|
+
if value is not False:
|
|
900
|
+
self.col_upper_bound[index] = value
|
|
901
|
+
else:
|
|
902
|
+
return self.col_upper_bound[index]
|
|
903
|
+
|
|
904
|
+
cpdef variable_lower_bound(self, int index, value=False):
|
|
905
|
+
"""
|
|
906
|
+
Return or define the lower bound on a variable.
|
|
907
|
+
|
|
908
|
+
INPUT:
|
|
909
|
+
|
|
910
|
+
- ``index`` -- integer; the variable's id
|
|
911
|
+
|
|
912
|
+
- ``value`` -- real value, or ``None`` to mean that the
|
|
913
|
+
variable has not lower bound. When set to ``None``
|
|
914
|
+
(default), the method returns the current value.
|
|
915
|
+
|
|
916
|
+
EXAMPLES::
|
|
917
|
+
|
|
918
|
+
sage: from sage.numerical.backends.generic_backend import get_solver
|
|
919
|
+
sage: p = get_solver(solver='CVXPY')
|
|
920
|
+
sage: p.add_variable()
|
|
921
|
+
0
|
|
922
|
+
sage: p.col_bounds(0)
|
|
923
|
+
(0.0, None)
|
|
924
|
+
sage: p.variable_lower_bound(0, 5)
|
|
925
|
+
sage: p.col_bounds(0)
|
|
926
|
+
(5, None)
|
|
927
|
+
sage: p.variable_lower_bound(0, None)
|
|
928
|
+
sage: p.col_bounds(0)
|
|
929
|
+
(None, None)
|
|
930
|
+
"""
|
|
931
|
+
if value is not False:
|
|
932
|
+
self.col_lower_bound[index] = value
|
|
933
|
+
else:
|
|
934
|
+
return self.col_lower_bound[index]
|