passagemath-polyhedra 10.6.37__cp314-cp314-musllinux_1_2_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- passagemath_polyhedra/__init__.py +3 -0
- passagemath_polyhedra-10.6.37.dist-info/METADATA +367 -0
- passagemath_polyhedra-10.6.37.dist-info/METADATA.bak +369 -0
- passagemath_polyhedra-10.6.37.dist-info/RECORD +209 -0
- passagemath_polyhedra-10.6.37.dist-info/WHEEL +5 -0
- passagemath_polyhedra-10.6.37.dist-info/top_level.txt +3 -0
- passagemath_polyhedra.libs/libgcc_s-0cd532bd.so.1 +0 -0
- passagemath_polyhedra.libs/libgmp-0e7fc84e.so.10.5.0 +0 -0
- passagemath_polyhedra.libs/libgomp-8949ffbe.so.1.0.0 +0 -0
- passagemath_polyhedra.libs/libstdc++-5d72f927.so.6.0.33 +0 -0
- sage/all__sagemath_polyhedra.py +50 -0
- sage/game_theory/all.py +8 -0
- sage/game_theory/catalog.py +6 -0
- sage/game_theory/catalog_normal_form_games.py +923 -0
- sage/game_theory/cooperative_game.py +844 -0
- sage/game_theory/matching_game.py +1181 -0
- sage/game_theory/normal_form_game.py +2697 -0
- sage/game_theory/parser.py +275 -0
- sage/geometry/all__sagemath_polyhedra.py +22 -0
- sage/geometry/cone.py +6940 -0
- sage/geometry/cone_catalog.py +847 -0
- sage/geometry/cone_critical_angles.py +1027 -0
- sage/geometry/convex_set.py +1119 -0
- sage/geometry/fan.py +3743 -0
- sage/geometry/fan_isomorphism.py +389 -0
- sage/geometry/fan_morphism.py +1884 -0
- sage/geometry/hasse_diagram.py +202 -0
- sage/geometry/hyperplane_arrangement/affine_subspace.py +390 -0
- sage/geometry/hyperplane_arrangement/all.py +1 -0
- sage/geometry/hyperplane_arrangement/arrangement.py +3905 -0
- sage/geometry/hyperplane_arrangement/check_freeness.py +145 -0
- sage/geometry/hyperplane_arrangement/hyperplane.py +773 -0
- sage/geometry/hyperplane_arrangement/library.py +825 -0
- sage/geometry/hyperplane_arrangement/ordered_arrangement.py +642 -0
- sage/geometry/hyperplane_arrangement/plot.py +520 -0
- sage/geometry/integral_points.py +35 -0
- sage/geometry/integral_points_generic_dense.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/integral_points_generic_dense.pyx +7 -0
- sage/geometry/lattice_polytope.py +5894 -0
- sage/geometry/linear_expression.py +773 -0
- sage/geometry/newton_polygon.py +767 -0
- sage/geometry/point_collection.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/point_collection.pyx +1008 -0
- sage/geometry/polyhedral_complex.py +2616 -0
- sage/geometry/polyhedron/all.py +8 -0
- sage/geometry/polyhedron/backend_cdd.py +460 -0
- sage/geometry/polyhedron/backend_cdd_rdf.py +231 -0
- sage/geometry/polyhedron/backend_field.py +347 -0
- sage/geometry/polyhedron/backend_normaliz.py +2503 -0
- sage/geometry/polyhedron/backend_number_field.py +168 -0
- sage/geometry/polyhedron/backend_polymake.py +765 -0
- sage/geometry/polyhedron/backend_ppl.py +582 -0
- sage/geometry/polyhedron/base.py +1206 -0
- sage/geometry/polyhedron/base0.py +1444 -0
- sage/geometry/polyhedron/base1.py +886 -0
- sage/geometry/polyhedron/base2.py +812 -0
- sage/geometry/polyhedron/base3.py +1845 -0
- sage/geometry/polyhedron/base4.py +1262 -0
- sage/geometry/polyhedron/base5.py +2700 -0
- sage/geometry/polyhedron/base6.py +1741 -0
- sage/geometry/polyhedron/base7.py +997 -0
- sage/geometry/polyhedron/base_QQ.py +1258 -0
- sage/geometry/polyhedron/base_RDF.py +98 -0
- sage/geometry/polyhedron/base_ZZ.py +934 -0
- sage/geometry/polyhedron/base_mutable.py +215 -0
- sage/geometry/polyhedron/base_number_field.py +122 -0
- sage/geometry/polyhedron/cdd_file_format.py +155 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/all.py +1 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/base.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd +76 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +3859 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd +39 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +1038 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/conversions.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pxd +9 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx +501 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd +207 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +102 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +2274 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd +370 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pyx +84 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd +31 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx +587 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd +52 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx +560 -0
- sage/geometry/polyhedron/constructor.py +773 -0
- sage/geometry/polyhedron/double_description.py +753 -0
- sage/geometry/polyhedron/double_description_inhomogeneous.py +564 -0
- sage/geometry/polyhedron/face.py +1060 -0
- sage/geometry/polyhedron/generating_function.py +1810 -0
- sage/geometry/polyhedron/lattice_euclidean_group_element.py +178 -0
- sage/geometry/polyhedron/library.py +3502 -0
- sage/geometry/polyhedron/misc.py +121 -0
- sage/geometry/polyhedron/modules/all.py +1 -0
- sage/geometry/polyhedron/modules/formal_polyhedra_module.py +155 -0
- sage/geometry/polyhedron/palp_database.py +447 -0
- sage/geometry/polyhedron/parent.py +1279 -0
- sage/geometry/polyhedron/plot.py +1986 -0
- sage/geometry/polyhedron/ppl_lattice_polygon.py +556 -0
- sage/geometry/polyhedron/ppl_lattice_polytope.py +1257 -0
- sage/geometry/polyhedron/representation.py +1723 -0
- sage/geometry/pseudolines.py +515 -0
- sage/geometry/relative_interior.py +445 -0
- sage/geometry/toric_plotter.py +1103 -0
- sage/geometry/triangulation/all.py +2 -0
- sage/geometry/triangulation/base.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/triangulation/base.pyx +963 -0
- sage/geometry/triangulation/data.h +147 -0
- sage/geometry/triangulation/data.pxd +4 -0
- sage/geometry/triangulation/element.py +914 -0
- sage/geometry/triangulation/functions.h +10 -0
- sage/geometry/triangulation/functions.pxd +4 -0
- sage/geometry/triangulation/point_configuration.py +2256 -0
- sage/geometry/triangulation/triangulations.h +49 -0
- sage/geometry/triangulation/triangulations.pxd +7 -0
- sage/geometry/voronoi_diagram.py +319 -0
- sage/interfaces/all__sagemath_polyhedra.py +1 -0
- sage/interfaces/polymake.py +2028 -0
- sage/numerical/all.py +13 -0
- sage/numerical/all__sagemath_polyhedra.py +11 -0
- sage/numerical/backends/all.py +1 -0
- sage/numerical/backends/all__sagemath_polyhedra.py +1 -0
- sage/numerical/backends/cvxopt_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/cvxopt_backend.pyx +1006 -0
- sage/numerical/backends/cvxopt_backend_test.py +19 -0
- sage/numerical/backends/cvxopt_sdp_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/cvxopt_sdp_backend.pyx +382 -0
- sage/numerical/backends/cvxpy_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/cvxpy_backend.pxd +41 -0
- sage/numerical/backends/cvxpy_backend.pyx +934 -0
- sage/numerical/backends/cvxpy_backend_test.py +13 -0
- sage/numerical/backends/generic_backend_test.py +24 -0
- sage/numerical/backends/interactivelp_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/interactivelp_backend.pxd +36 -0
- sage/numerical/backends/interactivelp_backend.pyx +1231 -0
- sage/numerical/backends/interactivelp_backend_test.py +12 -0
- sage/numerical/backends/logging_backend.py +391 -0
- sage/numerical/backends/matrix_sdp_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/matrix_sdp_backend.pxd +15 -0
- sage/numerical/backends/matrix_sdp_backend.pyx +478 -0
- sage/numerical/backends/ppl_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/ppl_backend.pyx +1126 -0
- sage/numerical/backends/ppl_backend_test.py +13 -0
- sage/numerical/backends/scip_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/scip_backend.pxd +22 -0
- sage/numerical/backends/scip_backend.pyx +1289 -0
- sage/numerical/backends/scip_backend_test.py +13 -0
- sage/numerical/interactive_simplex_method.py +5338 -0
- sage/numerical/knapsack.py +665 -0
- sage/numerical/linear_functions.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/linear_functions.pxd +31 -0
- sage/numerical/linear_functions.pyx +1648 -0
- sage/numerical/linear_tensor.py +470 -0
- sage/numerical/linear_tensor_constraints.py +448 -0
- sage/numerical/linear_tensor_element.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/linear_tensor_element.pxd +6 -0
- sage/numerical/linear_tensor_element.pyx +459 -0
- sage/numerical/mip.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/mip.pxd +40 -0
- sage/numerical/mip.pyx +3667 -0
- sage/numerical/sdp.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/sdp.pxd +39 -0
- sage/numerical/sdp.pyx +1433 -0
- sage/rings/all__sagemath_polyhedra.py +3 -0
- sage/rings/polynomial/all__sagemath_polyhedra.py +10 -0
- sage/rings/polynomial/omega.py +982 -0
- sage/schemes/all__sagemath_polyhedra.py +2 -0
- sage/schemes/toric/all.py +10 -0
- sage/schemes/toric/chow_group.py +1248 -0
- sage/schemes/toric/divisor.py +2082 -0
- sage/schemes/toric/divisor_class.cpython-314-x86_64-linux-musl.so +0 -0
- sage/schemes/toric/divisor_class.pyx +322 -0
- sage/schemes/toric/fano_variety.py +1606 -0
- sage/schemes/toric/homset.py +650 -0
- sage/schemes/toric/ideal.py +451 -0
- sage/schemes/toric/library.py +1322 -0
- sage/schemes/toric/morphism.py +1958 -0
- sage/schemes/toric/points.py +1032 -0
- sage/schemes/toric/sheaf/all.py +1 -0
- sage/schemes/toric/sheaf/constructor.py +302 -0
- sage/schemes/toric/sheaf/klyachko.py +921 -0
- sage/schemes/toric/toric_subscheme.py +905 -0
- sage/schemes/toric/variety.py +3460 -0
- sage/schemes/toric/weierstrass.py +1078 -0
- sage/schemes/toric/weierstrass_covering.py +457 -0
- sage/schemes/toric/weierstrass_higher.py +288 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.info +10 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v03 +0 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v04 +0 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v05 +1 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v06 +1 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.info +22 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v04 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v05 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v06 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v07 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v08 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v09 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v10 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v11 +1 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v12 +1 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v13 +1 -0
- sage_wheels/share/reflexive_polytopes/reflexive_polytopes_2d +80 -0
- sage_wheels/share/reflexive_polytopes/reflexive_polytopes_3d +37977 -0
|
@@ -0,0 +1,1648 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-polyhedra
|
|
2
|
+
"""
|
|
3
|
+
Linear Functions and Constraints
|
|
4
|
+
|
|
5
|
+
This module implements linear functions (see :class:`LinearFunction`)
|
|
6
|
+
in formal variables and chained (in)equalities between them (see
|
|
7
|
+
:class:`LinearConstraint`). By convention, these are always written as
|
|
8
|
+
either equalities or less-or-equal. For example::
|
|
9
|
+
|
|
10
|
+
sage: p = MixedIntegerLinearProgram()
|
|
11
|
+
sage: x = p.new_variable()
|
|
12
|
+
sage: f = 1 + x[1] + 2*x[2]; f # a linear function
|
|
13
|
+
1 + x_0 + 2*x_1
|
|
14
|
+
sage: type(f)
|
|
15
|
+
<class 'sage.numerical.linear_functions.LinearFunction'>
|
|
16
|
+
|
|
17
|
+
sage: c = (0 <= f); c # a constraint
|
|
18
|
+
0 <= 1 + x_0 + 2*x_1
|
|
19
|
+
sage: type(c)
|
|
20
|
+
<class 'sage.numerical.linear_functions.LinearConstraint'>
|
|
21
|
+
|
|
22
|
+
Note that you can use this module without any reference to linear
|
|
23
|
+
programming, it only implements linear functions over a base ring and
|
|
24
|
+
constraints. However, for ease of demonstration we will always
|
|
25
|
+
construct them out of linear programs (see
|
|
26
|
+
:mod:`~sage.numerical.mip`).
|
|
27
|
+
|
|
28
|
+
Constraints can be equations or (non-strict) inequalities. They can be
|
|
29
|
+
chained::
|
|
30
|
+
|
|
31
|
+
sage: p = MixedIntegerLinearProgram()
|
|
32
|
+
sage: x = p.new_variable()
|
|
33
|
+
sage: x[0] == x[1] == x[2] == x[3]
|
|
34
|
+
x_0 == x_1 == x_2 == x_3
|
|
35
|
+
|
|
36
|
+
sage: ieq_01234 = x[0] <= x[1] <= x[2] <= x[3] <= x[4]
|
|
37
|
+
sage: ieq_01234
|
|
38
|
+
x_0 <= x_1 <= x_2 <= x_3 <= x_4
|
|
39
|
+
|
|
40
|
+
If necessary, the direction of inequality is flipped to always write
|
|
41
|
+
inequalities as less or equal::
|
|
42
|
+
|
|
43
|
+
sage: x[5] >= ieq_01234
|
|
44
|
+
x_0 <= x_1 <= x_2 <= x_3 <= x_4 <= x_5
|
|
45
|
+
|
|
46
|
+
sage: (x[5] <= x[6]) >= ieq_01234
|
|
47
|
+
x_0 <= x_1 <= x_2 <= x_3 <= x_4 <= x_5 <= x_6
|
|
48
|
+
sage: (x[5] <= x[6]) <= ieq_01234
|
|
49
|
+
x_5 <= x_6 <= x_0 <= x_1 <= x_2 <= x_3 <= x_4
|
|
50
|
+
|
|
51
|
+
.. WARNING::
|
|
52
|
+
|
|
53
|
+
The implementation of chained inequalities uses a Python hack to
|
|
54
|
+
make it work, so it is not completely robust. In particular, while
|
|
55
|
+
constants are allowed, no two constants can appear next to
|
|
56
|
+
each other. The following does not work for example::
|
|
57
|
+
|
|
58
|
+
sage: x[0] <= 3 <= 4
|
|
59
|
+
True
|
|
60
|
+
|
|
61
|
+
If you really need this for some reason, you can explicitly convert
|
|
62
|
+
the constants to a :class:`LinearFunction`::
|
|
63
|
+
|
|
64
|
+
sage: from sage.numerical.linear_functions import LinearFunctionsParent
|
|
65
|
+
sage: LF = LinearFunctionsParent(QQ)
|
|
66
|
+
sage: x[1] <= LF(3) <= LF(4)
|
|
67
|
+
x_1 <= 3 <= 4
|
|
68
|
+
|
|
69
|
+
TESTS:
|
|
70
|
+
|
|
71
|
+
This was fixed in :issue:`24423`::
|
|
72
|
+
|
|
73
|
+
sage: p.<x> = MixedIntegerLinearProgram()
|
|
74
|
+
sage: from sage.numerical.linear_functions import LinearFunctionsParent
|
|
75
|
+
sage: LF = LinearFunctionsParent(QQ)
|
|
76
|
+
sage: 3 <= x[0] <= LF(4)
|
|
77
|
+
3 <= x_0 <= 4
|
|
78
|
+
|
|
79
|
+
See :issue:`12091`::
|
|
80
|
+
|
|
81
|
+
sage: p = MixedIntegerLinearProgram()
|
|
82
|
+
sage: b = p.new_variable()
|
|
83
|
+
sage: b[0] <= b[1] <= 2
|
|
84
|
+
x_0 <= x_1 <= 2
|
|
85
|
+
sage: list(b[0] <= b[1] <= 2)
|
|
86
|
+
[x_0, x_1, 2]
|
|
87
|
+
sage: 1 >= b[1] >= 2*b[0]
|
|
88
|
+
2*x_0 <= x_1 <= 1
|
|
89
|
+
sage: b[2] >= b[1] >= 2*b[0]
|
|
90
|
+
2*x_0 <= x_1 <= x_2
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
# ****************************************************************************
|
|
94
|
+
# Copyright (C) 2012 Nathann Cohen <nathann.cohen@gmail.com>
|
|
95
|
+
# Copyright (C) 2012 Volker Braun <vbraun.name@gmail.com>
|
|
96
|
+
# Copyright (C) 2016 Jeroen Demeyer <jdemeyer@cage.ugent.be>
|
|
97
|
+
#
|
|
98
|
+
# This program is free software: you can redistribute it and/or modify
|
|
99
|
+
# it under the terms of the GNU General Public License as published by
|
|
100
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
101
|
+
# (at your option) any later version.
|
|
102
|
+
# https://www.gnu.org/licenses/
|
|
103
|
+
# ****************************************************************************
|
|
104
|
+
|
|
105
|
+
from cpython.object cimport Py_EQ, Py_GE, Py_LE, Py_GT, Py_LT
|
|
106
|
+
|
|
107
|
+
from sage.misc.fast_methods cimport hash_by_id
|
|
108
|
+
from sage.structure.parent cimport Parent
|
|
109
|
+
from sage.structure.element cimport ModuleElement, Element
|
|
110
|
+
from sage.misc.cachefunc import cached_function
|
|
111
|
+
|
|
112
|
+
#*****************************************************************************
|
|
113
|
+
#
|
|
114
|
+
# Utility functions to test that something is a linear function / constraint
|
|
115
|
+
#
|
|
116
|
+
#*****************************************************************************
|
|
117
|
+
|
|
118
|
+
cpdef is_LinearFunction(x):
|
|
119
|
+
"""
|
|
120
|
+
Test whether ``x`` is a linear function.
|
|
121
|
+
|
|
122
|
+
INPUT:
|
|
123
|
+
|
|
124
|
+
- ``x`` -- anything
|
|
125
|
+
|
|
126
|
+
OUTPUT: boolean
|
|
127
|
+
|
|
128
|
+
EXAMPLES::
|
|
129
|
+
|
|
130
|
+
sage: p = MixedIntegerLinearProgram()
|
|
131
|
+
sage: x = p.new_variable()
|
|
132
|
+
sage: from sage.numerical.linear_functions import is_LinearFunction
|
|
133
|
+
sage: is_LinearFunction(x[0] - 2*x[2])
|
|
134
|
+
doctest:warning...
|
|
135
|
+
DeprecationWarning: The function is_LinearFunction is deprecated;
|
|
136
|
+
use 'isinstance(..., LinearFunction)' instead.
|
|
137
|
+
See https://github.com/sagemath/sage/issues/38184 for details.
|
|
138
|
+
True
|
|
139
|
+
sage: is_LinearFunction('a string')
|
|
140
|
+
False
|
|
141
|
+
"""
|
|
142
|
+
from sage.misc.superseded import deprecation_cython
|
|
143
|
+
deprecation_cython(38184,
|
|
144
|
+
"The function is_LinearFunction is deprecated; "
|
|
145
|
+
"use 'isinstance(..., LinearFunction)' instead.")
|
|
146
|
+
return isinstance(x, LinearFunction)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def is_LinearConstraint(x):
|
|
150
|
+
"""
|
|
151
|
+
Test whether ``x`` is a linear constraint.
|
|
152
|
+
|
|
153
|
+
INPUT:
|
|
154
|
+
|
|
155
|
+
- ``x`` -- anything
|
|
156
|
+
|
|
157
|
+
OUTPUT: boolean
|
|
158
|
+
|
|
159
|
+
EXAMPLES::
|
|
160
|
+
|
|
161
|
+
sage: p = MixedIntegerLinearProgram()
|
|
162
|
+
sage: x = p.new_variable()
|
|
163
|
+
sage: ieq = (x[0] <= x[1])
|
|
164
|
+
sage: from sage.numerical.linear_functions import is_LinearConstraint
|
|
165
|
+
sage: is_LinearConstraint(ieq)
|
|
166
|
+
doctest:warning...
|
|
167
|
+
DeprecationWarning: The function is_LinearConstraint is deprecated;
|
|
168
|
+
use 'isinstance(..., LinearConstraint)' instead.
|
|
169
|
+
See https://github.com/sagemath/sage/issues/38184 for details.
|
|
170
|
+
True
|
|
171
|
+
sage: is_LinearConstraint('a string')
|
|
172
|
+
False
|
|
173
|
+
"""
|
|
174
|
+
from sage.misc.superseded import deprecation_cython
|
|
175
|
+
deprecation_cython(38184,
|
|
176
|
+
"The function is_LinearConstraint is deprecated; "
|
|
177
|
+
"use 'isinstance(..., LinearConstraint)' instead.")
|
|
178
|
+
return isinstance(x, LinearConstraint)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
# ****************************************************************************
|
|
182
|
+
#
|
|
183
|
+
# Factory functions for the parents to ensure uniqueness
|
|
184
|
+
#
|
|
185
|
+
# ****************************************************************************
|
|
186
|
+
|
|
187
|
+
@cached_function
|
|
188
|
+
def LinearFunctionsParent(base_ring):
|
|
189
|
+
"""
|
|
190
|
+
Return the parent for linear functions over ``base_ring``.
|
|
191
|
+
|
|
192
|
+
The output is cached, so only a single parent is ever constructed
|
|
193
|
+
for a given base ring.
|
|
194
|
+
|
|
195
|
+
INPUT:
|
|
196
|
+
|
|
197
|
+
- ``base_ring`` -- a ring; the coefficient ring for the linear
|
|
198
|
+
functions
|
|
199
|
+
|
|
200
|
+
OUTPUT: the parent of the linear functions over ``base_ring``
|
|
201
|
+
|
|
202
|
+
EXAMPLES::
|
|
203
|
+
|
|
204
|
+
sage: from sage.numerical.linear_functions import LinearFunctionsParent
|
|
205
|
+
sage: LinearFunctionsParent(QQ)
|
|
206
|
+
Linear functions over Rational Field
|
|
207
|
+
"""
|
|
208
|
+
return LinearFunctionsParent_class(base_ring)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
@cached_function
|
|
212
|
+
def LinearConstraintsParent(linear_functions_parent):
|
|
213
|
+
"""
|
|
214
|
+
Return the parent for linear functions over ``base_ring``.
|
|
215
|
+
|
|
216
|
+
The output is cached, so only a single parent is ever constructed
|
|
217
|
+
for a given base ring.
|
|
218
|
+
|
|
219
|
+
INPUT:
|
|
220
|
+
|
|
221
|
+
- ``linear_functions_parent`` -- a
|
|
222
|
+
:class:`LinearFunctionsParent_class`; the type of linear
|
|
223
|
+
functions that the constraints are made out of
|
|
224
|
+
|
|
225
|
+
OUTPUT: the parent of the linear constraints with the given linear functions
|
|
226
|
+
|
|
227
|
+
EXAMPLES::
|
|
228
|
+
|
|
229
|
+
sage: from sage.numerical.linear_functions import (
|
|
230
|
+
....: LinearFunctionsParent, LinearConstraintsParent)
|
|
231
|
+
sage: LF = LinearFunctionsParent(QQ)
|
|
232
|
+
sage: LinearConstraintsParent(LF)
|
|
233
|
+
Linear constraints over Rational Field
|
|
234
|
+
"""
|
|
235
|
+
return LinearConstraintsParent_class(linear_functions_parent)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
# ****************************************************************************
|
|
239
|
+
#
|
|
240
|
+
# Elements of linear functions or constraints
|
|
241
|
+
#
|
|
242
|
+
# ****************************************************************************
|
|
243
|
+
|
|
244
|
+
cdef chained_comparator_left = None
|
|
245
|
+
cdef chained_comparator_right = None
|
|
246
|
+
cdef LinearConstraint chained_comparator_replace = None
|
|
247
|
+
|
|
248
|
+
cdef class LinearFunctionOrConstraint(ModuleElement):
|
|
249
|
+
"""
|
|
250
|
+
Base class for :class:`LinearFunction` and :class:`LinearConstraint`.
|
|
251
|
+
|
|
252
|
+
This class exists solely to implement chaining of inequalities
|
|
253
|
+
in constraints.
|
|
254
|
+
"""
|
|
255
|
+
def __richcmp__(self, other, int op):
|
|
256
|
+
"""
|
|
257
|
+
Create an inequality or equality object, possibly chaining
|
|
258
|
+
several together.
|
|
259
|
+
|
|
260
|
+
EXAMPLES::
|
|
261
|
+
|
|
262
|
+
sage: p.<x> = MixedIntegerLinearProgram()
|
|
263
|
+
sage: x[0].__le__(x[1])
|
|
264
|
+
x_0 <= x_1
|
|
265
|
+
|
|
266
|
+
::
|
|
267
|
+
|
|
268
|
+
sage: p = MixedIntegerLinearProgram()
|
|
269
|
+
sage: LF = p.linear_functions_parent()
|
|
270
|
+
sage: from sage.numerical.linear_functions import LinearFunction
|
|
271
|
+
sage: LF({2 : 5, 3 : 2}) <= LF({2 : 3, 9 : 2})
|
|
272
|
+
5*x_2 + 2*x_3 <= 3*x_2 + 2*x_9
|
|
273
|
+
|
|
274
|
+
sage: LF({2 : 5, 3 : 2}) >= LF({2 : 3, 9 : 2})
|
|
275
|
+
3*x_2 + 2*x_9 <= 5*x_2 + 2*x_3
|
|
276
|
+
|
|
277
|
+
sage: LF({2 : 5, 3 : 2}) == LF({2 : 3, 9 : 2})
|
|
278
|
+
5*x_2 + 2*x_3 == 3*x_2 + 2*x_9
|
|
279
|
+
|
|
280
|
+
We can chain multiple (in)equalities::
|
|
281
|
+
|
|
282
|
+
sage: p.<b> = MixedIntegerLinearProgram()
|
|
283
|
+
sage: b[0] == 1 == b[1] == 2 == b[2] == 3
|
|
284
|
+
x_0 == 1 == x_1 == 2 == x_2 == 3
|
|
285
|
+
sage: b[0] <= 1 <= b[1] <= 2 <= b[2] <= 3
|
|
286
|
+
x_0 <= 1 <= x_1 <= 2 <= x_2 <= 3
|
|
287
|
+
sage: b[0] <= b[1] <= b[2] <= b[3]
|
|
288
|
+
x_0 <= x_1 <= x_2 <= x_3
|
|
289
|
+
|
|
290
|
+
Other comparison operators are not allowed::
|
|
291
|
+
|
|
292
|
+
sage: b[0] < b[1]
|
|
293
|
+
Traceback (most recent call last):
|
|
294
|
+
...
|
|
295
|
+
ValueError: strict < is not allowed, use <= instead
|
|
296
|
+
sage: b[0] > b[1]
|
|
297
|
+
Traceback (most recent call last):
|
|
298
|
+
...
|
|
299
|
+
ValueError: strict > is not allowed, use >= instead
|
|
300
|
+
sage: b[0] != b[1]
|
|
301
|
+
Traceback (most recent call last):
|
|
302
|
+
...
|
|
303
|
+
ValueError: inequality != is not allowed, use one of <=, ==, >=
|
|
304
|
+
|
|
305
|
+
Mixing operators is also not allowed::
|
|
306
|
+
|
|
307
|
+
sage: 1 <= b[1] >= 2
|
|
308
|
+
Traceback (most recent call last):
|
|
309
|
+
...
|
|
310
|
+
ValueError: incorrectly chained inequality
|
|
311
|
+
sage: 1 >= b[1] <= 2
|
|
312
|
+
Traceback (most recent call last):
|
|
313
|
+
...
|
|
314
|
+
ValueError: incorrectly chained inequality
|
|
315
|
+
sage: 1 == b[1] <= 2
|
|
316
|
+
Traceback (most recent call last):
|
|
317
|
+
...
|
|
318
|
+
ValueError: cannot mix equations and inequalities
|
|
319
|
+
sage: 1 >= b[1] == 2
|
|
320
|
+
Traceback (most recent call last):
|
|
321
|
+
...
|
|
322
|
+
ValueError: cannot mix equations and inequalities
|
|
323
|
+
|
|
324
|
+
TESTS::
|
|
325
|
+
|
|
326
|
+
sage: p.<x> = MixedIntegerLinearProgram()
|
|
327
|
+
sage: LF = p.linear_functions_parent()
|
|
328
|
+
sage: cm = sage.structure.element.get_coercion_model()
|
|
329
|
+
sage: cm.explain(10, LF(1), operator.le)
|
|
330
|
+
Coercion on left operand via
|
|
331
|
+
Coercion map:
|
|
332
|
+
From: Integer Ring
|
|
333
|
+
To: Linear functions over Real Double Field
|
|
334
|
+
Arithmetic performed after coercions.
|
|
335
|
+
Result lives in Linear functions over Real Double Field
|
|
336
|
+
Linear functions over Real Double Field
|
|
337
|
+
|
|
338
|
+
sage: operator.le(10, x[0])
|
|
339
|
+
10 <= x_0
|
|
340
|
+
sage: x[0] <= 1
|
|
341
|
+
x_0 <= 1
|
|
342
|
+
sage: x[0] >= 1
|
|
343
|
+
1 <= x_0
|
|
344
|
+
sage: 1 <= x[0]
|
|
345
|
+
1 <= x_0
|
|
346
|
+
sage: 1 >= x[0]
|
|
347
|
+
x_0 <= 1
|
|
348
|
+
|
|
349
|
+
This works with non-Sage types too, see :issue:`14540`::
|
|
350
|
+
|
|
351
|
+
sage: p.<b> = MixedIntegerLinearProgram()
|
|
352
|
+
sage: int(1) <= b[0] <= int(2)
|
|
353
|
+
1 <= x_0 <= 2
|
|
354
|
+
sage: int(1) >= b[0] >= int(2)
|
|
355
|
+
2 <= x_0 <= 1
|
|
356
|
+
sage: int(1) == b[0] == int(2)
|
|
357
|
+
1 == x_0 == 2
|
|
358
|
+
sage: float(0) <= b[0] <= int(1) <= b[1] <= float(2)
|
|
359
|
+
0 <= x_0 <= 1 <= x_1 <= 2
|
|
360
|
+
"""
|
|
361
|
+
# Store the chaining variables and set them to None for now in
|
|
362
|
+
# order to have a sane state for any conversions below or when
|
|
363
|
+
# an exception is raised.
|
|
364
|
+
global chained_comparator_left
|
|
365
|
+
global chained_comparator_right
|
|
366
|
+
global chained_comparator_replace
|
|
367
|
+
|
|
368
|
+
chain_left = chained_comparator_left
|
|
369
|
+
chain_right = chained_comparator_right
|
|
370
|
+
chain_replace = chained_comparator_replace
|
|
371
|
+
|
|
372
|
+
chained_comparator_left = None
|
|
373
|
+
chained_comparator_right = None
|
|
374
|
+
chained_comparator_replace = None
|
|
375
|
+
|
|
376
|
+
# Assign {self, other} to {py_left, py_right} such that
|
|
377
|
+
# py_left <= py_right.
|
|
378
|
+
cdef bint equality = False
|
|
379
|
+
if op == Py_LE:
|
|
380
|
+
py_left, py_right = self, other
|
|
381
|
+
elif op == Py_GE:
|
|
382
|
+
py_left, py_right = other, self
|
|
383
|
+
elif op == Py_EQ:
|
|
384
|
+
py_left, py_right = self, other
|
|
385
|
+
equality = True
|
|
386
|
+
elif op == Py_LT:
|
|
387
|
+
raise ValueError("strict < is not allowed, use <= instead")
|
|
388
|
+
elif op == Py_GT:
|
|
389
|
+
raise ValueError("strict > is not allowed, use >= instead")
|
|
390
|
+
else:
|
|
391
|
+
raise ValueError("inequality != is not allowed, use one of <=, ==, >=")
|
|
392
|
+
|
|
393
|
+
# Convert py_left and py_right to constraints left and right,
|
|
394
|
+
# possibly replacing them to implement the hack below.
|
|
395
|
+
cdef LinearConstraint left = None
|
|
396
|
+
cdef LinearConstraint right = None
|
|
397
|
+
|
|
398
|
+
# HACK to allow chained constraints: Python translates
|
|
399
|
+
# x <= y <= z into:
|
|
400
|
+
#
|
|
401
|
+
# temp = x <= y # calls x.__richcmp__(y)
|
|
402
|
+
# if temp: # calls temp.__bool__()
|
|
403
|
+
# return y <= z # calls y.__richcmp__(z)
|
|
404
|
+
# else:
|
|
405
|
+
# return temp
|
|
406
|
+
#
|
|
407
|
+
# or, if x <= y is not implemented (for example, if x is a
|
|
408
|
+
# non-Sage type):
|
|
409
|
+
#
|
|
410
|
+
# temp = y >= x # calls y.__richcmp__(x)
|
|
411
|
+
# if temp: # calls temp.__bool__()
|
|
412
|
+
# return y <= z # calls y.__richcmp__(z)
|
|
413
|
+
# else:
|
|
414
|
+
# return temp
|
|
415
|
+
#
|
|
416
|
+
# but we would like x <= y <= z as output. The trick to make it
|
|
417
|
+
# work is to store x and y in the first call to __richcmp__
|
|
418
|
+
# and temp in the call to __bool__. Then we can replace x
|
|
419
|
+
# or y by x <= y in the second call to __richcmp__.
|
|
420
|
+
if chain_replace is not None:
|
|
421
|
+
if chain_replace.equality != equality:
|
|
422
|
+
raise ValueError("cannot mix equations and inequalities")
|
|
423
|
+
|
|
424
|
+
# First, check for correctly-chained inequalities
|
|
425
|
+
# x <= y <= z or z <= y <= x.
|
|
426
|
+
if py_left is chain_right:
|
|
427
|
+
left = chain_replace
|
|
428
|
+
elif py_right is chain_left:
|
|
429
|
+
right = chain_replace
|
|
430
|
+
# Next, check for incorrectly chained inequalities like
|
|
431
|
+
# x <= y >= z. If we are dealing with inequalities, this
|
|
432
|
+
# is an error. For an equality, we fix the chaining by
|
|
433
|
+
# reversing one of the sides.
|
|
434
|
+
elif py_left is chain_left:
|
|
435
|
+
if not equality:
|
|
436
|
+
raise ValueError("incorrectly chained inequality")
|
|
437
|
+
chain_replace.constraints.reverse()
|
|
438
|
+
left = chain_replace
|
|
439
|
+
elif py_right is chain_right:
|
|
440
|
+
if not equality:
|
|
441
|
+
raise ValueError("incorrectly chained inequality")
|
|
442
|
+
left = chain_replace
|
|
443
|
+
py_right = py_left
|
|
444
|
+
|
|
445
|
+
# Figure out the correct parent to work with: if we did a
|
|
446
|
+
# replacement, its parent takes priority.
|
|
447
|
+
if left is not None:
|
|
448
|
+
LC = left._parent
|
|
449
|
+
elif right is not None:
|
|
450
|
+
LC = right._parent
|
|
451
|
+
else:
|
|
452
|
+
LC = (<LinearFunctionOrConstraint>self)._parent
|
|
453
|
+
|
|
454
|
+
# We want the parent to be a LinearConstraintsParent
|
|
455
|
+
if not isinstance(LC, LinearConstraintsParent_class):
|
|
456
|
+
LC = LinearConstraintsParent(LC)
|
|
457
|
+
|
|
458
|
+
if left is None:
|
|
459
|
+
try:
|
|
460
|
+
left = <LinearConstraint?>py_left
|
|
461
|
+
except TypeError:
|
|
462
|
+
left = <LinearConstraint>LC(py_left, equality=equality)
|
|
463
|
+
else:
|
|
464
|
+
if left._parent is not LC:
|
|
465
|
+
left = <LinearConstraint>LC(left.constraints, equality=left.equality)
|
|
466
|
+
|
|
467
|
+
if right is None:
|
|
468
|
+
try:
|
|
469
|
+
right = <LinearConstraint?>py_right
|
|
470
|
+
except TypeError:
|
|
471
|
+
right = <LinearConstraint>LC(py_right, equality=equality)
|
|
472
|
+
else:
|
|
473
|
+
if right._parent is not LC:
|
|
474
|
+
right = <LinearConstraint>LC(right.constraints, equality=right.equality)
|
|
475
|
+
|
|
476
|
+
# Finally, chain the (in)equalities
|
|
477
|
+
if left.equality != equality or right.equality != equality:
|
|
478
|
+
raise ValueError("cannot mix equations and inequalities")
|
|
479
|
+
ret = LC(left.constraints + right.constraints, equality=equality)
|
|
480
|
+
|
|
481
|
+
# Everything done, so we set the chaining variables
|
|
482
|
+
chained_comparator_left = py_left
|
|
483
|
+
chained_comparator_right = py_right
|
|
484
|
+
chained_comparator_replace = None
|
|
485
|
+
|
|
486
|
+
return ret
|
|
487
|
+
|
|
488
|
+
def __hash__(self):
|
|
489
|
+
r"""
|
|
490
|
+
Return a hash from the ``id()``.
|
|
491
|
+
|
|
492
|
+
EXAMPLES::
|
|
493
|
+
|
|
494
|
+
sage: p = MixedIntegerLinearProgram()
|
|
495
|
+
sage: LF = p.linear_functions_parent()
|
|
496
|
+
sage: f = LF({2 : 5, 3 : 2})
|
|
497
|
+
sage: hash(f) # indirect doctest; random
|
|
498
|
+
103987752
|
|
499
|
+
|
|
500
|
+
Since we hash by ``id()``, linear functions and constraints are
|
|
501
|
+
only considered equal for sets and dicts if they are the same
|
|
502
|
+
object::
|
|
503
|
+
|
|
504
|
+
sage: f = LF.0
|
|
505
|
+
sage: set([f, f])
|
|
506
|
+
{x_0}
|
|
507
|
+
sage: set([f, f+0])
|
|
508
|
+
{x_0, x_0}
|
|
509
|
+
sage: len(set([f, f+1]))
|
|
510
|
+
2
|
|
511
|
+
sage: d = {}
|
|
512
|
+
sage: d[f] = 123
|
|
513
|
+
sage: d[f+0] = 456
|
|
514
|
+
sage: list(d)
|
|
515
|
+
[x_0, x_0]
|
|
516
|
+
"""
|
|
517
|
+
return hash_by_id(<void*>self)
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
#*****************************************************************************
|
|
521
|
+
#
|
|
522
|
+
# Parent of linear functions
|
|
523
|
+
#
|
|
524
|
+
#*****************************************************************************
|
|
525
|
+
|
|
526
|
+
cdef class LinearFunctionsParent_class(Parent):
|
|
527
|
+
r"""
|
|
528
|
+
The parent for all linear functions over a fixed base ring.
|
|
529
|
+
|
|
530
|
+
.. warning::
|
|
531
|
+
|
|
532
|
+
You should use :func:`LinearFunctionsParent` to construct
|
|
533
|
+
instances of this class.
|
|
534
|
+
|
|
535
|
+
INPUT/OUTPUT: see :func:`LinearFunctionsParent`
|
|
536
|
+
|
|
537
|
+
EXAMPLES::
|
|
538
|
+
|
|
539
|
+
sage: from sage.numerical.linear_functions import LinearFunctionsParent_class
|
|
540
|
+
sage: LinearFunctionsParent_class
|
|
541
|
+
<class 'sage.numerical.linear_functions.LinearFunctionsParent_class'>
|
|
542
|
+
"""
|
|
543
|
+
def __cinit__(self):
|
|
544
|
+
"""
|
|
545
|
+
Cython initializer.
|
|
546
|
+
|
|
547
|
+
TESTS::
|
|
548
|
+
|
|
549
|
+
sage: from sage.numerical.linear_functions import LinearFunctionsParent_class
|
|
550
|
+
sage: LF = LinearFunctionsParent_class.__new__(LinearFunctionsParent_class)
|
|
551
|
+
sage: LF._multiplication_symbol
|
|
552
|
+
'*'
|
|
553
|
+
"""
|
|
554
|
+
# Do not use coercion framework for __richcmp__
|
|
555
|
+
self.flags |= Parent_richcmp_element_without_coercion
|
|
556
|
+
self._multiplication_symbol = '*'
|
|
557
|
+
|
|
558
|
+
def __init__(self, base_ring):
|
|
559
|
+
"""
|
|
560
|
+
The Python constructor.
|
|
561
|
+
|
|
562
|
+
TESTS::
|
|
563
|
+
|
|
564
|
+
sage: from sage.numerical.linear_functions import LinearFunctionsParent
|
|
565
|
+
sage: LinearFunctionsParent(RDF)
|
|
566
|
+
Linear functions over Real Double Field
|
|
567
|
+
"""
|
|
568
|
+
from sage.categories.modules_with_basis import ModulesWithBasis
|
|
569
|
+
Parent.__init__(self, base=base_ring, category=ModulesWithBasis(base_ring))
|
|
570
|
+
|
|
571
|
+
def set_multiplication_symbol(self, symbol='*'):
|
|
572
|
+
"""
|
|
573
|
+
Set the multiplication symbol when pretty-printing linear functions.
|
|
574
|
+
|
|
575
|
+
INPUT:
|
|
576
|
+
|
|
577
|
+
- ``symbol`` -- string (default: ``'*'``); the multiplication
|
|
578
|
+
symbol to be used
|
|
579
|
+
|
|
580
|
+
EXAMPLES::
|
|
581
|
+
|
|
582
|
+
sage: p = MixedIntegerLinearProgram()
|
|
583
|
+
sage: x = p.new_variable()
|
|
584
|
+
sage: f = -1-2*x[0]-3*x[1]
|
|
585
|
+
sage: LF = f.parent()
|
|
586
|
+
sage: LF._get_multiplication_symbol()
|
|
587
|
+
'*'
|
|
588
|
+
sage: f
|
|
589
|
+
-1 - 2*x_0 - 3*x_1
|
|
590
|
+
sage: LF.set_multiplication_symbol(' ')
|
|
591
|
+
sage: f
|
|
592
|
+
-1 - 2 x_0 - 3 x_1
|
|
593
|
+
sage: LF.set_multiplication_symbol()
|
|
594
|
+
sage: f
|
|
595
|
+
-1 - 2*x_0 - 3*x_1
|
|
596
|
+
"""
|
|
597
|
+
self._multiplication_symbol = symbol
|
|
598
|
+
|
|
599
|
+
def _get_multiplication_symbol(self):
|
|
600
|
+
"""
|
|
601
|
+
Return the multiplication symbol.
|
|
602
|
+
|
|
603
|
+
OUTPUT: string. By default, this is ``'*'``
|
|
604
|
+
|
|
605
|
+
EXAMPLES::
|
|
606
|
+
|
|
607
|
+
sage: LF = MixedIntegerLinearProgram().linear_functions_parent()
|
|
608
|
+
sage: LF._get_multiplication_symbol()
|
|
609
|
+
'*'
|
|
610
|
+
"""
|
|
611
|
+
return self._multiplication_symbol
|
|
612
|
+
|
|
613
|
+
def tensor(self, free_module):
|
|
614
|
+
"""
|
|
615
|
+
Return the tensor product with ``free_module``.
|
|
616
|
+
|
|
617
|
+
INPUT:
|
|
618
|
+
|
|
619
|
+
- ``free_module`` -- vector space or matrix space over the
|
|
620
|
+
same base ring
|
|
621
|
+
|
|
622
|
+
OUTPUT:
|
|
623
|
+
|
|
624
|
+
Instance of
|
|
625
|
+
:class:`sage.numerical.linear_tensor.LinearTensorParent_class`.
|
|
626
|
+
|
|
627
|
+
EXAMPLES::
|
|
628
|
+
|
|
629
|
+
sage: LF = MixedIntegerLinearProgram().linear_functions_parent()
|
|
630
|
+
sage: LF.tensor(RDF^3)
|
|
631
|
+
Tensor product of Vector space of dimension 3 over Real Double Field
|
|
632
|
+
and Linear functions over Real Double Field
|
|
633
|
+
sage: LF.tensor(QQ^2)
|
|
634
|
+
Traceback (most recent call last):
|
|
635
|
+
...
|
|
636
|
+
ValueError: base rings must match
|
|
637
|
+
"""
|
|
638
|
+
from sage.numerical.linear_tensor import LinearTensorParent
|
|
639
|
+
return LinearTensorParent(free_module, self)
|
|
640
|
+
|
|
641
|
+
def gen(self, i):
|
|
642
|
+
"""
|
|
643
|
+
Return the linear variable `x_i`.
|
|
644
|
+
|
|
645
|
+
INPUT:
|
|
646
|
+
|
|
647
|
+
- ``i`` -- nonnegative integer
|
|
648
|
+
|
|
649
|
+
OUTPUT: the linear function `x_i`
|
|
650
|
+
|
|
651
|
+
EXAMPLES::
|
|
652
|
+
|
|
653
|
+
sage: LF = MixedIntegerLinearProgram().linear_functions_parent()
|
|
654
|
+
sage: LF.gen(23)
|
|
655
|
+
x_23
|
|
656
|
+
"""
|
|
657
|
+
return LinearFunction(self, {i: 1})
|
|
658
|
+
|
|
659
|
+
def _repr_(self):
|
|
660
|
+
"""
|
|
661
|
+
Return as string representation.
|
|
662
|
+
|
|
663
|
+
EXAMPLES::
|
|
664
|
+
|
|
665
|
+
sage: MixedIntegerLinearProgram().linear_functions_parent()
|
|
666
|
+
Linear functions over Real Double Field
|
|
667
|
+
"""
|
|
668
|
+
return 'Linear functions over ' + str(self.base_ring())
|
|
669
|
+
|
|
670
|
+
cpdef _element_constructor_(self, x):
|
|
671
|
+
"""
|
|
672
|
+
Construct a :class:`LinearFunction` from ``x``.
|
|
673
|
+
|
|
674
|
+
EXAMPLES::
|
|
675
|
+
|
|
676
|
+
sage: p = MixedIntegerLinearProgram()
|
|
677
|
+
sage: LF = p.linear_functions_parent()
|
|
678
|
+
sage: LF._element_constructor_(123)
|
|
679
|
+
123
|
|
680
|
+
sage: LF(123) # indirect doctest
|
|
681
|
+
123
|
|
682
|
+
sage: type(_)
|
|
683
|
+
<class 'sage.numerical.linear_functions.LinearFunction'>
|
|
684
|
+
|
|
685
|
+
sage: p_QQ = MixedIntegerLinearProgram(solver='ppl')
|
|
686
|
+
sage: LF_QQ = p_QQ.linear_functions_parent()
|
|
687
|
+
sage: f = LF({-1:1/2, 2:3/4}); f
|
|
688
|
+
0.5 + 0.75*x_2
|
|
689
|
+
sage: LF(f) is f
|
|
690
|
+
True
|
|
691
|
+
sage: LF_QQ(f)
|
|
692
|
+
1/2 + 3/4*x_2
|
|
693
|
+
sage: LF_QQ(f) is f
|
|
694
|
+
False
|
|
695
|
+
"""
|
|
696
|
+
if isinstance(x, LinearFunction):
|
|
697
|
+
return LinearFunction(self, (<LinearFunction>x)._f)
|
|
698
|
+
return LinearFunction(self, x)
|
|
699
|
+
|
|
700
|
+
cpdef _coerce_map_from_(self, R):
|
|
701
|
+
"""
|
|
702
|
+
Allow coercion of scalars into linear functions.
|
|
703
|
+
|
|
704
|
+
INPUT:
|
|
705
|
+
|
|
706
|
+
- ``R`` -- a ring
|
|
707
|
+
|
|
708
|
+
OUTPUT: boolean; whether there is a coercion map
|
|
709
|
+
|
|
710
|
+
EXAMPLES::
|
|
711
|
+
|
|
712
|
+
sage: p = MixedIntegerLinearProgram()
|
|
713
|
+
sage: parent = p.linear_functions_parent()
|
|
714
|
+
sage: parent.coerce(int(2))
|
|
715
|
+
2
|
|
716
|
+
sage: parent._coerce_map_from_(int)
|
|
717
|
+
True
|
|
718
|
+
"""
|
|
719
|
+
from sage.structure.coerce import parent_is_real_numerical
|
|
720
|
+
if parent_is_real_numerical(R) or R is complex:
|
|
721
|
+
return True
|
|
722
|
+
return False
|
|
723
|
+
|
|
724
|
+
def _an_element_(self):
|
|
725
|
+
"""
|
|
726
|
+
Return an element.
|
|
727
|
+
|
|
728
|
+
OUTPUT: a linear function
|
|
729
|
+
|
|
730
|
+
EXAMPLES::
|
|
731
|
+
|
|
732
|
+
sage: p = MixedIntegerLinearProgram().linear_functions_parent()
|
|
733
|
+
sage: p._an_element_()
|
|
734
|
+
5*x_2 + 7*x_5
|
|
735
|
+
sage: p.an_element() # indirect doctest
|
|
736
|
+
5*x_2 + 7*x_5
|
|
737
|
+
"""
|
|
738
|
+
return self._element_constructor_({2:5, 5:7})
|
|
739
|
+
|
|
740
|
+
|
|
741
|
+
#*****************************************************************************
|
|
742
|
+
#
|
|
743
|
+
# Elements of linear functions
|
|
744
|
+
#
|
|
745
|
+
#*****************************************************************************
|
|
746
|
+
|
|
747
|
+
cdef class LinearFunction(LinearFunctionOrConstraint):
|
|
748
|
+
r"""
|
|
749
|
+
An elementary algebra to represent symbolic linear functions.
|
|
750
|
+
|
|
751
|
+
.. warning::
|
|
752
|
+
|
|
753
|
+
You should never instantiate :class:`LinearFunction`
|
|
754
|
+
manually. Use the element constructor in the parent
|
|
755
|
+
instead.
|
|
756
|
+
|
|
757
|
+
EXAMPLES:
|
|
758
|
+
|
|
759
|
+
For example, do this::
|
|
760
|
+
|
|
761
|
+
sage: p = MixedIntegerLinearProgram()
|
|
762
|
+
sage: parent = p.linear_functions_parent()
|
|
763
|
+
sage: parent({0 : 1, 3 : -8})
|
|
764
|
+
x_0 - 8*x_3
|
|
765
|
+
|
|
766
|
+
instead of this::
|
|
767
|
+
|
|
768
|
+
sage: from sage.numerical.linear_functions import LinearFunction
|
|
769
|
+
sage: LinearFunction(p.linear_functions_parent(), {0 : 1, 3 : -8})
|
|
770
|
+
x_0 - 8*x_3
|
|
771
|
+
"""
|
|
772
|
+
|
|
773
|
+
def __init__(self, parent, f):
|
|
774
|
+
r"""
|
|
775
|
+
Constructor taking a dictionary or a numerical value as its argument.
|
|
776
|
+
|
|
777
|
+
A linear function is represented as a dictionary. The
|
|
778
|
+
values are the coefficient of the variable represented
|
|
779
|
+
by the keys ( which are integers ). The key ``-1``
|
|
780
|
+
corresponds to the constant term.
|
|
781
|
+
|
|
782
|
+
EXAMPLES:
|
|
783
|
+
|
|
784
|
+
With a dictionary::
|
|
785
|
+
|
|
786
|
+
sage: p = MixedIntegerLinearProgram()
|
|
787
|
+
sage: LF = p.linear_functions_parent()
|
|
788
|
+
sage: LF({0 : 1, 3 : -8})
|
|
789
|
+
x_0 - 8*x_3
|
|
790
|
+
|
|
791
|
+
Using the constructor with a numerical value::
|
|
792
|
+
|
|
793
|
+
sage: p = MixedIntegerLinearProgram()
|
|
794
|
+
sage: LF = p.linear_functions_parent()
|
|
795
|
+
sage: LF(25)
|
|
796
|
+
25
|
|
797
|
+
"""
|
|
798
|
+
ModuleElement.__init__(self, parent)
|
|
799
|
+
R = self.base_ring()
|
|
800
|
+
if isinstance(f, dict):
|
|
801
|
+
self._f = dict( (int(key), R(value)) for key, value in f.iteritems() )
|
|
802
|
+
else:
|
|
803
|
+
self._f = {-1: R(f)}
|
|
804
|
+
|
|
805
|
+
cpdef iteritems(self):
|
|
806
|
+
"""
|
|
807
|
+
Iterate over the index, coefficient pairs.
|
|
808
|
+
|
|
809
|
+
OUTPUT:
|
|
810
|
+
|
|
811
|
+
An iterator over the ``(key, coefficient)`` pairs. The keys
|
|
812
|
+
are integers indexing the variables. The key ``-1``
|
|
813
|
+
corresponds to the constant term.
|
|
814
|
+
|
|
815
|
+
EXAMPLES::
|
|
816
|
+
|
|
817
|
+
sage: p = MixedIntegerLinearProgram(solver = 'ppl')
|
|
818
|
+
sage: x = p.new_variable()
|
|
819
|
+
sage: f = 0.5 + 3/2*x[1] + 0.6*x[3]
|
|
820
|
+
sage: for id, coeff in sorted(f.iteritems()):
|
|
821
|
+
....: print('id = {} coeff = {}'.format(id, coeff))
|
|
822
|
+
id = -1 coeff = 1/2
|
|
823
|
+
id = 0 coeff = 3/2
|
|
824
|
+
id = 1 coeff = 3/5
|
|
825
|
+
"""
|
|
826
|
+
return self._f.iteritems()
|
|
827
|
+
|
|
828
|
+
def dict(self):
|
|
829
|
+
r"""
|
|
830
|
+
Return the dictionary corresponding to the Linear Function.
|
|
831
|
+
|
|
832
|
+
OUTPUT:
|
|
833
|
+
|
|
834
|
+
The linear function is represented as a dictionary. The value
|
|
835
|
+
are the coefficient of the variable represented by the keys (
|
|
836
|
+
which are integers ). The key ``-1`` corresponds to the
|
|
837
|
+
constant term.
|
|
838
|
+
|
|
839
|
+
EXAMPLES::
|
|
840
|
+
|
|
841
|
+
sage: p = MixedIntegerLinearProgram()
|
|
842
|
+
sage: LF = p.linear_functions_parent()
|
|
843
|
+
sage: lf = LF({0 : 1, 3 : -8})
|
|
844
|
+
sage: lf.dict()
|
|
845
|
+
{0: 1.0, 3: -8.0}
|
|
846
|
+
"""
|
|
847
|
+
return dict(self._f)
|
|
848
|
+
|
|
849
|
+
def coefficient(self, x):
|
|
850
|
+
r"""
|
|
851
|
+
Return one of the coefficients.
|
|
852
|
+
|
|
853
|
+
INPUT:
|
|
854
|
+
|
|
855
|
+
- ``x`` -- a linear variable or an integer; if an integer `i`
|
|
856
|
+
is passed, then `x_i` is used as linear variable
|
|
857
|
+
|
|
858
|
+
OUTPUT:
|
|
859
|
+
|
|
860
|
+
A base ring element. The coefficient of ``x`` in the linear
|
|
861
|
+
function. Pass ``-1`` for the constant term.
|
|
862
|
+
|
|
863
|
+
EXAMPLES::
|
|
864
|
+
|
|
865
|
+
sage: mip.<b> = MixedIntegerLinearProgram()
|
|
866
|
+
sage: lf = -8 * b[3] + b[0] - 5; lf
|
|
867
|
+
-5 - 8*x_0 + x_1
|
|
868
|
+
sage: lf.coefficient(b[3])
|
|
869
|
+
-8.0
|
|
870
|
+
sage: lf.coefficient(0) # x_0 is b[3]
|
|
871
|
+
-8.0
|
|
872
|
+
sage: lf.coefficient(4)
|
|
873
|
+
0.0
|
|
874
|
+
sage: lf.coefficient(-1)
|
|
875
|
+
-5.0
|
|
876
|
+
|
|
877
|
+
TESTS::
|
|
878
|
+
|
|
879
|
+
sage: lf.coefficient(b[3] + b[4])
|
|
880
|
+
Traceback (most recent call last):
|
|
881
|
+
...
|
|
882
|
+
ValueError: x is a sum, must be a single variable
|
|
883
|
+
sage: lf.coefficient(2*b[3])
|
|
884
|
+
Traceback (most recent call last):
|
|
885
|
+
...
|
|
886
|
+
ValueError: x must have a unit coefficient
|
|
887
|
+
sage: mip.<q> = MixedIntegerLinearProgram(solver='ppl')
|
|
888
|
+
sage: lf.coefficient(q[0])
|
|
889
|
+
Traceback (most recent call last):
|
|
890
|
+
...
|
|
891
|
+
ValueError: x is from a different linear functions module
|
|
892
|
+
"""
|
|
893
|
+
if isinstance(x, LinearFunction):
|
|
894
|
+
if self.parent() != x.parent():
|
|
895
|
+
raise ValueError('x is from a different linear functions module')
|
|
896
|
+
if len((<LinearFunction>x)._f) != 1:
|
|
897
|
+
raise ValueError('x is a sum, must be a single variable')
|
|
898
|
+
i, = (<LinearFunction>x)._f.keys()
|
|
899
|
+
if (<LinearFunction>x)._f[i] != 1:
|
|
900
|
+
raise ValueError('x must have a unit coefficient')
|
|
901
|
+
else:
|
|
902
|
+
i = int(x)
|
|
903
|
+
try:
|
|
904
|
+
return self._f[i]
|
|
905
|
+
except KeyError:
|
|
906
|
+
return self.parent().base_ring().zero()
|
|
907
|
+
|
|
908
|
+
cpdef _add_(self, b):
|
|
909
|
+
r"""
|
|
910
|
+
Defining the ``+`` operator.
|
|
911
|
+
|
|
912
|
+
EXAMPLES::
|
|
913
|
+
|
|
914
|
+
sage: p = MixedIntegerLinearProgram()
|
|
915
|
+
sage: LF = p.linear_functions_parent()
|
|
916
|
+
sage: LF({0 : 1, 3 : -8}) + LF({2 : 5, 3 : 2}) - 16
|
|
917
|
+
-16 + x_0 + 5*x_2 - 6*x_3
|
|
918
|
+
"""
|
|
919
|
+
e = dict(self._f)
|
|
920
|
+
for (id,coeff) in b.dict().iteritems():
|
|
921
|
+
e[id] = self._f.get(id,0) + coeff
|
|
922
|
+
P = self.parent()
|
|
923
|
+
return P(e)
|
|
924
|
+
|
|
925
|
+
cpdef _neg_(self):
|
|
926
|
+
r"""
|
|
927
|
+
Defining the - operator (opposite).
|
|
928
|
+
|
|
929
|
+
EXAMPLES::
|
|
930
|
+
|
|
931
|
+
sage: p = MixedIntegerLinearProgram()
|
|
932
|
+
sage: LF = p.linear_functions_parent()
|
|
933
|
+
sage: - LF({0 : 1, 3 : -8})
|
|
934
|
+
-1*x_0 + 8*x_3
|
|
935
|
+
"""
|
|
936
|
+
P = self.parent()
|
|
937
|
+
return P({id: -coeff for id, coeff in self._f.iteritems()})
|
|
938
|
+
|
|
939
|
+
cpdef _sub_(self, b):
|
|
940
|
+
r"""
|
|
941
|
+
Defining the - operator (subtraction).
|
|
942
|
+
|
|
943
|
+
EXAMPLES::
|
|
944
|
+
|
|
945
|
+
sage: p = MixedIntegerLinearProgram()
|
|
946
|
+
sage: LF = p.linear_functions_parent()
|
|
947
|
+
sage: LF({2 : 5, 3 : 2}) - 3
|
|
948
|
+
-3 + 5*x_2 + 2*x_3
|
|
949
|
+
sage: LF({0 : 1, 3 : -8}) - LF({2 : 5, 3 : 2}) - 16
|
|
950
|
+
-16 + x_0 - 5*x_2 - 10*x_3
|
|
951
|
+
"""
|
|
952
|
+
e = dict(self._f)
|
|
953
|
+
for id, coeff in b.dict().iteritems():
|
|
954
|
+
e[id] = self._f.get(id, 0) - coeff
|
|
955
|
+
P = self.parent()
|
|
956
|
+
return P(e)
|
|
957
|
+
|
|
958
|
+
cpdef _lmul_(self, Element b):
|
|
959
|
+
r"""
|
|
960
|
+
Multiplication by scalars.
|
|
961
|
+
|
|
962
|
+
EXAMPLES::
|
|
963
|
+
|
|
964
|
+
sage: p = MixedIntegerLinearProgram()
|
|
965
|
+
sage: LF = p.linear_functions_parent()
|
|
966
|
+
sage: LF({2 : 5, 3 : 2}) * 3
|
|
967
|
+
15*x_2 + 6*x_3
|
|
968
|
+
sage: 3 * LF({2 : 5, 3 : 2})
|
|
969
|
+
15*x_2 + 6*x_3
|
|
970
|
+
"""
|
|
971
|
+
P = self.parent()
|
|
972
|
+
return P(dict([(id,b*coeff) for (id, coeff) in self._f.iteritems()]))
|
|
973
|
+
|
|
974
|
+
cpdef _acted_upon_(self, x, bint self_on_left):
|
|
975
|
+
"""
|
|
976
|
+
Act with scalars that do not have a natural coercion into
|
|
977
|
+
``self.base_ring()``
|
|
978
|
+
|
|
979
|
+
EXAMPLES:
|
|
980
|
+
|
|
981
|
+
Note that there is no coercion from ``RR`` to ``QQ``, but you
|
|
982
|
+
can explicitly convert them::
|
|
983
|
+
|
|
984
|
+
sage: 1/2 * 0.6
|
|
985
|
+
0.300000000000000
|
|
986
|
+
|
|
987
|
+
If there were a coercion, then 0.6 would have been coerced into
|
|
988
|
+
6/10 and the result would have been rational. This is
|
|
989
|
+
undesirable, which is why there cannot be a coercion on the
|
|
990
|
+
level of the coefficient rings.
|
|
991
|
+
|
|
992
|
+
By declaring an action of ``RR`` on the linear functions over
|
|
993
|
+
``QQ`` we make the following possible::
|
|
994
|
+
|
|
995
|
+
sage: p = MixedIntegerLinearProgram(solver='ppl')
|
|
996
|
+
sage: p.base_ring()
|
|
997
|
+
Rational Field
|
|
998
|
+
sage: x = p.new_variable()
|
|
999
|
+
sage: x[0] * 0.6
|
|
1000
|
+
3/5*x_0
|
|
1001
|
+
|
|
1002
|
+
sage: vf = (2 + x[0]) * vector(ZZ, [3,4]); vf
|
|
1003
|
+
(6, 8) + (3, 4)*x_0
|
|
1004
|
+
sage: vf.parent()
|
|
1005
|
+
Tensor product of Vector space of dimension 2 over Rational Field
|
|
1006
|
+
and Linear functions over Rational Field
|
|
1007
|
+
|
|
1008
|
+
sage: tf = x[0] * identity_matrix(2); tf
|
|
1009
|
+
[x_0 0 ]
|
|
1010
|
+
[0 x_0]
|
|
1011
|
+
sage: tf.parent()
|
|
1012
|
+
Tensor product of Full MatrixSpace of 2 by 2 dense matrices over
|
|
1013
|
+
Rational Field and Linear functions over Rational Field
|
|
1014
|
+
"""
|
|
1015
|
+
R = self.base_ring()
|
|
1016
|
+
try:
|
|
1017
|
+
x_R = R(x)
|
|
1018
|
+
except TypeError:
|
|
1019
|
+
M = x.parent().base_extend(R)
|
|
1020
|
+
x_M = M(x)
|
|
1021
|
+
from sage.numerical.linear_tensor import LinearTensorParent
|
|
1022
|
+
P = LinearTensorParent(M, self.parent())
|
|
1023
|
+
tensor = dict()
|
|
1024
|
+
for k, v in self._f.items():
|
|
1025
|
+
tensor[k] = x_M * v
|
|
1026
|
+
return P(tensor)
|
|
1027
|
+
return self._rmul_(x_R)
|
|
1028
|
+
|
|
1029
|
+
def _coeff_formatter(self, coeff, constant_term=False):
|
|
1030
|
+
"""
|
|
1031
|
+
Pretty-print multiplicative coefficient ``x``.
|
|
1032
|
+
|
|
1033
|
+
OUTPUT:
|
|
1034
|
+
|
|
1035
|
+
string, including a trailing space if the coefficient is not
|
|
1036
|
+
one. Empty string otherwise.
|
|
1037
|
+
|
|
1038
|
+
EXAMPLES::
|
|
1039
|
+
|
|
1040
|
+
sage: p = MixedIntegerLinearProgram()
|
|
1041
|
+
sage: LF = p.linear_functions_parent()
|
|
1042
|
+
sage: f = LF(1); type(f)
|
|
1043
|
+
<class 'sage.numerical.linear_functions.LinearFunction'>
|
|
1044
|
+
sage: f._coeff_formatter(1)
|
|
1045
|
+
''
|
|
1046
|
+
sage: f._coeff_formatter(1, constant_term=True)
|
|
1047
|
+
'1'
|
|
1048
|
+
sage: f._coeff_formatter(RDF(12.0))
|
|
1049
|
+
'12*'
|
|
1050
|
+
sage: f._coeff_formatter(RDF(12.3))
|
|
1051
|
+
'12.3*'
|
|
1052
|
+
|
|
1053
|
+
sage: p = MixedIntegerLinearProgram(solver='ppl')
|
|
1054
|
+
sage: LF = p.linear_functions_parent()
|
|
1055
|
+
sage: f = LF(1)
|
|
1056
|
+
sage: f._coeff_formatter(13/45)
|
|
1057
|
+
'13/45*'
|
|
1058
|
+
|
|
1059
|
+
sage: # needs sage.rings.number_field
|
|
1060
|
+
sage: from sage.rings.number_field.number_field import QuadraticField
|
|
1061
|
+
sage: K.<sqrt5> = QuadraticField(5, 'sqrt5')
|
|
1062
|
+
sage: p = MixedIntegerLinearProgram(solver='interactivelp', base_ring=K)
|
|
1063
|
+
sage: LF = p.linear_functions_parent()
|
|
1064
|
+
sage: f = LF(1)
|
|
1065
|
+
sage: f._coeff_formatter(sqrt5)
|
|
1066
|
+
'sqrt5*'
|
|
1067
|
+
|
|
1068
|
+
sage: # needs sage.rings.number_field
|
|
1069
|
+
sage: from sage.rings.qqbar import AA
|
|
1070
|
+
sage: sqrt5 = AA(5).sqrt()
|
|
1071
|
+
sage: p = MixedIntegerLinearProgram(solver='interactivelp', base_ring=AA)
|
|
1072
|
+
sage: LF = p.linear_functions_parent()
|
|
1073
|
+
sage: f = LF(1)
|
|
1074
|
+
sage: f._coeff_formatter(sqrt5)
|
|
1075
|
+
'2.236067977499790?*'
|
|
1076
|
+
"""
|
|
1077
|
+
R = self.base_ring()
|
|
1078
|
+
if coeff == R.one() and not constant_term:
|
|
1079
|
+
return ''
|
|
1080
|
+
try:
|
|
1081
|
+
from sage.rings.integer_ring import ZZ
|
|
1082
|
+
coeff = ZZ(coeff)
|
|
1083
|
+
except (TypeError, ValueError):
|
|
1084
|
+
pass
|
|
1085
|
+
if constant_term:
|
|
1086
|
+
return str(coeff)
|
|
1087
|
+
else:
|
|
1088
|
+
return str(coeff) + self.parent()._multiplication_symbol
|
|
1089
|
+
|
|
1090
|
+
def _repr_(self):
|
|
1091
|
+
r"""
|
|
1092
|
+
Return a string version of the linear function.
|
|
1093
|
+
|
|
1094
|
+
EXAMPLES::
|
|
1095
|
+
|
|
1096
|
+
sage: p = MixedIntegerLinearProgram(solver='GLPK')
|
|
1097
|
+
sage: LF = p.linear_functions_parent()
|
|
1098
|
+
sage: LF({-1: -15, 2 : -5.1, 3 : 2/3})
|
|
1099
|
+
-15 - 5.1*x_2 + 0.6666666666666666*x_3
|
|
1100
|
+
sage: p = MixedIntegerLinearProgram(solver='ppl')
|
|
1101
|
+
sage: LF = p.linear_functions_parent()
|
|
1102
|
+
sage: LF({-1: -15, 2 : -5.1, 3 : 2/3})
|
|
1103
|
+
-15 - 51/10*x_2 + 2/3*x_3
|
|
1104
|
+
"""
|
|
1105
|
+
cdef dict d = dict(self._f)
|
|
1106
|
+
cdef bint first = True
|
|
1107
|
+
t = ""
|
|
1108
|
+
|
|
1109
|
+
if -1 in d:
|
|
1110
|
+
coeff = d.pop(-1)
|
|
1111
|
+
if coeff:
|
|
1112
|
+
t = self._coeff_formatter(coeff, constant_term=True)
|
|
1113
|
+
first = False
|
|
1114
|
+
|
|
1115
|
+
cdef list l = sorted(d.items())
|
|
1116
|
+
for id, coeff in l:
|
|
1117
|
+
sign = coeff.sign()
|
|
1118
|
+
if sign == 0:
|
|
1119
|
+
continue
|
|
1120
|
+
if not first:
|
|
1121
|
+
if sign == -1:
|
|
1122
|
+
t += ' - '
|
|
1123
|
+
if sign == +1:
|
|
1124
|
+
t += ' + '
|
|
1125
|
+
t += self._coeff_formatter(abs(coeff)) + 'x_' + str(id)
|
|
1126
|
+
else:
|
|
1127
|
+
t += self._coeff_formatter(coeff) + 'x_' + str(id)
|
|
1128
|
+
first = False
|
|
1129
|
+
|
|
1130
|
+
if first:
|
|
1131
|
+
return '0'
|
|
1132
|
+
else:
|
|
1133
|
+
return t
|
|
1134
|
+
|
|
1135
|
+
cpdef is_zero(self):
|
|
1136
|
+
"""
|
|
1137
|
+
Test whether ``self`` is zero.
|
|
1138
|
+
|
|
1139
|
+
OUTPUT: boolean
|
|
1140
|
+
|
|
1141
|
+
EXAMPLES::
|
|
1142
|
+
|
|
1143
|
+
sage: p = MixedIntegerLinearProgram()
|
|
1144
|
+
sage: x = p.new_variable()
|
|
1145
|
+
sage: (x[1] - x[1] + 0*x[2]).is_zero()
|
|
1146
|
+
True
|
|
1147
|
+
"""
|
|
1148
|
+
for coeff in self._f.values():
|
|
1149
|
+
if not coeff.is_zero():
|
|
1150
|
+
return False
|
|
1151
|
+
return True
|
|
1152
|
+
|
|
1153
|
+
cpdef equals(LinearFunction left, LinearFunction right):
|
|
1154
|
+
"""
|
|
1155
|
+
Logically compare ``left`` and ``right``.
|
|
1156
|
+
|
|
1157
|
+
OUTPUT: boolean
|
|
1158
|
+
|
|
1159
|
+
EXAMPLES::
|
|
1160
|
+
|
|
1161
|
+
sage: p = MixedIntegerLinearProgram()
|
|
1162
|
+
sage: x = p.new_variable()
|
|
1163
|
+
sage: (x[1] + 1).equals(3/3 + 1*x[1] + 0*x[2])
|
|
1164
|
+
True
|
|
1165
|
+
"""
|
|
1166
|
+
return (left-right).is_zero()
|
|
1167
|
+
|
|
1168
|
+
|
|
1169
|
+
#*****************************************************************************
|
|
1170
|
+
#
|
|
1171
|
+
# Parent of linear constraints
|
|
1172
|
+
#
|
|
1173
|
+
#*****************************************************************************
|
|
1174
|
+
|
|
1175
|
+
cdef class LinearConstraintsParent_class(Parent):
|
|
1176
|
+
"""
|
|
1177
|
+
Parent for :class:`LinearConstraint`.
|
|
1178
|
+
|
|
1179
|
+
.. warning::
|
|
1180
|
+
|
|
1181
|
+
This class has no reason to be instantiated by the user, and
|
|
1182
|
+
is meant to be used by instances of
|
|
1183
|
+
:class:`MixedIntegerLinearProgram`. Also, use the
|
|
1184
|
+
:func:`LinearConstraintsParent` factory function.
|
|
1185
|
+
|
|
1186
|
+
INPUT/OUTPUT: see :func:`LinearFunctionsParent`
|
|
1187
|
+
|
|
1188
|
+
EXAMPLES::
|
|
1189
|
+
|
|
1190
|
+
sage: p = MixedIntegerLinearProgram()
|
|
1191
|
+
sage: LC = p.linear_constraints_parent(); LC
|
|
1192
|
+
Linear constraints over Real Double Field
|
|
1193
|
+
sage: from sage.numerical.linear_functions import LinearConstraintsParent
|
|
1194
|
+
sage: LinearConstraintsParent(p.linear_functions_parent()) is LC
|
|
1195
|
+
True
|
|
1196
|
+
"""
|
|
1197
|
+
def __cinit__(self, linear_functions_parent):
|
|
1198
|
+
"""
|
|
1199
|
+
Cython initializer.
|
|
1200
|
+
|
|
1201
|
+
TESTS::
|
|
1202
|
+
|
|
1203
|
+
sage: from sage.numerical.linear_functions import LinearConstraintsParent_class
|
|
1204
|
+
sage: from sage.numerical.linear_functions import LinearFunctionsParent
|
|
1205
|
+
sage: LF = LinearFunctionsParent(RDF)
|
|
1206
|
+
sage: LinearConstraintsParent_class.__new__(LinearConstraintsParent_class, LF)
|
|
1207
|
+
Linear constraints over Real Double Field
|
|
1208
|
+
sage: LinearConstraintsParent_class.__new__(LinearConstraintsParent_class, None)
|
|
1209
|
+
Traceback (most recent call last):
|
|
1210
|
+
...
|
|
1211
|
+
TypeError: Cannot convert NoneType to sage.numerical.linear_functions.LinearFunctionsParent_class
|
|
1212
|
+
"""
|
|
1213
|
+
self._LF = <LinearFunctionsParent_class?>linear_functions_parent
|
|
1214
|
+
# Do not use coercion framework for __richcmp__
|
|
1215
|
+
self.flags |= Parent_richcmp_element_without_coercion
|
|
1216
|
+
|
|
1217
|
+
def __init__(self, linear_functions_parent):
|
|
1218
|
+
"""
|
|
1219
|
+
The Python constructor.
|
|
1220
|
+
|
|
1221
|
+
INPUT/OUTPUT: see :func:`LinearFunctionsParent`
|
|
1222
|
+
|
|
1223
|
+
TESTS::
|
|
1224
|
+
|
|
1225
|
+
sage: from sage.numerical.linear_functions import LinearFunctionsParent
|
|
1226
|
+
sage: LF = LinearFunctionsParent(RDF)
|
|
1227
|
+
sage: from sage.numerical.linear_functions import LinearConstraintsParent
|
|
1228
|
+
sage: LinearConstraintsParent(LF)
|
|
1229
|
+
Linear constraints over Real Double Field
|
|
1230
|
+
sage: LinearConstraintsParent(None)
|
|
1231
|
+
Traceback (most recent call last):
|
|
1232
|
+
...
|
|
1233
|
+
TypeError: Cannot convert NoneType to sage.numerical.linear_functions.LinearFunctionsParent_class
|
|
1234
|
+
"""
|
|
1235
|
+
Parent.__init__(self)
|
|
1236
|
+
|
|
1237
|
+
def linear_functions_parent(self):
|
|
1238
|
+
"""
|
|
1239
|
+
Return the parent for the linear functions.
|
|
1240
|
+
|
|
1241
|
+
EXAMPLES::
|
|
1242
|
+
|
|
1243
|
+
sage: LC = MixedIntegerLinearProgram().linear_constraints_parent()
|
|
1244
|
+
sage: LC.linear_functions_parent()
|
|
1245
|
+
Linear functions over Real Double Field
|
|
1246
|
+
"""
|
|
1247
|
+
return self._LF
|
|
1248
|
+
|
|
1249
|
+
def _repr_(self):
|
|
1250
|
+
"""
|
|
1251
|
+
Return a string representation.
|
|
1252
|
+
|
|
1253
|
+
OUTPUT: string
|
|
1254
|
+
|
|
1255
|
+
EXAMPLES::
|
|
1256
|
+
|
|
1257
|
+
sage: MixedIntegerLinearProgram().linear_constraints_parent()
|
|
1258
|
+
Linear constraints over Real Double Field
|
|
1259
|
+
"""
|
|
1260
|
+
return 'Linear constraints over ' + str(self.linear_functions_parent().base_ring())
|
|
1261
|
+
|
|
1262
|
+
cpdef _element_constructor_(self, left, right=None, equality=False):
|
|
1263
|
+
"""
|
|
1264
|
+
Construct a :class:`LinearConstraint`.
|
|
1265
|
+
|
|
1266
|
+
INPUT:
|
|
1267
|
+
|
|
1268
|
+
- ``left`` -- a :class:`LinearFunction`, or something that can
|
|
1269
|
+
be converted into one, a list/tuple of
|
|
1270
|
+
:class:`LinearFunction`, or an existing
|
|
1271
|
+
:class:`LinearConstraint`.
|
|
1272
|
+
|
|
1273
|
+
- ``right`` -- a :class:`LinearFunction` or ``None``
|
|
1274
|
+
(default)
|
|
1275
|
+
|
|
1276
|
+
- ``equality`` -- boolean (default: ``True``); whether to
|
|
1277
|
+
construct an equation or an inequality
|
|
1278
|
+
|
|
1279
|
+
OUTPUT: the :class:`LinearConstraint` constructed from the input data
|
|
1280
|
+
|
|
1281
|
+
EXAMPLES::
|
|
1282
|
+
|
|
1283
|
+
sage: p = MixedIntegerLinearProgram()
|
|
1284
|
+
sage: LC = p.linear_constraints_parent()
|
|
1285
|
+
sage: LC._element_constructor_(1, 2)
|
|
1286
|
+
1 <= 2
|
|
1287
|
+
|
|
1288
|
+
sage: x = p.new_variable()
|
|
1289
|
+
sage: LC([x[0], x[1], x[2]])
|
|
1290
|
+
x_0 <= x_1 <= x_2
|
|
1291
|
+
|
|
1292
|
+
sage: LC([x[0], x[1], x[2]], equality=True)
|
|
1293
|
+
x_0 == x_1 == x_2
|
|
1294
|
+
|
|
1295
|
+
sage: type(_)
|
|
1296
|
+
<class 'sage.numerical.linear_functions.LinearConstraint'>
|
|
1297
|
+
|
|
1298
|
+
TESTS::
|
|
1299
|
+
|
|
1300
|
+
sage: inequality = LC([x[0], 1/2*x[1], 3/4*x[2]]); inequality
|
|
1301
|
+
x_0 <= 0.5*x_1 <= 0.75*x_2
|
|
1302
|
+
sage: LC(inequality) is inequality
|
|
1303
|
+
True
|
|
1304
|
+
sage: p_QQ = MixedIntegerLinearProgram(solver='ppl')
|
|
1305
|
+
sage: LC_QQ = p_QQ.linear_constraints_parent()
|
|
1306
|
+
sage: LC_QQ(inequality)
|
|
1307
|
+
x_0 <= 1/2*x_1 <= 3/4*x_2
|
|
1308
|
+
sage: LC_QQ(inequality) is inequality
|
|
1309
|
+
False
|
|
1310
|
+
"""
|
|
1311
|
+
if right is None and isinstance(left, LinearConstraint):
|
|
1312
|
+
if (left.parent() is self) and (left.is_equation() == equality):
|
|
1313
|
+
return left
|
|
1314
|
+
else:
|
|
1315
|
+
return LinearConstraint(self, (<LinearConstraint>left).constraints,
|
|
1316
|
+
equality=equality)
|
|
1317
|
+
if right is None:
|
|
1318
|
+
if isinstance(left, (list,tuple)):
|
|
1319
|
+
return LinearConstraint(self, left, equality=equality)
|
|
1320
|
+
else:
|
|
1321
|
+
return LinearConstraint(self, [left], equality=equality)
|
|
1322
|
+
else:
|
|
1323
|
+
return LinearConstraint(self, [left, right], equality=equality)
|
|
1324
|
+
|
|
1325
|
+
cpdef _coerce_map_from_(self, R):
|
|
1326
|
+
"""
|
|
1327
|
+
Allow coercion of scalars into linear functions.
|
|
1328
|
+
|
|
1329
|
+
EXAMPLES::
|
|
1330
|
+
|
|
1331
|
+
sage: p = MixedIntegerLinearProgram()
|
|
1332
|
+
sage: parent = p.linear_constraints_parent()
|
|
1333
|
+
sage: parent.coerce(int(2))
|
|
1334
|
+
trivial constraint starting with 2
|
|
1335
|
+
sage: parent._coerce_map_from_(int)
|
|
1336
|
+
True
|
|
1337
|
+
"""
|
|
1338
|
+
return self.linear_functions_parent().has_coerce_map_from(R)
|
|
1339
|
+
|
|
1340
|
+
def _an_element_(self):
|
|
1341
|
+
"""
|
|
1342
|
+
Return an element.
|
|
1343
|
+
|
|
1344
|
+
EXAMPLES::
|
|
1345
|
+
|
|
1346
|
+
sage: p = MixedIntegerLinearProgram().linear_functions_parent()
|
|
1347
|
+
sage: p._an_element_()
|
|
1348
|
+
5*x_2 + 7*x_5
|
|
1349
|
+
sage: p.an_element() # indirect doctest
|
|
1350
|
+
5*x_2 + 7*x_5
|
|
1351
|
+
"""
|
|
1352
|
+
LF = self.linear_functions_parent()
|
|
1353
|
+
return self(0) <= LF.an_element()
|
|
1354
|
+
|
|
1355
|
+
|
|
1356
|
+
#*****************************************************************************
|
|
1357
|
+
#
|
|
1358
|
+
# Elements of linear constraints
|
|
1359
|
+
#
|
|
1360
|
+
#*****************************************************************************
|
|
1361
|
+
|
|
1362
|
+
cdef class LinearConstraint(LinearFunctionOrConstraint):
|
|
1363
|
+
"""
|
|
1364
|
+
A class to represent formal Linear Constraints.
|
|
1365
|
+
|
|
1366
|
+
A Linear Constraint being an inequality between
|
|
1367
|
+
two linear functions, this class lets the user
|
|
1368
|
+
write ``LinearFunction1 <= LinearFunction2``
|
|
1369
|
+
to define the corresponding constraint, which
|
|
1370
|
+
can potentially involve several layers of such
|
|
1371
|
+
inequalities (``A <= B <= C``), or even equalities
|
|
1372
|
+
like ``A == B == C``.
|
|
1373
|
+
|
|
1374
|
+
Trivial constraints (meaning that they have only one term and no
|
|
1375
|
+
relation) are also allowed. They are required for the coercion
|
|
1376
|
+
system to work.
|
|
1377
|
+
|
|
1378
|
+
.. warning::
|
|
1379
|
+
|
|
1380
|
+
This class has no reason to be instantiated by the user, and
|
|
1381
|
+
is meant to be used by instances of
|
|
1382
|
+
:class:`MixedIntegerLinearProgram`.
|
|
1383
|
+
|
|
1384
|
+
INPUT:
|
|
1385
|
+
|
|
1386
|
+
- ``parent`` -- the parent; a :class:`LinearConstraintsParent_class`
|
|
1387
|
+
|
|
1388
|
+
- ``terms`` -- list/tuple/iterable of two or more linear
|
|
1389
|
+
functions (or things that can be converted into linear
|
|
1390
|
+
functions)
|
|
1391
|
+
|
|
1392
|
+
- ``equality`` -- boolean (default: ``False``); whether the terms
|
|
1393
|
+
are the entries of a chained less-or-equal (``<=``) inequality
|
|
1394
|
+
or a chained equality
|
|
1395
|
+
|
|
1396
|
+
EXAMPLES::
|
|
1397
|
+
|
|
1398
|
+
sage: p = MixedIntegerLinearProgram()
|
|
1399
|
+
sage: b = p.new_variable()
|
|
1400
|
+
sage: b[2]+2*b[3] <= b[8]-5
|
|
1401
|
+
x_0 + 2*x_1 <= -5 + x_2
|
|
1402
|
+
"""
|
|
1403
|
+
|
|
1404
|
+
def __init__(self, parent, terms, equality=False):
|
|
1405
|
+
r"""
|
|
1406
|
+
Constructor for ``LinearConstraint``.
|
|
1407
|
+
|
|
1408
|
+
INPUT:
|
|
1409
|
+
|
|
1410
|
+
See :class:`LinearConstraint`.
|
|
1411
|
+
|
|
1412
|
+
EXAMPLES::
|
|
1413
|
+
|
|
1414
|
+
sage: p = MixedIntegerLinearProgram()
|
|
1415
|
+
sage: b = p.new_variable()
|
|
1416
|
+
sage: b[2]+2*b[3] <= b[8]-5
|
|
1417
|
+
x_0 + 2*x_1 <= -5 + x_2
|
|
1418
|
+
"""
|
|
1419
|
+
assert len(terms) > 0
|
|
1420
|
+
super().__init__(parent)
|
|
1421
|
+
self.equality = equality
|
|
1422
|
+
LF = parent.linear_functions_parent()
|
|
1423
|
+
self.constraints = [ LF(term) for term in terms ]
|
|
1424
|
+
|
|
1425
|
+
cpdef equals(LinearConstraint left, LinearConstraint right):
|
|
1426
|
+
"""
|
|
1427
|
+
Compare ``left`` and ``right``.
|
|
1428
|
+
|
|
1429
|
+
OUTPUT:
|
|
1430
|
+
|
|
1431
|
+
boolean; whether all terms of ``left`` and ``right`` are
|
|
1432
|
+
equal. Note that this is stronger than mathematical
|
|
1433
|
+
equivalence of the relations.
|
|
1434
|
+
|
|
1435
|
+
EXAMPLES::
|
|
1436
|
+
|
|
1437
|
+
sage: p = MixedIntegerLinearProgram()
|
|
1438
|
+
sage: x = p.new_variable()
|
|
1439
|
+
sage: (x[1] + 1 >= 2).equals(3/3 + 1*x[1] + 0*x[2] >= 8/4)
|
|
1440
|
+
True
|
|
1441
|
+
sage: (x[1] + 1 >= 2).equals(x[1] + 1-1 >= 1-1)
|
|
1442
|
+
False
|
|
1443
|
+
"""
|
|
1444
|
+
if len(left.constraints) != len(right.constraints):
|
|
1445
|
+
return False
|
|
1446
|
+
if left.equality != right.equality:
|
|
1447
|
+
return False
|
|
1448
|
+
cdef LinearFunction l, r
|
|
1449
|
+
for i in range(len(left.constraints)):
|
|
1450
|
+
l = <LinearFunction>(left.constraints[i])
|
|
1451
|
+
r = <LinearFunction>(right.constraints[i])
|
|
1452
|
+
if not l.equals(r):
|
|
1453
|
+
return False
|
|
1454
|
+
return True
|
|
1455
|
+
|
|
1456
|
+
def is_equation(self):
|
|
1457
|
+
"""
|
|
1458
|
+
Whether the constraint is a chained equation.
|
|
1459
|
+
|
|
1460
|
+
OUTPUT: boolean
|
|
1461
|
+
|
|
1462
|
+
EXAMPLES::
|
|
1463
|
+
|
|
1464
|
+
sage: p = MixedIntegerLinearProgram()
|
|
1465
|
+
sage: b = p.new_variable()
|
|
1466
|
+
sage: (b[0] == b[1]).is_equation()
|
|
1467
|
+
True
|
|
1468
|
+
sage: (b[0] <= b[1]).is_equation()
|
|
1469
|
+
False
|
|
1470
|
+
"""
|
|
1471
|
+
return self.equality
|
|
1472
|
+
|
|
1473
|
+
def is_less_or_equal(self):
|
|
1474
|
+
"""
|
|
1475
|
+
Whether the constraint is a chained less-or_equal inequality.
|
|
1476
|
+
|
|
1477
|
+
OUTPUT: boolean
|
|
1478
|
+
|
|
1479
|
+
EXAMPLES::
|
|
1480
|
+
|
|
1481
|
+
sage: p = MixedIntegerLinearProgram()
|
|
1482
|
+
sage: b = p.new_variable()
|
|
1483
|
+
sage: (b[0] == b[1]).is_less_or_equal()
|
|
1484
|
+
False
|
|
1485
|
+
sage: (b[0] <= b[1]).is_less_or_equal()
|
|
1486
|
+
True
|
|
1487
|
+
"""
|
|
1488
|
+
return not self.equality
|
|
1489
|
+
|
|
1490
|
+
def is_trivial(self):
|
|
1491
|
+
"""
|
|
1492
|
+
Test whether the constraint is trivial.
|
|
1493
|
+
|
|
1494
|
+
EXAMPLES::
|
|
1495
|
+
|
|
1496
|
+
sage: p = MixedIntegerLinearProgram()
|
|
1497
|
+
sage: LC = p.linear_constraints_parent()
|
|
1498
|
+
sage: ieq = LC(1,2); ieq
|
|
1499
|
+
1 <= 2
|
|
1500
|
+
sage: ieq.is_trivial()
|
|
1501
|
+
False
|
|
1502
|
+
|
|
1503
|
+
sage: ieq = LC(1); ieq
|
|
1504
|
+
trivial constraint starting with 1
|
|
1505
|
+
sage: ieq.is_trivial()
|
|
1506
|
+
True
|
|
1507
|
+
"""
|
|
1508
|
+
return len(self.constraints) < 2
|
|
1509
|
+
|
|
1510
|
+
def __iter__(self):
|
|
1511
|
+
"""
|
|
1512
|
+
Iterate over the terms of the chained (in)-equality.
|
|
1513
|
+
|
|
1514
|
+
OUTPUT:
|
|
1515
|
+
|
|
1516
|
+
A generator yielding the individual terms of the constraint in
|
|
1517
|
+
left-to-right order.
|
|
1518
|
+
|
|
1519
|
+
EXAMPLES::
|
|
1520
|
+
|
|
1521
|
+
sage: p = MixedIntegerLinearProgram()
|
|
1522
|
+
sage: b = p.new_variable()
|
|
1523
|
+
sage: ieq = 1 <= b[0] <= b[2] <= 3 <= b[3]; ieq
|
|
1524
|
+
1 <= x_0 <= x_1 <= 3 <= x_2
|
|
1525
|
+
sage: list(ieq)
|
|
1526
|
+
[1, x_0, x_1, 3, x_2]
|
|
1527
|
+
sage: for term in ieq:
|
|
1528
|
+
....: print(term)
|
|
1529
|
+
1
|
|
1530
|
+
x_0
|
|
1531
|
+
x_1
|
|
1532
|
+
3
|
|
1533
|
+
x_2
|
|
1534
|
+
"""
|
|
1535
|
+
for term in self.constraints:
|
|
1536
|
+
yield term
|
|
1537
|
+
|
|
1538
|
+
def equations(self):
|
|
1539
|
+
"""
|
|
1540
|
+
Iterate over the unchained(!) equations.
|
|
1541
|
+
|
|
1542
|
+
OUTPUT:
|
|
1543
|
+
|
|
1544
|
+
An iterator over pairs ``(lhs, rhs)`` such that the individual
|
|
1545
|
+
equations are ``lhs == rhs``.
|
|
1546
|
+
|
|
1547
|
+
EXAMPLES::
|
|
1548
|
+
|
|
1549
|
+
sage: p = MixedIntegerLinearProgram()
|
|
1550
|
+
sage: b = p.new_variable()
|
|
1551
|
+
sage: eqns = 1 == b[0] == b[2] == 3 == b[3]; eqns
|
|
1552
|
+
1 == x_0 == x_1 == 3 == x_2
|
|
1553
|
+
sage: for lhs, rhs in eqns.equations():
|
|
1554
|
+
....: print(str(lhs) + ' == ' + str(rhs))
|
|
1555
|
+
1 == x_0
|
|
1556
|
+
x_0 == x_1
|
|
1557
|
+
x_1 == 3
|
|
1558
|
+
3 == x_2
|
|
1559
|
+
"""
|
|
1560
|
+
if not self.is_equation() or self.is_trivial():
|
|
1561
|
+
return
|
|
1562
|
+
term_iter = iter(self)
|
|
1563
|
+
lhs = next(term_iter)
|
|
1564
|
+
rhs = next(term_iter)
|
|
1565
|
+
while True:
|
|
1566
|
+
yield (lhs, rhs)
|
|
1567
|
+
lhs = rhs
|
|
1568
|
+
try:
|
|
1569
|
+
rhs = next(term_iter)
|
|
1570
|
+
except StopIteration:
|
|
1571
|
+
return
|
|
1572
|
+
|
|
1573
|
+
def inequalities(self):
|
|
1574
|
+
"""
|
|
1575
|
+
Iterate over the unchained(!) inequalities.
|
|
1576
|
+
|
|
1577
|
+
OUTPUT:
|
|
1578
|
+
|
|
1579
|
+
An iterator over pairs ``(lhs, rhs)`` such that the individual
|
|
1580
|
+
equations are ``lhs <= rhs``.
|
|
1581
|
+
|
|
1582
|
+
EXAMPLES::
|
|
1583
|
+
|
|
1584
|
+
sage: p = MixedIntegerLinearProgram()
|
|
1585
|
+
sage: b = p.new_variable()
|
|
1586
|
+
sage: ieq = 1 <= b[0] <= b[2] <= 3 <= b[3]; ieq
|
|
1587
|
+
1 <= x_0 <= x_1 <= 3 <= x_2
|
|
1588
|
+
|
|
1589
|
+
sage: for lhs, rhs in ieq.inequalities():
|
|
1590
|
+
....: print(str(lhs) + ' <= ' + str(rhs))
|
|
1591
|
+
1 <= x_0
|
|
1592
|
+
x_0 <= x_1
|
|
1593
|
+
x_1 <= 3
|
|
1594
|
+
3 <= x_2
|
|
1595
|
+
"""
|
|
1596
|
+
if not self.is_less_or_equal() or self.is_trivial():
|
|
1597
|
+
return
|
|
1598
|
+
term_iter = iter(self)
|
|
1599
|
+
lhs = next(term_iter)
|
|
1600
|
+
rhs = next(term_iter)
|
|
1601
|
+
while True:
|
|
1602
|
+
yield (lhs, rhs)
|
|
1603
|
+
lhs = rhs
|
|
1604
|
+
try:
|
|
1605
|
+
rhs = next(term_iter)
|
|
1606
|
+
except StopIteration:
|
|
1607
|
+
return
|
|
1608
|
+
|
|
1609
|
+
def _repr_(self):
|
|
1610
|
+
r"""
|
|
1611
|
+
Return a string representation of the constraint.
|
|
1612
|
+
|
|
1613
|
+
OUTPUT: string
|
|
1614
|
+
|
|
1615
|
+
EXAMPLES::
|
|
1616
|
+
|
|
1617
|
+
sage: p = MixedIntegerLinearProgram()
|
|
1618
|
+
sage: b = p.new_variable()
|
|
1619
|
+
sage: b[3] <= b[8] + 9
|
|
1620
|
+
x_0 <= 9 + x_1
|
|
1621
|
+
|
|
1622
|
+
sage: LC = p.linear_constraints_parent()
|
|
1623
|
+
sage: LC(b[3], b[8] + 9)
|
|
1624
|
+
x_0 <= 9 + x_1
|
|
1625
|
+
sage: LC(b[3])
|
|
1626
|
+
trivial constraint starting with x_0
|
|
1627
|
+
"""
|
|
1628
|
+
comparator = (' == ' if self.equality else ' <= ')
|
|
1629
|
+
result = comparator.join(map(str, self))
|
|
1630
|
+
if self.is_trivial():
|
|
1631
|
+
return 'trivial constraint starting with ' + result
|
|
1632
|
+
return result
|
|
1633
|
+
|
|
1634
|
+
def __bool__(self):
|
|
1635
|
+
"""
|
|
1636
|
+
Part of the hack to allow chained (in)equalities.
|
|
1637
|
+
|
|
1638
|
+
EXAMPLES::
|
|
1639
|
+
|
|
1640
|
+
sage: p = MixedIntegerLinearProgram()
|
|
1641
|
+
sage: b = p.new_variable()
|
|
1642
|
+
sage: ieq = (b[3] <= b[8] + 9)
|
|
1643
|
+
sage: ieq <= ieq <= ieq
|
|
1644
|
+
x_0 <= 9 + x_1 <= x_0 <= 9 + x_1 <= x_0 <= 9 + x_1
|
|
1645
|
+
"""
|
|
1646
|
+
global chained_comparator_replace
|
|
1647
|
+
chained_comparator_replace = self
|
|
1648
|
+
return True
|