5 from . import islhelper
7 from .islhelper
import mainctx
, libisl
8 from .geometry
import GeometricObject
, Point
9 from .linexprs
import Expression
, Rational
10 from .domains
import Domain
15 'Lt', 'Le', 'Eq', 'Ne', 'Ge', 'Gt',
20 class Polyhedron(Domain
):
30 def __new__(cls
, equalities
=None, inequalities
=None):
31 if isinstance(equalities
, str):
32 if inequalities
is not None:
33 raise TypeError('too many arguments')
34 return cls
.fromstring(equalities
)
35 elif isinstance(equalities
, GeometricObject
):
36 if inequalities
is not None:
37 raise TypeError('too many arguments')
38 return equalities
.aspolyhedron()
39 if equalities
is None:
42 for i
, equality
in enumerate(equalities
):
43 if not isinstance(equality
, Expression
):
44 raise TypeError('equalities must be linear expressions')
45 equalities
[i
] = equality
.scaleint()
46 if inequalities
is None:
49 for i
, inequality
in enumerate(inequalities
):
50 if not isinstance(inequality
, Expression
):
51 raise TypeError('inequalities must be linear expressions')
52 inequalities
[i
] = inequality
.scaleint()
53 symbols
= cls
._xsymbols
(equalities
+ inequalities
)
54 islbset
= cls
._toislbasicset
(equalities
, inequalities
, symbols
)
55 return cls
._fromislbasicset
(islbset
, symbols
)
60 Return a list of the equalities in a set.
62 return self
._equalities
65 def inequalities(self
):
67 Return a list of the inequalities in a set.
69 return self
._inequalities
72 def constraints(self
):
74 Return ta list of the constraints of a set.
76 return self
._constraints
84 Return a set as disjoint.
90 Return true if a set is the Universe set.
92 islbset
= self
._toislbasicset
(self
.equalities
, self
.inequalities
,
94 universe
= bool(libisl
.isl_basic_set_is_universe(islbset
))
95 libisl
.isl_basic_set_free(islbset
)
98 def aspolyhedron(self
):
100 Return polyhedral hull of a set.
104 def __contains__(self
, point
):
105 if not isinstance(point
, Point
):
106 raise TypeError('point must be a Point instance')
107 if self
.symbols
!= point
.symbols
:
108 raise ValueError('arguments must belong to the same space')
109 for equality
in self
.equalities
:
110 if equality
.subs(point
.coordinates()) != 0:
112 for inequality
in self
.inequalities
:
113 if inequality
.subs(point
.coordinates()) < 0:
117 def subs(self
, symbol
, expression
=None):
119 Subsitute the given value into an expression and return the resulting
122 equalities
= [equality
.subs(symbol
, expression
)
123 for equality
in self
.equalities
]
124 inequalities
= [inequality
.subs(symbol
, expression
)
125 for inequality
in self
.inequalities
]
126 return Polyhedron(equalities
, inequalities
)
128 def _asinequalities(self
):
129 inequalities
= list(self
.equalities
)
130 inequalities
.extend([-expression
for expression
in self
.equalities
])
131 inequalities
.extend(self
.inequalities
)
134 def widen(self
, other
):
135 if not isinstance(other
, Polyhedron
):
136 raise ValueError('argument must be a Polyhedron instance')
137 inequalities1
= self
._asinequalities
()
138 inequalities2
= other
._asinequalities
()
140 for inequality1
in inequalities1
:
141 if other
<= Polyhedron(inequalities
=[inequality1
]):
142 inequalities
.append(inequality1
)
143 for inequality2
in inequalities2
:
144 for i
in range(len(inequalities1
)):
145 inequalities3
= inequalities1
[:i
] + inequalities
[i
+ 1:]
146 inequalities3
.append(inequality2
)
147 polyhedron3
= Polyhedron(inequalities
=inequalities3
)
148 if self
== polyhedron3
:
149 inequalities
.append(inequality2
)
151 return Polyhedron(inequalities
=inequalities
)
154 def _fromislbasicset(cls
, islbset
, symbols
):
155 islconstraints
= islhelper
.isl_basic_set_constraints(islbset
)
158 for islconstraint
in islconstraints
:
159 constant
= libisl
.isl_constraint_get_constant_val(islconstraint
)
160 constant
= islhelper
.isl_val_to_int(constant
)
162 for index
, symbol
in enumerate(symbols
):
163 coefficient
= libisl
.isl_constraint_get_coefficient_val(islconstraint
,
164 libisl
.isl_dim_set
, index
)
165 coefficient
= islhelper
.isl_val_to_int(coefficient
)
167 coefficients
[symbol
] = coefficient
168 expression
= Expression(coefficients
, constant
)
169 if libisl
.isl_constraint_is_equality(islconstraint
):
170 equalities
.append(expression
)
172 inequalities
.append(expression
)
173 libisl
.isl_basic_set_free(islbset
)
174 self
= object().__new
__(Polyhedron
)
175 self
._equalities
= tuple(equalities
)
176 self
._inequalities
= tuple(inequalities
)
177 self
._constraints
= tuple(equalities
+ inequalities
)
178 self
._symbols
= cls
._xsymbols
(self
._constraints
)
179 self
._dimension
= len(self
._symbols
)
183 def _toislbasicset(cls
, equalities
, inequalities
, symbols
):
184 dimension
= len(symbols
)
185 indices
= {symbol
: index
for index
, symbol
in enumerate(symbols
)}
186 islsp
= libisl
.isl_space_set_alloc(mainctx
, 0, dimension
)
187 islbset
= libisl
.isl_basic_set_universe(libisl
.isl_space_copy(islsp
))
188 islls
= libisl
.isl_local_space_from_space(islsp
)
189 for equality
in equalities
:
190 isleq
= libisl
.isl_equality_alloc(libisl
.isl_local_space_copy(islls
))
191 for symbol
, coefficient
in equality
.coefficients():
192 islval
= str(coefficient
).encode()
193 islval
= libisl
.isl_val_read_from_str(mainctx
, islval
)
194 index
= indices
[symbol
]
195 isleq
= libisl
.isl_constraint_set_coefficient_val(isleq
,
196 libisl
.isl_dim_set
, index
, islval
)
197 if equality
.constant
!= 0:
198 islval
= str(equality
.constant
).encode()
199 islval
= libisl
.isl_val_read_from_str(mainctx
, islval
)
200 isleq
= libisl
.isl_constraint_set_constant_val(isleq
, islval
)
201 islbset
= libisl
.isl_basic_set_add_constraint(islbset
, isleq
)
202 for inequality
in inequalities
:
203 islin
= libisl
.isl_inequality_alloc(libisl
.isl_local_space_copy(islls
))
204 for symbol
, coefficient
in inequality
.coefficients():
205 islval
= str(coefficient
).encode()
206 islval
= libisl
.isl_val_read_from_str(mainctx
, islval
)
207 index
= indices
[symbol
]
208 islin
= libisl
.isl_constraint_set_coefficient_val(islin
,
209 libisl
.isl_dim_set
, index
, islval
)
210 if inequality
.constant
!= 0:
211 islval
= str(inequality
.constant
).encode()
212 islval
= libisl
.isl_val_read_from_str(mainctx
, islval
)
213 islin
= libisl
.isl_constraint_set_constant_val(islin
, islval
)
214 islbset
= libisl
.isl_basic_set_add_constraint(islbset
, islin
)
218 def fromstring(cls
, string
):
219 domain
= Domain
.fromstring(string
)
220 if not isinstance(domain
, Polyhedron
):
221 raise ValueError('non-polyhedral expression: {!r}'.format(string
))
226 for equality
in self
.equalities
:
227 strings
.append('Eq({}, 0)'.format(equality
))
228 for inequality
in self
.inequalities
:
229 strings
.append('Ge({}, 0)'.format(inequality
))
230 if len(strings
) == 1:
233 return 'And({})'.format(', '.join(strings
))
236 def _repr_latex_(self
):
238 for equality
in self
.equalities
:
239 strings
.append('{} = 0'.format(equality
._repr
_latex
_().strip('$')))
240 for inequality
in self
.inequalities
:
241 strings
.append('{} \\ge 0'.format(inequality
._repr
_latex
_().strip('$')))
242 return '$${}$$'.format(' \\wedge '.join(strings
))
245 def fromsympy(cls
, expr
):
247 Convert a sympy object to an expression.
249 domain
= Domain
.fromsympy(expr
)
250 if not isinstance(domain
, Polyhedron
):
251 raise ValueError('non-polyhedral expression: {!r}'.format(expr
))
256 Return an expression as a sympy object.
260 for equality
in self
.equalities
:
261 constraints
.append(sympy
.Eq(equality
.tosympy(), 0))
262 for inequality
in self
.inequalities
:
263 constraints
.append(sympy
.Ge(inequality
.tosympy(), 0))
264 return sympy
.And(*constraints
)
266 class EmptyType(Polyhedron
):
268 __slots__
= Polyhedron
.__slots
__
271 self
= object().__new
__(cls
)
272 self
._equalities
= (Rational(1),)
273 self
._inequalities
= ()
274 self
._constraints
= self
._equalities
279 def widen(self
, other
):
280 if not isinstance(other
, Polyhedron
):
281 raise ValueError('argument must be a Polyhedron instance')
287 def _repr_latex_(self
):
288 return '$$\\emptyset$$'
293 class UniverseType(Polyhedron
):
295 __slots__
= Polyhedron
.__slots
__
298 self
= object().__new
__(cls
)
299 self
._equalities
= ()
300 self
._inequalities
= ()
301 self
._constraints
= ()
309 def _repr_latex_(self
):
312 Universe
= UniverseType()
315 def _polymorphic(func
):
316 @functools.wraps(func
)
317 def wrapper(left
, right
):
318 if not isinstance(left
, Expression
):
319 if isinstance(left
, numbers
.Rational
):
320 left
= Rational(left
)
322 raise TypeError('left must be a a rational number '
323 'or a linear expression')
324 if not isinstance(right
, Expression
):
325 if isinstance(right
, numbers
.Rational
):
326 right
= Rational(right
)
328 raise TypeError('right must be a a rational number '
329 'or a linear expression')
330 return func(left
, right
)
336 Assert first set is less than the second set.
338 return Polyhedron([], [right
- left
- 1])
343 Assert first set is less than or equal to the second set.
345 return Polyhedron([], [right
- left
])
350 Assert first set is equal to the second set.
352 return Polyhedron([left
- right
], [])
357 Assert first set is not equal to the second set.
359 return ~
Eq(left
, right
)
364 Assert first set is greater than the second set.
366 return Polyhedron([], [left
- right
- 1])
371 Assert first set is greater than or equal to the second set.
373 return Polyhedron([], [left
- right
])