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
sage/numerical/sdp.pyx
ADDED
|
@@ -0,0 +1,1433 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-polyhedra
|
|
2
|
+
r"""
|
|
3
|
+
Semidefinite Programming
|
|
4
|
+
|
|
5
|
+
A semidefinite program (:wikipedia:`SDP <Semidefinite_programming>`)
|
|
6
|
+
is an optimization problem (:wikipedia:`Optimization_(mathematics)>`)
|
|
7
|
+
of the following form
|
|
8
|
+
|
|
9
|
+
.. MATH::
|
|
10
|
+
|
|
11
|
+
\min \sum_{i,j=1}^n C_{ij}X_{ij} & \qquad \text{(Dual problem)}\\
|
|
12
|
+
\text{Subject to:} & \sum_{i,j=1}^n A_{ijk}X_{ij} = b_k, \qquad k=1\dots m\\
|
|
13
|
+
&X \succeq 0
|
|
14
|
+
|
|
15
|
+
where the `X_{ij}`, `1 \leq i,j \leq n` are `n^2` variables satisfying the symmetry
|
|
16
|
+
conditions `x_{ij} = x_{ji}` for all `i,j`, the `C_{ij}=C_{ji}`, `A_{ijk}=A_{kji}` and `b_k`
|
|
17
|
+
are real coefficients, and `X` is positive semidefinite, i.e., all the eigenvalues of `X` are nonnegative.
|
|
18
|
+
The closely related dual problem of this one is the following, where we denote by
|
|
19
|
+
`A_k` the matrix `(A_{kij})` and by `C` the matrix `(C_{ij})`,
|
|
20
|
+
|
|
21
|
+
.. MATH::
|
|
22
|
+
|
|
23
|
+
\max \sum_k b_k x_k & \qquad \text{(Primal problem)}\\
|
|
24
|
+
\text{Subject to:} & \sum_k x_k A_k \preceq C.
|
|
25
|
+
|
|
26
|
+
Here `(x_1,...,x_m)` is a vector of scalar variables.
|
|
27
|
+
A wide variety of problems in optimization can be formulated in one of these two standard
|
|
28
|
+
forms. Then, solvers are able to calculate an approximation to a solution.
|
|
29
|
+
Here we refer to the latter problem as primal, and to the former problem as dual.
|
|
30
|
+
The optimal value of the dual is always at least the
|
|
31
|
+
optimal value of the primal, and usually (although not always) they are equal.
|
|
32
|
+
|
|
33
|
+
For instance, suppose you want to maximize `x_1 - x_0` subject to
|
|
34
|
+
|
|
35
|
+
.. MATH::
|
|
36
|
+
|
|
37
|
+
\left( \begin{array}{cc} 1 & 2 \\ 2 & 3 \end{array} \right) x_0 +
|
|
38
|
+
\left( \begin{array}{cc} 3 & 4 \\ 4 & 5 \end{array} \right) x_1 \preceq
|
|
39
|
+
\left( \begin{array}{cc} 5 & 6 \\ 6 & 7 \end{array} \right),\quad
|
|
40
|
+
\left( \begin{array}{cc} 1 & 1 \\ 1 & 1 \end{array} \right) x_0 +
|
|
41
|
+
\left( \begin{array}{cc} 2 & 2 \\ 2 & 2 \end{array} \right) x_1 \preceq
|
|
42
|
+
\left( \begin{array}{cc} 3 & 3 \\ 3 & 3 \end{array} \right),
|
|
43
|
+
\quad x_0\geq 0, x_1\geq 0.
|
|
44
|
+
|
|
45
|
+
An SDP can give you an answer to the problem above. Here is how it's done:
|
|
46
|
+
|
|
47
|
+
#. You have to create an instance of :class:`SemidefiniteProgram`.
|
|
48
|
+
#. Create a dictionary `x` of integer variables via :meth:`~SemidefiniteProgram.new_variable`,
|
|
49
|
+
for example doing ``x = p.new_variable()`` if ``p`` is the name of the SDP instance.
|
|
50
|
+
#. Add those two matrix inequalities as inequality constraints via
|
|
51
|
+
:meth:`~SemidefiniteProgram.add_constraint`.
|
|
52
|
+
#. Add another matrix inequality to specify nonnegativity of `x`.
|
|
53
|
+
#. Specify the objective function via :meth:`~SemidefiniteProgram.set_objective`.
|
|
54
|
+
In our case it is `x_1 - x_0`. If it
|
|
55
|
+
is a pure constraint satisfaction problem, specify it as ``None``.
|
|
56
|
+
#. To check if everything is set up correctly, you can print the problem via
|
|
57
|
+
:meth:`show <sage.numerical.sdp.SemidefiniteProgram.show>`.
|
|
58
|
+
#. :meth:`Solve <sage.numerical.sdp.SemidefiniteProgram.solve>` it and print the solution.
|
|
59
|
+
|
|
60
|
+
The following example shows all these steps::
|
|
61
|
+
|
|
62
|
+
sage: p = SemidefiniteProgram()
|
|
63
|
+
sage: x = p.new_variable()
|
|
64
|
+
sage: p.set_objective(x[1] - x[0])
|
|
65
|
+
sage: a1 = matrix([[1, 2.], [2., 3.]])
|
|
66
|
+
sage: a2 = matrix([[3, 4.], [4., 5.]])
|
|
67
|
+
sage: a3 = matrix([[5, 6.], [6., 7.]])
|
|
68
|
+
sage: b1 = matrix([[1, 1.], [1., 1.]])
|
|
69
|
+
sage: b2 = matrix([[2, 2.], [2., 2.]])
|
|
70
|
+
sage: b3 = matrix([[3, 3.], [3., 3.]])
|
|
71
|
+
sage: c1 = matrix([[1.0, 0],[0,0]],sparse=True)
|
|
72
|
+
sage: c2 = matrix([[0.0, 0],[0,1]],sparse=True)
|
|
73
|
+
sage: p.add_constraint(a1*x[0] + a2*x[1] <= a3)
|
|
74
|
+
sage: p.add_constraint(b1*x[0] + b2*x[1] <= b3)
|
|
75
|
+
sage: p.add_constraint(c1*x[0] + c2*x[1] >= matrix.zero(2,2,sparse=True))
|
|
76
|
+
|
|
77
|
+
sage: # needs cvxopt
|
|
78
|
+
sage: p.solver_parameter("show_progress", True)
|
|
79
|
+
sage: opt = p.solve()
|
|
80
|
+
pcost dcost gap pres dres k/t
|
|
81
|
+
0: ...
|
|
82
|
+
...
|
|
83
|
+
Optimal solution found.
|
|
84
|
+
sage: print('Objective Value: {}'.format(N(opt,3)))
|
|
85
|
+
Objective Value: 1.0
|
|
86
|
+
sage: [N(x, 3) for x in sorted(p.get_values(x).values())]
|
|
87
|
+
[3.0e-8, 1.0]
|
|
88
|
+
sage: p.show()
|
|
89
|
+
Maximization:
|
|
90
|
+
x_0 - x_1
|
|
91
|
+
Constraints:
|
|
92
|
+
constraint_0: [3.0 4.0][4.0 5.0]x_0 + [1.0 2.0][2.0 3.0]x_1 <= [5.0 6.0][6.0 7.0]
|
|
93
|
+
constraint_1: [2.0 2.0][2.0 2.0]x_0 + [1.0 1.0][1.0 1.0]x_1 <= [3.0 3.0][3.0 3.0]
|
|
94
|
+
constraint_2: [ 0.0 0.0][ 0.0 -1.0]x_0 + [-1.0 0.0][ 0.0 0.0]x_1 <= [0 0][0 0]
|
|
95
|
+
Variables:
|
|
96
|
+
x_0, x_1
|
|
97
|
+
|
|
98
|
+
Most solvers, e.g. the default Sage SDP solver CVXOPT, solve simultaneously the pair
|
|
99
|
+
of primal and dual problems. Thus we can get the optimizer `X` of the dual problem
|
|
100
|
+
as follows, as diagonal blocks, one per each constraint, via :meth:`~SemidefiniteProgram.dual_variable`.
|
|
101
|
+
E.g.::
|
|
102
|
+
|
|
103
|
+
sage: p.dual_variable(1) # rel tol 2e-03 # needs cvxopt
|
|
104
|
+
[ 85555.0 -85555.0]
|
|
105
|
+
[-85555.0 85555.0]
|
|
106
|
+
|
|
107
|
+
We can see that the optimal value of the dual is equal (up to numerical noise) to `opt`.::
|
|
108
|
+
|
|
109
|
+
sage: opt - ((p.dual_variable(0)*a3).trace() # tol 8e-08 # needs cvxopt
|
|
110
|
+
....: + (p.dual_variable(1)*b3).trace())
|
|
111
|
+
0.0
|
|
112
|
+
|
|
113
|
+
Dual variable blocks at optimality are orthogonal to "slack variables", that is,
|
|
114
|
+
matrices `C-\sum_k x_k A_k`, cf. (Primal problem) above, available via
|
|
115
|
+
:meth:`~SemidefiniteProgram.slack`. E.g.::
|
|
116
|
+
|
|
117
|
+
sage: (p.slack(0)*p.dual_variable(0)).trace() # tol 2e-07 # needs cvxopt
|
|
118
|
+
0.0
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
More interesting example, the :func:`Lovasz theta <sage.graphs.lovasz_theta.lovasz_theta>` of the 7-gon::
|
|
122
|
+
|
|
123
|
+
sage: # needs sage.graphs
|
|
124
|
+
sage: c = graphs.CycleGraph(7)
|
|
125
|
+
sage: c2 = c.distance_graph(2).adjacency_matrix()
|
|
126
|
+
sage: c3 = c.distance_graph(3).adjacency_matrix()
|
|
127
|
+
sage: p.<y> = SemidefiniteProgram()
|
|
128
|
+
sage: p.add_constraint((1/7)*matrix.identity(7)>=-y[0]*c2-y[1]*c3)
|
|
129
|
+
sage: p.set_objective(y[0]*(c2**2).trace()+y[1]*(c3**2).trace())
|
|
130
|
+
sage: x = p.solve(); x + 1 # needs cvxopt
|
|
131
|
+
3.31766...
|
|
132
|
+
|
|
133
|
+
Unlike in the previous example, the slack variable is very far from 0::
|
|
134
|
+
|
|
135
|
+
sage: p.slack(0).trace() # tol 1e-14 # needs cvxopt sage.graphs
|
|
136
|
+
1.0
|
|
137
|
+
|
|
138
|
+
The default CVXOPT backend computes with the Real Double Field, for example::
|
|
139
|
+
|
|
140
|
+
sage: # needs cvxopt
|
|
141
|
+
sage: p = SemidefiniteProgram(solver='cvxopt')
|
|
142
|
+
sage: p.base_ring()
|
|
143
|
+
Real Double Field
|
|
144
|
+
sage: x = p.new_variable()
|
|
145
|
+
sage: 0.5 + 3/2*x[1]
|
|
146
|
+
0.5 + 1.5*x_0
|
|
147
|
+
|
|
148
|
+
For representing an SDP with exact data, there is another backend::
|
|
149
|
+
|
|
150
|
+
sage: from sage.numerical.backends.matrix_sdp_backend import MatrixSDPBackend
|
|
151
|
+
sage: p = SemidefiniteProgram(solver=MatrixSDPBackend, base_ring=QQ)
|
|
152
|
+
sage: p.base_ring()
|
|
153
|
+
Rational Field
|
|
154
|
+
sage: x = p.new_variable()
|
|
155
|
+
sage: 1/2 + 3/2 * x[1]
|
|
156
|
+
1/2 + 3/2*x_0
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
Linear Variables and Expressions
|
|
160
|
+
--------------------------------
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
To make your code more readable, you can construct
|
|
164
|
+
:class:`SDPVariable` objects that can be arbitrarily named and
|
|
165
|
+
indexed. Internally, this is then translated back to the `x_i`
|
|
166
|
+
variables. For example::
|
|
167
|
+
|
|
168
|
+
sage: sdp.<a,b> = SemidefiniteProgram()
|
|
169
|
+
sage: a
|
|
170
|
+
SDPVariable
|
|
171
|
+
sage: 5 + a[1] + 2*b[3]
|
|
172
|
+
5 + x_0 + 2*x_1
|
|
173
|
+
|
|
174
|
+
Indices can be any object, not necessarily integers. Multi-indices are
|
|
175
|
+
also allowed::
|
|
176
|
+
|
|
177
|
+
sage: a[4, 'string', QQ]
|
|
178
|
+
x_2
|
|
179
|
+
sage: a[4, 'string', QQ] - 7*b[2]
|
|
180
|
+
x_2 - 7*x_3
|
|
181
|
+
sage: sdp.show()
|
|
182
|
+
Maximization:
|
|
183
|
+
<BLANKLINE>
|
|
184
|
+
Constraints:
|
|
185
|
+
Variables:
|
|
186
|
+
a[1], b[3], a[(4, 'string', Rational Field)], b[2]
|
|
187
|
+
|
|
188
|
+
Index of functions and methods
|
|
189
|
+
------------------------------
|
|
190
|
+
|
|
191
|
+
Below are listed the methods of :class:`SemidefiniteProgram`. This module
|
|
192
|
+
also implements the :class:`SDPSolverException` exception, as well as the
|
|
193
|
+
:class:`SDPVariable` class.
|
|
194
|
+
|
|
195
|
+
.. csv-table::
|
|
196
|
+
:class: contentstable
|
|
197
|
+
:widths: 30, 70
|
|
198
|
+
:delim: |
|
|
199
|
+
|
|
200
|
+
:meth:`~SemidefiniteProgram.add_constraint` | Add a constraint to the ``SemidefiniteProgram``
|
|
201
|
+
:meth:`~SemidefiniteProgram.base_ring` | Return the base ring
|
|
202
|
+
:meth:`~SemidefiniteProgram.dual_variable` | Return optimal dual variable block
|
|
203
|
+
:meth:`~SemidefiniteProgram.get_backend` | Return the backend instance used
|
|
204
|
+
:meth:`~SemidefiniteProgram.get_values` | Return values found by the previous call to ``solve()``
|
|
205
|
+
:meth:`~SemidefiniteProgram.linear_constraints_parent` | Return the parent for all linear constraints
|
|
206
|
+
:meth:`~SemidefiniteProgram.linear_function` | Construct a new linear function
|
|
207
|
+
:meth:`~SemidefiniteProgram.linear_functions_parent` | Return the parent for all linear functions
|
|
208
|
+
:meth:`~SemidefiniteProgram.new_variable` | Return an instance of ``SDPVariable`` associated to the ``SemidefiniteProgram``
|
|
209
|
+
:meth:`~SemidefiniteProgram.number_of_constraints` | Return the number of constraints assigned so far
|
|
210
|
+
:meth:`~SemidefiniteProgram.number_of_variables` | Return the number of variables used so far
|
|
211
|
+
:meth:`~SemidefiniteProgram.set_objective` | Set the objective of the ``SemidefiniteProgram``
|
|
212
|
+
:meth:`~SemidefiniteProgram.set_problem_name` | Set the name of the ``SemidefiniteProgram``
|
|
213
|
+
:meth:`~SemidefiniteProgram.slack` | Return the slack variable block at the optimum
|
|
214
|
+
:meth:`~SemidefiniteProgram.show` | Display the ``SemidefiniteProgram`` in a human-readable way
|
|
215
|
+
:meth:`~SemidefiniteProgram.solve` | Solve the ``SemidefiniteProgram``
|
|
216
|
+
:meth:`~SemidefiniteProgram.solver_parameter` | Return or define a solver parameter
|
|
217
|
+
:meth:`~SemidefiniteProgram.sum` | Efficiently compute the sum of a sequence of LinearFunction elements
|
|
218
|
+
|
|
219
|
+
AUTHORS:
|
|
220
|
+
|
|
221
|
+
- Ingolfur Edvardsson (2014/08): added extension for exact computation
|
|
222
|
+
|
|
223
|
+
- Dima Pasechnik (2014-) : supervision, minor fixes, duality
|
|
224
|
+
"""
|
|
225
|
+
# ****************************************************************************
|
|
226
|
+
# Copyright (C) 2014 Ingolfur Edvardsson <ingolfured@gmail.com>
|
|
227
|
+
#
|
|
228
|
+
# This program is free software: you can redistribute it and/or modify
|
|
229
|
+
# it under the terms of the GNU General Public License as published by
|
|
230
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
231
|
+
# (at your option) any later version.
|
|
232
|
+
# https://www.gnu.org/licenses/
|
|
233
|
+
# ****************************************************************************
|
|
234
|
+
|
|
235
|
+
from sage.structure.parent cimport Parent
|
|
236
|
+
from sage.structure.element cimport Element
|
|
237
|
+
from sage.numerical.linear_functions import LinearFunction, LinearConstraint
|
|
238
|
+
from sage.matrix.constructor import matrix
|
|
239
|
+
from sage.structure.element import Matrix
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
cdef class SemidefiniteProgram(SageObject):
|
|
243
|
+
r"""
|
|
244
|
+
The ``SemidefiniteProgram`` class is the link between Sage, semidefinite
|
|
245
|
+
programming (SDP) and semidefinite programming solvers.
|
|
246
|
+
|
|
247
|
+
A Semidefinite Programming (SDP) 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 :wikipedia:`Semidefinite_programming` for further information on semidefinite
|
|
252
|
+
programming, and the :mod:`SDP module <sage.numerical.sdp>` for its use in
|
|
253
|
+
Sage.
|
|
254
|
+
|
|
255
|
+
INPUT:
|
|
256
|
+
|
|
257
|
+
- ``solver`` -- selects a solver:
|
|
258
|
+
|
|
259
|
+
- CVXOPT (``solver="CVXOPT"``). See the `CVXOPT <http://www.cvxopt.org/>`_
|
|
260
|
+
website.
|
|
261
|
+
|
|
262
|
+
- If ``solver=None`` (default), the default solver is used (see
|
|
263
|
+
:func:`default_sdp_solver`)
|
|
264
|
+
|
|
265
|
+
- ``maximization``
|
|
266
|
+
|
|
267
|
+
- When set to ``True`` (default), the ``SemidefiniteProgram``
|
|
268
|
+
is defined as a maximization.
|
|
269
|
+
|
|
270
|
+
- When set to ``False``, the ``SemidefiniteProgram`` is
|
|
271
|
+
defined as a minimization.
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
.. SEEALSO::
|
|
275
|
+
|
|
276
|
+
- :func:`default_sdp_solver` -- returns/sets the default SDP solver
|
|
277
|
+
|
|
278
|
+
EXAMPLES:
|
|
279
|
+
|
|
280
|
+
Computation of a basic Semidefinite Program::
|
|
281
|
+
|
|
282
|
+
sage: p = SemidefiniteProgram(maximization=False)
|
|
283
|
+
sage: x = p.new_variable()
|
|
284
|
+
sage: p.set_objective(x[0] - x[1])
|
|
285
|
+
sage: a1 = matrix([[1, 2.], [2., 3.]])
|
|
286
|
+
sage: a2 = matrix([[3, 4.], [4., 5.]])
|
|
287
|
+
sage: a3 = matrix([[5, 6.], [6., 7.]])
|
|
288
|
+
sage: b1 = matrix([[1, 1.], [1., 1.]])
|
|
289
|
+
sage: b2 = matrix([[2, 2.], [2., 2.]])
|
|
290
|
+
sage: b3 = matrix([[3, 3.], [3., 3.]])
|
|
291
|
+
sage: p.add_constraint(a1*x[0] + a2*x[1] <= a3)
|
|
292
|
+
sage: p.add_constraint(b1*x[0] + b2*x[1] <= b3)
|
|
293
|
+
sage: N(p.solve(), 2) # needs cvxopt
|
|
294
|
+
-3.0
|
|
295
|
+
"""
|
|
296
|
+
|
|
297
|
+
def __init__(self, solver=None, maximization=True,
|
|
298
|
+
names=tuple(), **kwds):
|
|
299
|
+
r"""
|
|
300
|
+
Constructor for the ``SemidefiniteProgram`` class.
|
|
301
|
+
|
|
302
|
+
INPUT:
|
|
303
|
+
|
|
304
|
+
- ``solver`` -- the following solvers should be available through this class:
|
|
305
|
+
|
|
306
|
+
- CVXOPT (``solver="CVXOPT"``). See the `CVXOPT <http://www.cvxopt.org/>`_
|
|
307
|
+
web site.
|
|
308
|
+
|
|
309
|
+
- If ``solver=None`` (default), the default solver is used (see
|
|
310
|
+
``default_sdp_solver`` method.
|
|
311
|
+
|
|
312
|
+
- ``maximization``
|
|
313
|
+
|
|
314
|
+
- When set to ``True`` (default), the ``SemidefiniteProgram``
|
|
315
|
+
is defined as a maximization.
|
|
316
|
+
- When set to ``False``, the ``SemidefiniteProgram`` is
|
|
317
|
+
defined as a minimization.
|
|
318
|
+
|
|
319
|
+
- ``names`` -- list/tuple/iterable of string. Default names of
|
|
320
|
+
the SDP variables. Used to enable the ``sdp.<x> =
|
|
321
|
+
SemidefiniteProgram()`` syntax.
|
|
322
|
+
|
|
323
|
+
- other keyword arguments are passed to the solver.
|
|
324
|
+
|
|
325
|
+
.. SEEALSO::
|
|
326
|
+
|
|
327
|
+
- :meth:`default_sdp_solver` -- returns/Sets the default SDP solver
|
|
328
|
+
|
|
329
|
+
EXAMPLES::
|
|
330
|
+
|
|
331
|
+
sage: p = SemidefiniteProgram(maximization=True)
|
|
332
|
+
"""
|
|
333
|
+
self._first_variable_names = list(names)
|
|
334
|
+
from sage.numerical.backends.generic_sdp_backend import get_solver
|
|
335
|
+
self._backend = get_solver(solver=solver, **kwds)
|
|
336
|
+
if not maximization:
|
|
337
|
+
self._backend.set_sense(-1)
|
|
338
|
+
|
|
339
|
+
# Associates an index to the variables
|
|
340
|
+
self._variables = {}
|
|
341
|
+
|
|
342
|
+
def linear_functions_parent(self):
|
|
343
|
+
"""
|
|
344
|
+
Return the parent for all linear functions.
|
|
345
|
+
|
|
346
|
+
EXAMPLES::
|
|
347
|
+
|
|
348
|
+
sage: p = SemidefiniteProgram()
|
|
349
|
+
sage: p.linear_functions_parent()
|
|
350
|
+
Linear functions over Real Double Field
|
|
351
|
+
"""
|
|
352
|
+
if self._linear_functions_parent is None:
|
|
353
|
+
base_ring = self._backend.base_ring()
|
|
354
|
+
from sage.numerical.linear_functions import LinearFunctionsParent
|
|
355
|
+
self._linear_functions_parent = LinearFunctionsParent(base_ring)
|
|
356
|
+
return self._linear_functions_parent
|
|
357
|
+
|
|
358
|
+
def linear_constraints_parent(self):
|
|
359
|
+
"""
|
|
360
|
+
Return the parent for all linear constraints.
|
|
361
|
+
|
|
362
|
+
See :mod:`~sage.numerical.linear_functions` for more
|
|
363
|
+
details.
|
|
364
|
+
|
|
365
|
+
EXAMPLES::
|
|
366
|
+
|
|
367
|
+
sage: p = SemidefiniteProgram()
|
|
368
|
+
sage: p.linear_constraints_parent()
|
|
369
|
+
Linear constraints over Real Double Field
|
|
370
|
+
"""
|
|
371
|
+
if self._linear_constraints_parent is None:
|
|
372
|
+
from sage.numerical.linear_functions import LinearConstraintsParent
|
|
373
|
+
LF = self.linear_functions_parent()
|
|
374
|
+
self._linear_constraints_parent = LinearConstraintsParent(LF)
|
|
375
|
+
return self._linear_constraints_parent
|
|
376
|
+
|
|
377
|
+
def __call__(self, x):
|
|
378
|
+
"""
|
|
379
|
+
Construct a new linear function.
|
|
380
|
+
|
|
381
|
+
EXAMPLES::
|
|
382
|
+
|
|
383
|
+
sage: p = SemidefiniteProgram()
|
|
384
|
+
sage: p.linear_function({0:1})
|
|
385
|
+
x_0
|
|
386
|
+
"""
|
|
387
|
+
parent = self.linear_functions_parent()
|
|
388
|
+
return parent(x)
|
|
389
|
+
|
|
390
|
+
linear_function = __call__
|
|
391
|
+
|
|
392
|
+
def _repr_(self):
|
|
393
|
+
r"""
|
|
394
|
+
Return a short description of the ``SemidefiniteProgram``.
|
|
395
|
+
|
|
396
|
+
EXAMPLES::
|
|
397
|
+
|
|
398
|
+
sage: p = SemidefiniteProgram()
|
|
399
|
+
sage: x = p.new_variable()
|
|
400
|
+
sage: a1 = matrix([[1, 2.], [2., 3.]])
|
|
401
|
+
sage: a2 = matrix([[3, 4.], [4., 5.]])
|
|
402
|
+
sage: a3 = matrix([[5, 6.], [6., 7.]])
|
|
403
|
+
sage: b1 = matrix([[1, 1.], [1., 1.]])
|
|
404
|
+
sage: b2 = matrix([[2, 2.], [2., 2.]])
|
|
405
|
+
sage: b3 = matrix([[3, 3.], [3., 3.]])
|
|
406
|
+
sage: p.add_constraint(a1*x[0] + a2*x[1] <= a3)
|
|
407
|
+
sage: p.add_constraint(b1*x[0] + b2*x[1] <= b3)
|
|
408
|
+
sage: p.add_constraint(b1*x[0] + b2*x[1] <= a1)
|
|
409
|
+
sage: print(p)
|
|
410
|
+
Semidefinite Program ( maximization, 2 variables, 3 constraints )
|
|
411
|
+
"""
|
|
412
|
+
cdef GenericSDPBackend b = self._backend
|
|
413
|
+
|
|
414
|
+
return ("Semidefinite Program " +
|
|
415
|
+
|
|
416
|
+
( "\"" +self._backend.problem_name()+ "\""
|
|
417
|
+
if (str(self._backend.problem_name()) != "") else "")+
|
|
418
|
+
|
|
419
|
+
" ( " + ("maximization" if b.is_maximization() else "minimization" ) +
|
|
420
|
+
|
|
421
|
+
", " + str(b.ncols()) + " variables, " +
|
|
422
|
+
str(b.nrows()) + " constraints )")
|
|
423
|
+
|
|
424
|
+
def __getitem__(self, v):
|
|
425
|
+
r"""
|
|
426
|
+
Return the symbolic variable corresponding to the key
|
|
427
|
+
from a default dictionary.
|
|
428
|
+
|
|
429
|
+
It returns the element asked, and otherwise creates it.
|
|
430
|
+
If necessary, it also creates the default dictionary.
|
|
431
|
+
|
|
432
|
+
This method lets the user define LinearProgram without having to
|
|
433
|
+
define independent dictionaries when it is not necessary for him.
|
|
434
|
+
|
|
435
|
+
EXAMPLES::
|
|
436
|
+
|
|
437
|
+
sage: p = SemidefiniteProgram()
|
|
438
|
+
sage: p.set_objective(p['x'] + p['z'])
|
|
439
|
+
sage: p['x']
|
|
440
|
+
x_0
|
|
441
|
+
"""
|
|
442
|
+
|
|
443
|
+
try:
|
|
444
|
+
return self._default_sdpvariable[v]
|
|
445
|
+
except TypeError:
|
|
446
|
+
self._default_sdpvariable = self.new_variable()
|
|
447
|
+
return self._default_sdpvariable[v]
|
|
448
|
+
|
|
449
|
+
def base_ring(self):
|
|
450
|
+
"""
|
|
451
|
+
Return the base ring.
|
|
452
|
+
|
|
453
|
+
OUTPUT: a ring. The coefficients that the chosen solver supports
|
|
454
|
+
|
|
455
|
+
EXAMPLES::
|
|
456
|
+
|
|
457
|
+
sage: p = SemidefiniteProgram(solver='cvxopt')
|
|
458
|
+
sage: p.base_ring()
|
|
459
|
+
Real Double Field
|
|
460
|
+
"""
|
|
461
|
+
return self._backend.base_ring()
|
|
462
|
+
|
|
463
|
+
def set_problem_name(self, name):
|
|
464
|
+
r"""
|
|
465
|
+
Set the name of the ``SemidefiniteProgram``.
|
|
466
|
+
|
|
467
|
+
INPUT:
|
|
468
|
+
|
|
469
|
+
- ``name`` -- string representing the name of the
|
|
470
|
+
``SemidefiniteProgram``
|
|
471
|
+
|
|
472
|
+
EXAMPLES::
|
|
473
|
+
|
|
474
|
+
sage: p = SemidefiniteProgram()
|
|
475
|
+
sage: p.set_problem_name("Test program")
|
|
476
|
+
sage: p
|
|
477
|
+
Semidefinite Program "Test program" ( maximization, 0 variables, 0 constraints )
|
|
478
|
+
"""
|
|
479
|
+
self._backend.problem_name(name)
|
|
480
|
+
|
|
481
|
+
def new_variable(self, name=""):
|
|
482
|
+
r"""
|
|
483
|
+
Return an instance of :class:`SDPVariable` associated
|
|
484
|
+
to the current instance of :class:`SemidefiniteProgram`.
|
|
485
|
+
|
|
486
|
+
A new variable ``x`` is defined by::
|
|
487
|
+
|
|
488
|
+
sage: p = SemidefiniteProgram()
|
|
489
|
+
sage: x = p.new_variable()
|
|
490
|
+
|
|
491
|
+
It behaves exactly as an usual dictionary would. It can use any key
|
|
492
|
+
argument you may like, as ``x[5]`` or ``x["b"]``, and has methods
|
|
493
|
+
``items()`` and ``keys()``.
|
|
494
|
+
|
|
495
|
+
INPUT:
|
|
496
|
+
|
|
497
|
+
- ``dim`` -- integer; defines the dimension of the dictionary
|
|
498
|
+
If ``x`` has dimension `2`, its fields will be of the form
|
|
499
|
+
``x[key1][key2]``. Deprecated.
|
|
500
|
+
|
|
501
|
+
- ``name`` -- string; associates a name to the variable
|
|
502
|
+
|
|
503
|
+
EXAMPLES::
|
|
504
|
+
|
|
505
|
+
sage: p = SemidefiniteProgram()
|
|
506
|
+
sage: x = p.new_variable()
|
|
507
|
+
sage: a1 = matrix([[1, 2.], [2., 3.]])
|
|
508
|
+
sage: p.add_constraint(a1*x[0] + a1*x[3] <= 0)
|
|
509
|
+
sage: p.show()
|
|
510
|
+
Maximization:
|
|
511
|
+
<BLANKLINE>
|
|
512
|
+
Constraints:
|
|
513
|
+
constraint_0: [1.0 2.0][2.0 3.0]x_0 + [1.0 2.0][2.0 3.0]x_1 <= [0 0][0 0]
|
|
514
|
+
Variables:
|
|
515
|
+
x_0, x_1
|
|
516
|
+
"""
|
|
517
|
+
if not name and self._first_variable_names:
|
|
518
|
+
name = self._first_variable_names.pop(0)
|
|
519
|
+
|
|
520
|
+
return sdp_variable_parent(self,
|
|
521
|
+
name=name)
|
|
522
|
+
|
|
523
|
+
def _first_ngens(self, n):
|
|
524
|
+
"""
|
|
525
|
+
Construct the first `n` SDPVariables.
|
|
526
|
+
|
|
527
|
+
This method is used for the generator syntax (see below). You
|
|
528
|
+
probably should not use it for anything else.
|
|
529
|
+
|
|
530
|
+
INPUT:
|
|
531
|
+
|
|
532
|
+
- ``n`` -- integer; the number of variables to construct
|
|
533
|
+
|
|
534
|
+
OUTPUT:
|
|
535
|
+
|
|
536
|
+
A tuple of not necessarily positive :class:`SDPVariable`
|
|
537
|
+
instances.
|
|
538
|
+
|
|
539
|
+
EXAMPLES::
|
|
540
|
+
|
|
541
|
+
sage: sdp.<a,b> = SemidefiniteProgram()
|
|
542
|
+
sage: a[0] + b[2]
|
|
543
|
+
x_0 + x_1
|
|
544
|
+
sage: sdp.show()
|
|
545
|
+
Maximization:
|
|
546
|
+
<BLANKLINE>
|
|
547
|
+
Constraints:
|
|
548
|
+
Variables:
|
|
549
|
+
a[0], b[2]
|
|
550
|
+
"""
|
|
551
|
+
return tuple(self.new_variable() for i in range(n))
|
|
552
|
+
|
|
553
|
+
def gen(self, i):
|
|
554
|
+
"""
|
|
555
|
+
Return the linear variable `x_i`.
|
|
556
|
+
|
|
557
|
+
EXAMPLES::
|
|
558
|
+
|
|
559
|
+
sage: sdp = SemidefiniteProgram()
|
|
560
|
+
sage: sdp.gen(0)
|
|
561
|
+
x_0
|
|
562
|
+
sage: [sdp.gen(i) for i in range(10)]
|
|
563
|
+
[x_0, x_1, x_2, x_3, x_4, x_5, x_6, x_7, x_8, x_9]
|
|
564
|
+
"""
|
|
565
|
+
return self.linear_functions_parent().gen(i)
|
|
566
|
+
|
|
567
|
+
cpdef int number_of_constraints(self) noexcept:
|
|
568
|
+
r"""
|
|
569
|
+
Return the number of constraints assigned so far.
|
|
570
|
+
|
|
571
|
+
EXAMPLES::
|
|
572
|
+
|
|
573
|
+
sage: p = SemidefiniteProgram(solver = "cvxopt")
|
|
574
|
+
sage: x = p.new_variable()
|
|
575
|
+
sage: a1 = matrix([[1, 2.], [2., 3.]])
|
|
576
|
+
sage: a2 = matrix([[3, 4.], [4., 5.]])
|
|
577
|
+
sage: a3 = matrix([[5, 6.], [6., 7.]])
|
|
578
|
+
sage: b1 = matrix([[1, 1.], [1., 1.]])
|
|
579
|
+
sage: b2 = matrix([[2, 2.], [2., 2.]])
|
|
580
|
+
sage: b3 = matrix([[3, 3.], [3., 3.]])
|
|
581
|
+
sage: p.add_constraint(a1*x[0] + a2*x[1] <= a3)
|
|
582
|
+
sage: p.add_constraint(b1*x[0] + b2*x[1] <= b3)
|
|
583
|
+
sage: p.add_constraint(b1*x[0] + a2*x[1] <= b3)
|
|
584
|
+
sage: p.number_of_constraints()
|
|
585
|
+
3
|
|
586
|
+
"""
|
|
587
|
+
return self._backend.nrows()
|
|
588
|
+
|
|
589
|
+
cpdef int number_of_variables(self) noexcept:
|
|
590
|
+
r"""
|
|
591
|
+
Return the number of variables used so far.
|
|
592
|
+
|
|
593
|
+
EXAMPLES::
|
|
594
|
+
|
|
595
|
+
sage: p = SemidefiniteProgram()
|
|
596
|
+
sage: a = matrix([[1, 2.], [2., 3.]])
|
|
597
|
+
sage: p.add_constraint(a*p[0] - a*p[2] <= 2*a*p[4] )
|
|
598
|
+
sage: p.number_of_variables()
|
|
599
|
+
3
|
|
600
|
+
"""
|
|
601
|
+
return self._backend.ncols()
|
|
602
|
+
|
|
603
|
+
def show(self):
|
|
604
|
+
r"""
|
|
605
|
+
Display the :class:`SemidefiniteProgram` in a human-readable way.
|
|
606
|
+
|
|
607
|
+
EXAMPLES:
|
|
608
|
+
|
|
609
|
+
When constraints and variables have names ::
|
|
610
|
+
|
|
611
|
+
sage: p = SemidefiniteProgram()
|
|
612
|
+
sage: x = p.new_variable(name='hihi')
|
|
613
|
+
sage: a1 = matrix([[1,2],[2,3]])
|
|
614
|
+
sage: a2 = matrix([[2,3],[3,4]])
|
|
615
|
+
sage: a3 = matrix([[3,4],[4,5]])
|
|
616
|
+
sage: p.set_objective(x[0] - x[1])
|
|
617
|
+
sage: p.add_constraint(a1*x[0] + a2*x[1]<= a3)
|
|
618
|
+
sage: p.show()
|
|
619
|
+
Maximization:
|
|
620
|
+
hihi[0] - hihi[1]
|
|
621
|
+
Constraints:
|
|
622
|
+
constraint_0: [1.0 2.0][2.0 3.0]hihi[0] + [2.0 3.0][3.0 4.0]hihi[1] <= [3.0 4.0][4.0 5.0]
|
|
623
|
+
Variables:
|
|
624
|
+
hihi[0], hihi[1]
|
|
625
|
+
"""
|
|
626
|
+
cdef int i, j
|
|
627
|
+
cdef GenericSDPBackend b = self._backend
|
|
628
|
+
|
|
629
|
+
# inv_variables associates a SDPVariable object to an id
|
|
630
|
+
inv_variables = {}
|
|
631
|
+
for (v, id) in self._variables.iteritems():
|
|
632
|
+
inv_variables[id] = v
|
|
633
|
+
|
|
634
|
+
# varid_name associates variables id to names
|
|
635
|
+
varid_name = {}
|
|
636
|
+
for 0<= i < b.ncols():
|
|
637
|
+
s = b.col_name(i)
|
|
638
|
+
varid_name[i] = s if s else 'x_'+str(i)
|
|
639
|
+
|
|
640
|
+
##### Sense and objective function
|
|
641
|
+
print("Maximization:" if b.is_maximization() else "Minimization:")
|
|
642
|
+
print(" ", end=" ")
|
|
643
|
+
first = True
|
|
644
|
+
for 0<= i< b.ncols():
|
|
645
|
+
c = b.objective_coefficient(i)
|
|
646
|
+
if c == 0:
|
|
647
|
+
continue
|
|
648
|
+
print((("+ " if (not first and c>0) else "") +
|
|
649
|
+
("" if c == 1 else ("- " if c == -1 else str(c)+" ")) + varid_name[i]
|
|
650
|
+
), end=" ")
|
|
651
|
+
first = False
|
|
652
|
+
if b.obj_constant_term > self._backend.zero():
|
|
653
|
+
print("+ {}".format(b.obj_constant_term))
|
|
654
|
+
elif b.obj_constant_term < self._backend.zero():
|
|
655
|
+
print("- {}".format(-b.obj_constant_term))
|
|
656
|
+
print("\n")
|
|
657
|
+
|
|
658
|
+
##### Constraints
|
|
659
|
+
print("Constraints:")
|
|
660
|
+
for i in range(b.nrows()):
|
|
661
|
+
indices, values = b.row(i)
|
|
662
|
+
print(" ", end=" ")
|
|
663
|
+
# Constraint's name
|
|
664
|
+
if b.row_name(i):
|
|
665
|
+
print(b.row_name(i)+":", end=" ")
|
|
666
|
+
first = True
|
|
667
|
+
l = sorted(zip(indices,values))
|
|
668
|
+
l.reverse()
|
|
669
|
+
if l[-1][0] == -1:
|
|
670
|
+
_, last_value = l.pop()
|
|
671
|
+
else:
|
|
672
|
+
last_value = matrix.zero( l[0][1].dimensions()[0],l[0][1].dimensions()[1] )
|
|
673
|
+
l.reverse()
|
|
674
|
+
for j, c in l:
|
|
675
|
+
if c == 0:
|
|
676
|
+
continue
|
|
677
|
+
print((("+ " if (not first) else "") +
|
|
678
|
+
( str(repr(c).replace('\n',"") ) )+varid_name[j]),
|
|
679
|
+
end=" ")
|
|
680
|
+
first = False
|
|
681
|
+
print(("<= "), end=" ")
|
|
682
|
+
print(repr(-last_value).replace('\n',""))
|
|
683
|
+
|
|
684
|
+
##### Variables
|
|
685
|
+
print("Variables:")
|
|
686
|
+
print(" ", end=" ")
|
|
687
|
+
for 0<= i < b.ncols()-1:
|
|
688
|
+
print(str(varid_name[i]) + ", ", end=" ")
|
|
689
|
+
print(str(varid_name[b.ncols()-1]))
|
|
690
|
+
|
|
691
|
+
def get_values(self, *lists):
|
|
692
|
+
r"""
|
|
693
|
+
Return values found by the previous call to ``solve()``.
|
|
694
|
+
|
|
695
|
+
INPUT:
|
|
696
|
+
|
|
697
|
+
- Any instance of :class:`SDPVariable` (or one of its elements),
|
|
698
|
+
or lists of them.
|
|
699
|
+
|
|
700
|
+
OUTPUT:
|
|
701
|
+
|
|
702
|
+
- Each instance of :class:`SDPVariable` is replaced by a dictionary
|
|
703
|
+
containing the numerical values found for each
|
|
704
|
+
corresponding variable in the instance.
|
|
705
|
+
- Each element of an instance of a :class:`SDPVariable` is replaced
|
|
706
|
+
by its corresponding numerical value.
|
|
707
|
+
|
|
708
|
+
EXAMPLES::
|
|
709
|
+
|
|
710
|
+
sage: p = SemidefiniteProgram(solver = "cvxopt", maximization = False)
|
|
711
|
+
sage: x = p.new_variable()
|
|
712
|
+
sage: p.set_objective(x[3] - x[5])
|
|
713
|
+
sage: a1 = matrix([[1, 2.], [2., 3.]])
|
|
714
|
+
sage: a2 = matrix([[3, 4.], [4., 5.]])
|
|
715
|
+
sage: a3 = matrix([[5, 6.], [6., 7.]])
|
|
716
|
+
sage: b1 = matrix([[1, 1.], [1., 1.]])
|
|
717
|
+
sage: b2 = matrix([[2, 2.], [2., 2.]])
|
|
718
|
+
sage: b3 = matrix([[3, 3.], [3., 3.]])
|
|
719
|
+
sage: p.add_constraint(a1*x[3] + a2*x[5] <= a3)
|
|
720
|
+
sage: p.add_constraint(b1*x[3] + b2*x[5] <= b3)
|
|
721
|
+
sage: N(p.solve(), 3) # needs cvxopt
|
|
722
|
+
-3.0
|
|
723
|
+
|
|
724
|
+
To return the optimal value of ``x[3]``::
|
|
725
|
+
|
|
726
|
+
sage: N(p.get_values(x[3]),3) # needs cvxopt
|
|
727
|
+
-1.0
|
|
728
|
+
|
|
729
|
+
To get a dictionary identical to ``x`` containing optimal
|
|
730
|
+
values for the corresponding variables ::
|
|
731
|
+
|
|
732
|
+
sage: x_sol = p.get_values(x) # needs cvxopt
|
|
733
|
+
sage: sorted(x_sol) # needs cvxopt
|
|
734
|
+
[3, 5]
|
|
735
|
+
"""
|
|
736
|
+
val = []
|
|
737
|
+
for l in lists:
|
|
738
|
+
if isinstance(l, SDPVariable):
|
|
739
|
+
c = {}
|
|
740
|
+
for k, v in l.items():
|
|
741
|
+
c[k] = self._backend.get_variable_value(self._variables[v])
|
|
742
|
+
val.append(c)
|
|
743
|
+
elif isinstance(l, list):
|
|
744
|
+
if len(l) == 1:
|
|
745
|
+
val.append([self.get_values(l[0])])
|
|
746
|
+
else:
|
|
747
|
+
c = []
|
|
748
|
+
[c.append(self.get_values(ll)) for ll in l]
|
|
749
|
+
val.append(c)
|
|
750
|
+
elif l in self._variables:
|
|
751
|
+
#val.append(self._values[l])
|
|
752
|
+
val.append(self._backend.get_variable_value(self._variables[l]))
|
|
753
|
+
|
|
754
|
+
if len(lists) == 1:
|
|
755
|
+
return val[0]
|
|
756
|
+
else:
|
|
757
|
+
return val
|
|
758
|
+
|
|
759
|
+
def set_objective(self, obj):
|
|
760
|
+
r"""
|
|
761
|
+
Set the objective of the :class:`SemidefiniteProgram`.
|
|
762
|
+
|
|
763
|
+
INPUT:
|
|
764
|
+
|
|
765
|
+
- ``obj`` -- a semidefinite function to be optimized
|
|
766
|
+
(can also be set to ``None`` or ``0`` when just
|
|
767
|
+
looking for a feasible solution)
|
|
768
|
+
|
|
769
|
+
EXAMPLES:
|
|
770
|
+
|
|
771
|
+
Let's solve the following semidefinite program:
|
|
772
|
+
|
|
773
|
+
.. MATH::
|
|
774
|
+
|
|
775
|
+
\begin{aligned}
|
|
776
|
+
\text{maximize} &\qquad x + 5y \qquad \\
|
|
777
|
+
\text{subject to} &\qquad \left( \begin{array}{cc} 1 & 2 \\ 2 & 3 \end{array} \right) x +
|
|
778
|
+
\left( \begin{array}{cc} 1 & 1 \\ 1 & 1 \end{array} \right) y \preceq
|
|
779
|
+
\left( \begin{array}{cc} 1 & -1 \\ -1 & 1 \end{array} \right)
|
|
780
|
+
\end{aligned}
|
|
781
|
+
|
|
782
|
+
This SDP can be solved as follows::
|
|
783
|
+
|
|
784
|
+
sage: p = SemidefiniteProgram(maximization=True)
|
|
785
|
+
sage: x = p.new_variable()
|
|
786
|
+
sage: p.set_objective(x[1] + 5*x[2])
|
|
787
|
+
sage: a1 = matrix([[1,2],[2,3]])
|
|
788
|
+
sage: a2 = matrix([[1,1],[1,1]])
|
|
789
|
+
sage: a3 = matrix([[1,-1],[-1,1]])
|
|
790
|
+
sage: p.add_constraint(a1*x[1] + a2*x[2] <= a3)
|
|
791
|
+
sage: N(p.solve(), digits=3) # needs cvxopt
|
|
792
|
+
16.2
|
|
793
|
+
sage: p.set_objective(None)
|
|
794
|
+
sage: _ = p.solve() # needs cvxopt
|
|
795
|
+
"""
|
|
796
|
+
cdef list values = []
|
|
797
|
+
|
|
798
|
+
# If the objective is None, or a constant, we want to remember
|
|
799
|
+
# that the objective function has been defined ( the user did not
|
|
800
|
+
# forget it ). In some SDO problems, you just want a feasible solution
|
|
801
|
+
# and do not care about any function being optimal.
|
|
802
|
+
cdef int i
|
|
803
|
+
|
|
804
|
+
if obj is not None:
|
|
805
|
+
f = obj.dict()
|
|
806
|
+
else:
|
|
807
|
+
f = {-1: 0}
|
|
808
|
+
d = f.pop(-1, self._backend.zero())
|
|
809
|
+
|
|
810
|
+
for i in range(self._backend.ncols()):
|
|
811
|
+
values.append(f.get(i,self._backend.zero()))
|
|
812
|
+
self._backend.set_objective(values, d)
|
|
813
|
+
|
|
814
|
+
def add_constraint(self, linear_function, name=None):
|
|
815
|
+
r"""
|
|
816
|
+
Add a constraint to the ``SemidefiniteProgram``.
|
|
817
|
+
|
|
818
|
+
INPUT:
|
|
819
|
+
|
|
820
|
+
- ``linear_function`` -- two different types of arguments are possible:
|
|
821
|
+
|
|
822
|
+
- A linear function. In this case, arguments ``min`` or ``max``
|
|
823
|
+
have to be specified.
|
|
824
|
+
- A linear constraint of the form ``A <= B``, ``A >= B``,
|
|
825
|
+
``A <= B <= C``, ``A >= B >= C`` or ``A == B``. In this
|
|
826
|
+
case, arguments ``min`` and ``max`` will be ignored.
|
|
827
|
+
|
|
828
|
+
- ``name`` -- a name for the constraint
|
|
829
|
+
|
|
830
|
+
EXAMPLES:
|
|
831
|
+
|
|
832
|
+
Let's solve the following semidefinite program:
|
|
833
|
+
|
|
834
|
+
.. MATH::
|
|
835
|
+
|
|
836
|
+
\begin{aligned}
|
|
837
|
+
\text{maximize} &\qquad x + 5y \qquad \\
|
|
838
|
+
\text{subject to} &\qquad \left( \begin{array}{cc} 1 & 2 \\ 2 & 3 \end{array} \right) x +
|
|
839
|
+
\left( \begin{array}{cc} 1 & 1 \\ 1 & 1 \end{array} \right) y \preceq
|
|
840
|
+
\left( \begin{array}{cc} 1 & -1 \\ -1 & 1 \end{array} \right)
|
|
841
|
+
\end{aligned}
|
|
842
|
+
|
|
843
|
+
This SDP can be solved as follows::
|
|
844
|
+
|
|
845
|
+
sage: p = SemidefiniteProgram(maximization=True)
|
|
846
|
+
sage: x = p.new_variable()
|
|
847
|
+
sage: p.set_objective(x[1] + 5*x[2])
|
|
848
|
+
sage: a1 = matrix([[1,2],[2,3]])
|
|
849
|
+
sage: a2 = matrix([[1,1],[1,1]])
|
|
850
|
+
sage: a3 = matrix([[1,-1],[-1,1]])
|
|
851
|
+
sage: p.add_constraint(a1*x[1] + a2*x[2] <= a3)
|
|
852
|
+
sage: N(p.solve(), digits=3) # needs cvxopt
|
|
853
|
+
16.2
|
|
854
|
+
|
|
855
|
+
One can also define double-bounds or equality using the symbol
|
|
856
|
+
``>=`` or ``==``::
|
|
857
|
+
|
|
858
|
+
sage: p = SemidefiniteProgram(maximization=True)
|
|
859
|
+
sage: x = p.new_variable()
|
|
860
|
+
sage: p.set_objective(x[1] + 5*x[2])
|
|
861
|
+
sage: a1 = matrix([[1,2],[2,3]])
|
|
862
|
+
sage: a2 = matrix([[1,1],[1,1]])
|
|
863
|
+
sage: a3 = matrix([[1,-1],[-1,1]])
|
|
864
|
+
sage: p.add_constraint(a3 >= a1*x[1] + a2*x[2])
|
|
865
|
+
sage: N(p.solve(), digits=3) # needs cvxopt
|
|
866
|
+
16.2
|
|
867
|
+
|
|
868
|
+
TESTS:
|
|
869
|
+
|
|
870
|
+
Complex constraints::
|
|
871
|
+
|
|
872
|
+
sage: p = SemidefiniteProgram()
|
|
873
|
+
sage: b = p.new_variable()
|
|
874
|
+
sage: a1 = matrix([[1,2],[2,3]])
|
|
875
|
+
sage: a2 = matrix([[1,-2],[-2,4]])
|
|
876
|
+
sage: p.add_constraint(a1*b[8] - a1*b[15] <= a2*b[8])
|
|
877
|
+
sage: p.show()
|
|
878
|
+
Maximization:
|
|
879
|
+
<BLANKLINE>
|
|
880
|
+
Constraints:
|
|
881
|
+
constraint_0: [ 0.0 4.0][ 4.0 -1.0]x_0 + [-1.0 -2.0][-2.0 -3.0]x_1 <= [0 0][0 0]
|
|
882
|
+
Variables:
|
|
883
|
+
x_0, x_1
|
|
884
|
+
|
|
885
|
+
Empty constraint::
|
|
886
|
+
|
|
887
|
+
sage: p = SemidefiniteProgram()
|
|
888
|
+
sage: p.add_constraint(sum([]))
|
|
889
|
+
"""
|
|
890
|
+
if linear_function is 0:
|
|
891
|
+
return
|
|
892
|
+
|
|
893
|
+
from sage.numerical.linear_tensor_constraints import LinearTensorConstraint
|
|
894
|
+
from sage.numerical.linear_tensor import LinearTensor
|
|
895
|
+
|
|
896
|
+
if isinstance(linear_function, (LinearTensorConstraint,
|
|
897
|
+
LinearConstraint)):
|
|
898
|
+
c = linear_function
|
|
899
|
+
if c.is_equation():
|
|
900
|
+
self.add_constraint(c.lhs()-c.rhs(), name=name)
|
|
901
|
+
self.add_constraint(-c.lhs()+c.rhs(), name=name)
|
|
902
|
+
else:
|
|
903
|
+
self.add_constraint(c.lhs()-c.rhs(), name=name)
|
|
904
|
+
|
|
905
|
+
elif isinstance(linear_function, (LinearFunction, LinearTensor)):
|
|
906
|
+
l = sorted(linear_function.dict().items())
|
|
907
|
+
self._backend.add_linear_constraint(l, name)
|
|
908
|
+
|
|
909
|
+
else:
|
|
910
|
+
raise ValueError('argument must be a linear function or constraint, got '+str(linear_function))
|
|
911
|
+
|
|
912
|
+
def solve(self, objective_only=False):
|
|
913
|
+
r"""
|
|
914
|
+
Solve the :class:`SemidefiniteProgram`.
|
|
915
|
+
|
|
916
|
+
INPUT:
|
|
917
|
+
|
|
918
|
+
- ``objective_only`` -- boolean:
|
|
919
|
+
|
|
920
|
+
- when set to ``True``, only the objective function is returned
|
|
921
|
+
- when set to ``False`` (default), the optimal numerical values
|
|
922
|
+
are stored (takes computational time)
|
|
923
|
+
|
|
924
|
+
OUTPUT: the optimal value taken by the objective function
|
|
925
|
+
|
|
926
|
+
TESTS:
|
|
927
|
+
|
|
928
|
+
The SDP from the header of this module::
|
|
929
|
+
|
|
930
|
+
sage: p = SemidefiniteProgram(maximization=False)
|
|
931
|
+
sage: x = p.new_variable()
|
|
932
|
+
sage: p.set_objective(x[0] - x[1])
|
|
933
|
+
sage: a1 = matrix([[1, 2.], [2., 3.]])
|
|
934
|
+
sage: a2 = matrix([[3, 4.], [4., 2.]])
|
|
935
|
+
sage: a3 = matrix([[5, 6.], [6., 7.]])
|
|
936
|
+
sage: b1 = matrix([[1, 1.], [1., 1.]])
|
|
937
|
+
sage: b2 = matrix([[2, 2.], [2., 1.]])
|
|
938
|
+
sage: b3 = matrix([[3, 3.], [3., 3.]])
|
|
939
|
+
sage: p.add_constraint(a1*x[0] + a2*x[1] <= a3)
|
|
940
|
+
sage: p.add_constraint(b1*x[0] + b2*x[1] <= b3)
|
|
941
|
+
|
|
942
|
+
sage: # needs cvxopt
|
|
943
|
+
sage: N(p.solve(), 4)
|
|
944
|
+
-11.
|
|
945
|
+
sage: x = p.get_values(x)
|
|
946
|
+
sage: N(x[0],4)
|
|
947
|
+
-8.0
|
|
948
|
+
sage: N(x[1],4)
|
|
949
|
+
3.0
|
|
950
|
+
"""
|
|
951
|
+
self._backend.solve()
|
|
952
|
+
return self._backend.get_objective_value()
|
|
953
|
+
|
|
954
|
+
cpdef dual_variable(self, int i, sparse=False):
|
|
955
|
+
"""
|
|
956
|
+
The `i`-th dual variable.
|
|
957
|
+
|
|
958
|
+
Available after ``self.solve()`` is called, otherwise the result is
|
|
959
|
+
undefined.
|
|
960
|
+
|
|
961
|
+
INPUT:
|
|
962
|
+
|
|
963
|
+
- ``index`` -- integer; the constraint's id
|
|
964
|
+
|
|
965
|
+
OUTPUT: the matrix of the `i`-th dual variable
|
|
966
|
+
|
|
967
|
+
EXAMPLES:
|
|
968
|
+
|
|
969
|
+
Dual objective value is the same as the primal one::
|
|
970
|
+
|
|
971
|
+
sage: p = SemidefiniteProgram(maximization=False)
|
|
972
|
+
sage: x = p.new_variable()
|
|
973
|
+
sage: p.set_objective(x[0] - x[1])
|
|
974
|
+
sage: a1 = matrix([[1, 2.], [2., 3.]])
|
|
975
|
+
sage: a2 = matrix([[3, 4.], [4., 5.]])
|
|
976
|
+
sage: a3 = matrix([[5, 6.], [6., 7.]])
|
|
977
|
+
sage: b1 = matrix([[1, 1.], [1., 1.]])
|
|
978
|
+
sage: b2 = matrix([[2, 2.], [2., 2.]])
|
|
979
|
+
sage: b3 = matrix([[3, 3.], [3., 3.]])
|
|
980
|
+
sage: p.add_constraint(a1*x[0] + a2*x[1] <= a3)
|
|
981
|
+
sage: p.add_constraint(b1*x[0] + b2*x[1] <= b3)
|
|
982
|
+
|
|
983
|
+
sage: # needs cvxopt
|
|
984
|
+
sage: p.solve() # tol 1e-08
|
|
985
|
+
-3.0
|
|
986
|
+
sage: x = p.get_values(x).values()
|
|
987
|
+
sage: -(a3*p.dual_variable(0)).trace() - (b3*p.dual_variable(1)).trace() # tol 1e-07
|
|
988
|
+
-3.0
|
|
989
|
+
|
|
990
|
+
Dual variable is orthogonal to the slack ::
|
|
991
|
+
|
|
992
|
+
sage: # needs cvxopt
|
|
993
|
+
sage: g = sum((p.slack(j)*p.dual_variable(j)).trace() for j in range(2)); g # tol 1.2e-08
|
|
994
|
+
0.0
|
|
995
|
+
|
|
996
|
+
TESTS::
|
|
997
|
+
|
|
998
|
+
sage: p.dual_variable(7) # needs cvxopt
|
|
999
|
+
Traceback (most recent call last):
|
|
1000
|
+
...
|
|
1001
|
+
IndexError: list index out of range
|
|
1002
|
+
"""
|
|
1003
|
+
return self._backend.dual_variable(i, sparse=sparse)
|
|
1004
|
+
|
|
1005
|
+
cpdef slack(self, int i, sparse=False):
|
|
1006
|
+
"""
|
|
1007
|
+
Slack of the `i`-th constraint.
|
|
1008
|
+
|
|
1009
|
+
Available after ``self.solve()`` is called, otherwise the result is
|
|
1010
|
+
undefined.
|
|
1011
|
+
|
|
1012
|
+
INPUT:
|
|
1013
|
+
|
|
1014
|
+
- ``index`` -- integer; the constraint's id
|
|
1015
|
+
|
|
1016
|
+
OUTPUT: the matrix of the slack of the `i`-th constraint
|
|
1017
|
+
|
|
1018
|
+
EXAMPLES::
|
|
1019
|
+
|
|
1020
|
+
sage: p = SemidefiniteProgram(maximization = False)
|
|
1021
|
+
sage: x = p.new_variable()
|
|
1022
|
+
sage: p.set_objective(x[0] - x[1])
|
|
1023
|
+
sage: a1 = matrix([[1, 2.], [2., 3.]])
|
|
1024
|
+
sage: a2 = matrix([[3, 4.], [4., 5.]])
|
|
1025
|
+
sage: a3 = matrix([[5, 6.], [6., 7.]])
|
|
1026
|
+
sage: b1 = matrix([[1, 1.], [1., 1.]])
|
|
1027
|
+
sage: b2 = matrix([[2, 2.], [2., 2.]])
|
|
1028
|
+
sage: b3 = matrix([[3, 3.], [3., 3.]])
|
|
1029
|
+
sage: p.add_constraint(a1*x[0] + a2*x[1] <= a3)
|
|
1030
|
+
sage: p.add_constraint(b1*x[0] + b2*x[1] <= b3)
|
|
1031
|
+
|
|
1032
|
+
sage: # needs cvxopt
|
|
1033
|
+
sage: p.solve() # tol 1e-08
|
|
1034
|
+
-3.0
|
|
1035
|
+
sage: B1 = p.slack(1); B1 # tol 1e-08
|
|
1036
|
+
[0.0 0.0]
|
|
1037
|
+
[0.0 0.0]
|
|
1038
|
+
sage: B1.is_positive_definite()
|
|
1039
|
+
True
|
|
1040
|
+
sage: x = sorted(p.get_values(x).values())
|
|
1041
|
+
sage: x[0]*b1 + x[1]*b2 - b3 + B1 # tol 1e-09
|
|
1042
|
+
[0.0 0.0]
|
|
1043
|
+
[0.0 0.0]
|
|
1044
|
+
|
|
1045
|
+
TESTS::
|
|
1046
|
+
|
|
1047
|
+
sage: p.slack(7) # needs cvxopt
|
|
1048
|
+
Traceback (most recent call last):
|
|
1049
|
+
...
|
|
1050
|
+
IndexError: list index out of range
|
|
1051
|
+
"""
|
|
1052
|
+
return self._backend.slack(i, sparse=sparse)
|
|
1053
|
+
|
|
1054
|
+
def solver_parameter(self, name, value=None):
|
|
1055
|
+
"""
|
|
1056
|
+
Return or define a solver parameter.
|
|
1057
|
+
|
|
1058
|
+
The solver parameters are by essence solver-specific, which
|
|
1059
|
+
means their meaning heavily depends on the solver used.
|
|
1060
|
+
|
|
1061
|
+
(If you do not know which solver you are using, then you are
|
|
1062
|
+
using CVXOPT).
|
|
1063
|
+
|
|
1064
|
+
INPUT:
|
|
1065
|
+
|
|
1066
|
+
- ``name`` -- string; the parameter
|
|
1067
|
+
|
|
1068
|
+
- ``value`` -- the parameter's value if it is to be defined,
|
|
1069
|
+
or ``None`` (default) to obtain its current value
|
|
1070
|
+
|
|
1071
|
+
EXAMPLES::
|
|
1072
|
+
|
|
1073
|
+
sage: # needs cvxopt
|
|
1074
|
+
sage: p.<x> = SemidefiniteProgram(solver='cvxopt',
|
|
1075
|
+
....: maximization=False)
|
|
1076
|
+
sage: p.solver_parameter("show_progress", True)
|
|
1077
|
+
sage: p.solver_parameter("show_progress")
|
|
1078
|
+
True
|
|
1079
|
+
sage: p.set_objective(x[0] - x[1])
|
|
1080
|
+
sage: a1 = matrix([[1, 2.], [2., 3.]])
|
|
1081
|
+
sage: a2 = matrix([[3, 4.], [4., 2.]])
|
|
1082
|
+
sage: a3 = matrix([[5, 6.], [6., 7.]])
|
|
1083
|
+
sage: b1 = matrix([[1, 1.], [1., 1.]])
|
|
1084
|
+
sage: b2 = matrix([[2, 2.], [2., 1.]])
|
|
1085
|
+
sage: b3 = matrix([[3, 3.], [3., 3.]])
|
|
1086
|
+
sage: p.add_constraint(a1*x[0] + a2*x[1] <= a3)
|
|
1087
|
+
sage: p.add_constraint(b1*x[0] + b2*x[1] <= b3)
|
|
1088
|
+
sage: N(p.solve(), 4)
|
|
1089
|
+
pcost dcost gap pres dres k/t
|
|
1090
|
+
0: 1...
|
|
1091
|
+
...
|
|
1092
|
+
Optimal solution found.
|
|
1093
|
+
-11.
|
|
1094
|
+
"""
|
|
1095
|
+
if value is None:
|
|
1096
|
+
return self._backend.solver_parameter(name)
|
|
1097
|
+
else:
|
|
1098
|
+
self._backend.solver_parameter(name, value)
|
|
1099
|
+
|
|
1100
|
+
cpdef sum(self, L):
|
|
1101
|
+
r"""
|
|
1102
|
+
Efficiently compute the sum of a sequence of
|
|
1103
|
+
:class:`~sage.numerical.linear_functions.LinearFunction` elements.
|
|
1104
|
+
|
|
1105
|
+
INPUT:
|
|
1106
|
+
|
|
1107
|
+
- ``L`` -- list of
|
|
1108
|
+
:class:`~sage.numerical.linear_functions.LinearFunction` instances
|
|
1109
|
+
|
|
1110
|
+
.. NOTE::
|
|
1111
|
+
|
|
1112
|
+
The use of the regular ``sum`` function is not recommended
|
|
1113
|
+
as it is much less efficient than this one.
|
|
1114
|
+
|
|
1115
|
+
EXAMPLES::
|
|
1116
|
+
|
|
1117
|
+
sage: p = SemidefiniteProgram()
|
|
1118
|
+
sage: v = p.new_variable()
|
|
1119
|
+
|
|
1120
|
+
The following command::
|
|
1121
|
+
|
|
1122
|
+
sage: s = p.sum(v[i] for i in range(90))
|
|
1123
|
+
|
|
1124
|
+
is much more efficient than::
|
|
1125
|
+
|
|
1126
|
+
sage: s = sum(v[i] for i in range(90))
|
|
1127
|
+
"""
|
|
1128
|
+
d = {}
|
|
1129
|
+
for v in L:
|
|
1130
|
+
for id, coeff in v.iteritems():
|
|
1131
|
+
d[id] = coeff + d.get(id, 0)
|
|
1132
|
+
return self.linear_functions_parent()(d)
|
|
1133
|
+
|
|
1134
|
+
def get_backend(self):
|
|
1135
|
+
r"""
|
|
1136
|
+
Return the backend instance used.
|
|
1137
|
+
|
|
1138
|
+
This might be useful when access to additional functions provided by
|
|
1139
|
+
the backend is needed.
|
|
1140
|
+
|
|
1141
|
+
EXAMPLES:
|
|
1142
|
+
|
|
1143
|
+
This example prints a matrix coefficient::
|
|
1144
|
+
|
|
1145
|
+
sage: p = SemidefiniteProgram(solver='cvxopt')
|
|
1146
|
+
sage: x = p.new_variable()
|
|
1147
|
+
sage: a1 = matrix([[1, 2.], [2., 3.]])
|
|
1148
|
+
sage: a2 = matrix([[3, 4.], [4., 5.]])
|
|
1149
|
+
sage: p.add_constraint(a1*x[0] + a2*x[1] <= a1)
|
|
1150
|
+
sage: b = p.get_backend()
|
|
1151
|
+
sage: b.get_matrix()[0][0]
|
|
1152
|
+
(
|
|
1153
|
+
[-1.0 -2.0]
|
|
1154
|
+
-1, [-2.0 -3.0]
|
|
1155
|
+
)
|
|
1156
|
+
"""
|
|
1157
|
+
return self._backend
|
|
1158
|
+
|
|
1159
|
+
|
|
1160
|
+
class SDPSolverException(RuntimeError):
|
|
1161
|
+
r"""
|
|
1162
|
+
Exception raised when the solver fails.
|
|
1163
|
+
|
|
1164
|
+
``SDPSolverException`` is the exception raised when the solver fails.
|
|
1165
|
+
|
|
1166
|
+
EXAMPLES::
|
|
1167
|
+
|
|
1168
|
+
sage: from sage.numerical.sdp import SDPSolverException
|
|
1169
|
+
sage: SDPSolverException("Error")
|
|
1170
|
+
SDPSolverException('Error'...)
|
|
1171
|
+
|
|
1172
|
+
TESTS:
|
|
1173
|
+
|
|
1174
|
+
No solution::
|
|
1175
|
+
|
|
1176
|
+
sage: # needs cvxopt
|
|
1177
|
+
sage: p = SemidefiniteProgram(solver='cvxopt')
|
|
1178
|
+
sage: x = p.new_variable()
|
|
1179
|
+
sage: p.set_objective(x[0])
|
|
1180
|
+
sage: a = matrix([[1,2],[2,4]])
|
|
1181
|
+
sage: b = matrix([[1,9],[9,4]])
|
|
1182
|
+
sage: p.add_constraint(a*x[0] == b)
|
|
1183
|
+
sage: p.solve()
|
|
1184
|
+
Traceback (most recent call last):
|
|
1185
|
+
...
|
|
1186
|
+
SDPSolverException: ...
|
|
1187
|
+
|
|
1188
|
+
The value of the exception::
|
|
1189
|
+
|
|
1190
|
+
sage: from sage.numerical.sdp import SDPSolverException
|
|
1191
|
+
sage: e = SDPSolverException("Error")
|
|
1192
|
+
sage: print(e)
|
|
1193
|
+
Error
|
|
1194
|
+
"""
|
|
1195
|
+
pass
|
|
1196
|
+
|
|
1197
|
+
|
|
1198
|
+
cdef class SDPVariable(Element):
|
|
1199
|
+
r"""
|
|
1200
|
+
``SDPVariable`` is a variable used by the class
|
|
1201
|
+
``SemidefiniteProgram``.
|
|
1202
|
+
|
|
1203
|
+
.. warning::
|
|
1204
|
+
|
|
1205
|
+
You should not instantiate this class directly. Instead, use
|
|
1206
|
+
:meth:`SemidefiniteProgram.new_variable`.
|
|
1207
|
+
"""
|
|
1208
|
+
|
|
1209
|
+
def __init__(self, parent, sdp, name):
|
|
1210
|
+
r"""
|
|
1211
|
+
Constructor for ``SDPVariable``.
|
|
1212
|
+
|
|
1213
|
+
INPUT:
|
|
1214
|
+
|
|
1215
|
+
- ``parent`` -- :class:`SDPVariableParent`; the parent of the
|
|
1216
|
+
SDP variable
|
|
1217
|
+
|
|
1218
|
+
- ``sdp`` -- :class:`SemidefiniteProgram`; the
|
|
1219
|
+
underlying linear program
|
|
1220
|
+
|
|
1221
|
+
- ``name`` -- a name for the ``SDPVariable``
|
|
1222
|
+
|
|
1223
|
+
- ``lower_bound``, ``upper_bound`` -- lower bound and upper
|
|
1224
|
+
bound on the variable. Set to ``None`` to indicate that the
|
|
1225
|
+
variable is unbounded.
|
|
1226
|
+
|
|
1227
|
+
For more informations, see the method
|
|
1228
|
+
``SemidefiniteProgram.new_variable``.
|
|
1229
|
+
|
|
1230
|
+
EXAMPLES::
|
|
1231
|
+
|
|
1232
|
+
sage: p = SemidefiniteProgram()
|
|
1233
|
+
sage: p.new_variable()
|
|
1234
|
+
SDPVariable
|
|
1235
|
+
"""
|
|
1236
|
+
super().__init__(parent)
|
|
1237
|
+
self._dict = {}
|
|
1238
|
+
self._p = sdp
|
|
1239
|
+
self._name = name
|
|
1240
|
+
|
|
1241
|
+
def __getitem__(self, i):
|
|
1242
|
+
r"""
|
|
1243
|
+
Return the symbolic variable corresponding to the key.
|
|
1244
|
+
|
|
1245
|
+
Returns the element asked, otherwise creates it.
|
|
1246
|
+
|
|
1247
|
+
EXAMPLES::
|
|
1248
|
+
|
|
1249
|
+
sage: p = SemidefiniteProgram()
|
|
1250
|
+
sage: v = p.new_variable()
|
|
1251
|
+
sage: p.set_objective(v[0] + v[1])
|
|
1252
|
+
sage: v[0]
|
|
1253
|
+
x_0
|
|
1254
|
+
"""
|
|
1255
|
+
cdef int j
|
|
1256
|
+
if i in self._dict:
|
|
1257
|
+
return self._dict[i]
|
|
1258
|
+
zero = self._p._backend.zero()
|
|
1259
|
+
name = self._name + "[" + str(i) + "]" if self._name else None
|
|
1260
|
+
j = self._p._backend.add_variable( obj=zero, name=name)
|
|
1261
|
+
v = self._p.linear_function({j: 1})
|
|
1262
|
+
self._p._variables[v] = j
|
|
1263
|
+
self._dict[i] = v
|
|
1264
|
+
return v
|
|
1265
|
+
|
|
1266
|
+
def _repr_(self):
|
|
1267
|
+
r"""
|
|
1268
|
+
Return a representation of ``self``.
|
|
1269
|
+
|
|
1270
|
+
EXAMPLES::
|
|
1271
|
+
|
|
1272
|
+
sage: p = SemidefiniteProgram()
|
|
1273
|
+
sage: v = p.new_variable()
|
|
1274
|
+
sage: v
|
|
1275
|
+
SDPVariable
|
|
1276
|
+
"""
|
|
1277
|
+
return "SDPVariable"
|
|
1278
|
+
|
|
1279
|
+
def keys(self):
|
|
1280
|
+
r"""
|
|
1281
|
+
Return the keys already defined in the dictionary.
|
|
1282
|
+
|
|
1283
|
+
EXAMPLES::
|
|
1284
|
+
|
|
1285
|
+
sage: p = SemidefiniteProgram()
|
|
1286
|
+
sage: v = p.new_variable()
|
|
1287
|
+
sage: p.set_objective(v[0] + v[1])
|
|
1288
|
+
sage: sorted(v.keys())
|
|
1289
|
+
[0, 1]
|
|
1290
|
+
"""
|
|
1291
|
+
return self._dict.keys()
|
|
1292
|
+
|
|
1293
|
+
def items(self):
|
|
1294
|
+
r"""
|
|
1295
|
+
Return the pairs (keys,value) contained in the dictionary.
|
|
1296
|
+
|
|
1297
|
+
EXAMPLES::
|
|
1298
|
+
|
|
1299
|
+
sage: p = SemidefiniteProgram()
|
|
1300
|
+
sage: v = p.new_variable()
|
|
1301
|
+
sage: p.set_objective(v[0] + v[1])
|
|
1302
|
+
sage: sorted(v.items())
|
|
1303
|
+
[(0, x_0), (1, x_1)]
|
|
1304
|
+
"""
|
|
1305
|
+
return self._dict.items()
|
|
1306
|
+
|
|
1307
|
+
def values(self):
|
|
1308
|
+
r"""
|
|
1309
|
+
Return the symbolic variables associated to the current dictionary.
|
|
1310
|
+
|
|
1311
|
+
EXAMPLES::
|
|
1312
|
+
|
|
1313
|
+
sage: p = SemidefiniteProgram()
|
|
1314
|
+
sage: v = p.new_variable()
|
|
1315
|
+
sage: p.set_objective(v[0] + v[1])
|
|
1316
|
+
sage: sorted(v.values(), key=str)
|
|
1317
|
+
[x_0, x_1]
|
|
1318
|
+
"""
|
|
1319
|
+
return self._dict.values()
|
|
1320
|
+
|
|
1321
|
+
cdef _matrix_rmul_impl(self, m):
|
|
1322
|
+
"""
|
|
1323
|
+
Implement the action of a matrix multiplying from the right.
|
|
1324
|
+
"""
|
|
1325
|
+
result = dict()
|
|
1326
|
+
for i, row in enumerate(m.rows()):
|
|
1327
|
+
x = self[i]
|
|
1328
|
+
x_index, = x.dict().keys()
|
|
1329
|
+
result[x_index] = row
|
|
1330
|
+
from sage.modules.free_module import FreeModule
|
|
1331
|
+
V = FreeModule(self._p.base_ring(), m.ncols())
|
|
1332
|
+
T = self._p.linear_functions_parent().tensor(V)
|
|
1333
|
+
return T(result)
|
|
1334
|
+
|
|
1335
|
+
cdef _matrix_lmul_impl(self, m):
|
|
1336
|
+
"""
|
|
1337
|
+
Implement the action of a matrix multiplying from the left.
|
|
1338
|
+
"""
|
|
1339
|
+
result = dict()
|
|
1340
|
+
for i, col in enumerate(m.columns()):
|
|
1341
|
+
x = self[i]
|
|
1342
|
+
x_index, = x.dict().keys()
|
|
1343
|
+
result[x_index] = col
|
|
1344
|
+
from sage.modules.free_module import FreeModule
|
|
1345
|
+
V = FreeModule(self._p.base_ring(), m.nrows())
|
|
1346
|
+
T = self._p.linear_functions_parent().tensor(V)
|
|
1347
|
+
return T(result)
|
|
1348
|
+
|
|
1349
|
+
cpdef _acted_upon_(self, mat, bint self_on_left):
|
|
1350
|
+
"""
|
|
1351
|
+
Act with matrices on SDPVariables.
|
|
1352
|
+
|
|
1353
|
+
EXAMPLES::
|
|
1354
|
+
|
|
1355
|
+
sage: p = SemidefiniteProgram()
|
|
1356
|
+
sage: v = p.new_variable()
|
|
1357
|
+
sage: m = matrix([[1,2], [3,4]])
|
|
1358
|
+
sage: v * m
|
|
1359
|
+
(1.0, 2.0)*x_0 + (3.0, 4.0)*x_1
|
|
1360
|
+
sage: m * v
|
|
1361
|
+
(1.0, 3.0)*x_0 + (2.0, 4.0)*x_1
|
|
1362
|
+
"""
|
|
1363
|
+
from sage.structure.element import Matrix
|
|
1364
|
+
if isinstance(mat, Matrix):
|
|
1365
|
+
return self._matrix_rmul_impl(mat) if self_on_left else self._matrix_lmul_impl(mat)
|
|
1366
|
+
|
|
1367
|
+
|
|
1368
|
+
cdef class SDPVariableParent(Parent):
|
|
1369
|
+
"""
|
|
1370
|
+
Parent for :class:`SDPVariable`.
|
|
1371
|
+
|
|
1372
|
+
.. warning::
|
|
1373
|
+
|
|
1374
|
+
This class is for internal use. You should not instantiate it
|
|
1375
|
+
yourself. Use :meth:`SemidefiniteProgram.new_variable`
|
|
1376
|
+
to generate sdp variables.
|
|
1377
|
+
"""
|
|
1378
|
+
|
|
1379
|
+
Element = SDPVariable
|
|
1380
|
+
|
|
1381
|
+
def _repr_(self):
|
|
1382
|
+
r"""
|
|
1383
|
+
Return representation of ``self``.
|
|
1384
|
+
|
|
1385
|
+
OUTPUT: string
|
|
1386
|
+
|
|
1387
|
+
EXAMPLES::
|
|
1388
|
+
|
|
1389
|
+
sage: sdp.<v> = SemidefiniteProgram()
|
|
1390
|
+
sage: v.parent()
|
|
1391
|
+
Parent of SDPVariables
|
|
1392
|
+
"""
|
|
1393
|
+
return 'Parent of SDPVariables'
|
|
1394
|
+
|
|
1395
|
+
def _an_element_(self):
|
|
1396
|
+
"""
|
|
1397
|
+
Construct a SDP variable.
|
|
1398
|
+
|
|
1399
|
+
OUTPUT:
|
|
1400
|
+
|
|
1401
|
+
This is required for the coercion framework. We raise a
|
|
1402
|
+
:exc:`TypeError` to abort search for any coercion to another
|
|
1403
|
+
parent for binary operations. The only interesting operations
|
|
1404
|
+
involving :class:`SDPVariable` elements are actions by
|
|
1405
|
+
matrices.
|
|
1406
|
+
|
|
1407
|
+
EXAMPLES::
|
|
1408
|
+
|
|
1409
|
+
sage: sdp.<x> = SemidefiniteProgram()
|
|
1410
|
+
sage: parent = x.parent()
|
|
1411
|
+
sage: parent.an_element() # indirect doctest
|
|
1412
|
+
Traceback (most recent call last):
|
|
1413
|
+
...
|
|
1414
|
+
TypeError: disallow coercion
|
|
1415
|
+
"""
|
|
1416
|
+
raise TypeError('disallow coercion')
|
|
1417
|
+
|
|
1418
|
+
def _element_constructor_(self, sdp, name=""):
|
|
1419
|
+
"""
|
|
1420
|
+
The Element constructor.
|
|
1421
|
+
|
|
1422
|
+
INPUT/OUTPUT: see :meth:`SDPVariable.__init__`
|
|
1423
|
+
|
|
1424
|
+
EXAMPLES::
|
|
1425
|
+
|
|
1426
|
+
sage: sdp = SemidefiniteProgram()
|
|
1427
|
+
sage: sdp.new_variable() # indirect doctest
|
|
1428
|
+
SDPVariable
|
|
1429
|
+
"""
|
|
1430
|
+
return self.element_class(self, sdp, name)
|
|
1431
|
+
|
|
1432
|
+
|
|
1433
|
+
sdp_variable_parent = SDPVariableParent()
|