6 from . import islhelper
8 from .islhelper
import mainctx
, libisl
9 from .coordinates
import Point
10 from .linexprs
import Expression
, Symbol
, Rational
11 from .domains
import Domain
16 'Lt', 'Le', 'Eq', 'Ne', 'Ge', 'Gt',
21 class Polyhedron(Domain
):
31 def __new__(cls
, equalities
=None, inequalities
=None):
32 if isinstance(equalities
, str):
33 if inequalities
is not None:
34 raise TypeError('too many arguments')
35 return cls
.fromstring(equalities
)
36 elif isinstance(equalities
, Polyhedron
):
37 if inequalities
is not None:
38 raise TypeError('too many arguments')
40 elif isinstance(equalities
, Domain
):
41 if inequalities
is not None:
42 raise TypeError('too many arguments')
43 return equalities
.aspolyhedron()
44 if equalities
is None:
47 for i
, equality
in enumerate(equalities
):
48 if not isinstance(equality
, Expression
):
49 raise TypeError('equalities must be linear expressions')
50 equalities
[i
] = equality
.scaleint()
51 if inequalities
is None:
54 for i
, inequality
in enumerate(inequalities
):
55 if not isinstance(inequality
, Expression
):
56 raise TypeError('inequalities must be linear expressions')
57 inequalities
[i
] = inequality
.scaleint()
58 symbols
= cls
._xsymbols
(equalities
+ inequalities
)
59 islbset
= cls
._toislbasicset
(equalities
, inequalities
, symbols
)
60 return cls
._fromislbasicset
(islbset
, symbols
)
64 return self
._equalities
67 def inequalities(self
):
68 return self
._inequalities
71 def constraints(self
):
72 return self
._constraints
82 islbset
= self
._toislbasicset
(self
.equalities
, self
.inequalities
,
84 universe
= bool(libisl
.isl_basic_set_is_universe(islbset
))
85 libisl
.isl_basic_set_free(islbset
)
88 def aspolyhedron(self
):
91 def __contains__(self
, point
):
92 if not isinstance(point
, Point
):
93 raise TypeError('point must be a Point instance')
94 if self
.symbols
!= point
.symbols
:
95 raise ValueError('arguments must belong to the same space')
96 for equality
in self
.equalities
:
97 if equality
.subs(point
.coordinates()) != 0:
99 for inequality
in self
.inequalities
:
100 if inequality
.subs(point
.coordinates()) < 0:
104 def subs(self
, symbol
, expression
=None):
105 equalities
= [equality
.subs(symbol
, expression
)
106 for equality
in self
.equalities
]
107 inequalities
= [inequality
.subs(symbol
, expression
)
108 for inequality
in self
.inequalities
]
109 return Polyhedron(equalities
, inequalities
)
112 def _fromislbasicset(cls
, islbset
, symbols
):
113 islconstraints
= islhelper
.isl_basic_set_constraints(islbset
)
116 for islconstraint
in islconstraints
:
117 constant
= libisl
.isl_constraint_get_constant_val(islconstraint
)
118 constant
= islhelper
.isl_val_to_int(constant
)
120 for index
, symbol
in enumerate(symbols
):
121 coefficient
= libisl
.isl_constraint_get_coefficient_val(islconstraint
,
122 libisl
.isl_dim_set
, index
)
123 coefficient
= islhelper
.isl_val_to_int(coefficient
)
125 coefficients
[symbol
] = coefficient
126 expression
= Expression(coefficients
, constant
)
127 if libisl
.isl_constraint_is_equality(islconstraint
):
128 equalities
.append(expression
)
130 inequalities
.append(expression
)
131 libisl
.isl_basic_set_free(islbset
)
132 self
= object().__new
__(Polyhedron
)
133 self
._equalities
= tuple(equalities
)
134 self
._inequalities
= tuple(inequalities
)
135 self
._constraints
= tuple(equalities
+ inequalities
)
136 self
._symbols
= cls
._xsymbols
(self
._constraints
)
137 self
._dimension
= len(self
._symbols
)
141 def _toislbasicset(cls
, equalities
, inequalities
, symbols
):
142 dimension
= len(symbols
)
143 indices
= {symbol
: index
for index
, symbol
in enumerate(symbols
)}
144 islsp
= libisl
.isl_space_set_alloc(mainctx
, 0, dimension
)
145 islbset
= libisl
.isl_basic_set_universe(libisl
.isl_space_copy(islsp
))
146 islls
= libisl
.isl_local_space_from_space(islsp
)
147 for equality
in equalities
:
148 isleq
= libisl
.isl_equality_alloc(libisl
.isl_local_space_copy(islls
))
149 for symbol
, coefficient
in equality
.coefficients():
150 islval
= str(coefficient
).encode()
151 islval
= libisl
.isl_val_read_from_str(mainctx
, islval
)
152 index
= indices
[symbol
]
153 isleq
= libisl
.isl_constraint_set_coefficient_val(isleq
,
154 libisl
.isl_dim_set
, index
, islval
)
155 if equality
.constant
!= 0:
156 islval
= str(equality
.constant
).encode()
157 islval
= libisl
.isl_val_read_from_str(mainctx
, islval
)
158 isleq
= libisl
.isl_constraint_set_constant_val(isleq
, islval
)
159 islbset
= libisl
.isl_basic_set_add_constraint(islbset
, isleq
)
160 for inequality
in inequalities
:
161 islin
= libisl
.isl_inequality_alloc(libisl
.isl_local_space_copy(islls
))
162 for symbol
, coefficient
in inequality
.coefficients():
163 islval
= str(coefficient
).encode()
164 islval
= libisl
.isl_val_read_from_str(mainctx
, islval
)
165 index
= indices
[symbol
]
166 islin
= libisl
.isl_constraint_set_coefficient_val(islin
,
167 libisl
.isl_dim_set
, index
, islval
)
168 if inequality
.constant
!= 0:
169 islval
= str(inequality
.constant
).encode()
170 islval
= libisl
.isl_val_read_from_str(mainctx
, islval
)
171 islin
= libisl
.isl_constraint_set_constant_val(islin
, islval
)
172 islbset
= libisl
.isl_basic_set_add_constraint(islbset
, islin
)
176 def fromstring(cls
, string
):
177 domain
= Domain
.fromstring(string
)
178 if not isinstance(domain
, Polyhedron
):
179 raise ValueError('non-polyhedral expression: {!r}'.format(string
))
185 elif self
.isuniverse():
189 for equality
in self
.equalities
:
190 strings
.append('0 == {}'.format(equality
))
191 for inequality
in self
.inequalities
:
192 strings
.append('0 <= {}'.format(inequality
))
193 if len(strings
) == 1:
196 return 'And({})'.format(', '.join(strings
))
199 def fromsympy(cls
, expr
):
200 domain
= Domain
.fromsympy(expr
)
201 if not isinstance(domain
, Polyhedron
):
202 raise ValueError('non-polyhedral expression: {!r}'.format(expr
))
208 for equality
in self
.equalities
:
209 constraints
.append(sympy
.Eq(equality
.tosympy(), 0))
210 for inequality
in self
.inequalities
:
211 constraints
.append(sympy
.Ge(inequality
.tosympy(), 0))
212 return sympy
.And(*constraints
)
215 def _sort_polygon_2d(cls
, points
):
218 o
= sum((Vector(point
) for point
in points
)) / len(points
)
219 o
= Point(o
.coordinates())
223 dx
, dy
= (coordinate
for symbol
, coordinates
in om
.coordinates())
224 angle
= math
.atan2(dy
, dx
)
226 return sorted(points
, key
=angles
.get
)
229 def _sort_polygon_3d(cls
, points
):
232 o
= sum((Vector(point
) for point
in points
)) / len(points
)
233 o
= Point(o
.coordinates())
238 u
= (oa
.cross(ob
)).asunit()
242 normprod
= norm_oa
* om
.norm()
243 cosinus
= oa
.dot(om
) / normprod
244 sinus
= u
.dot(oa
.cross(om
)) / normprod
245 angle
= math
.acos(cosinus
)
246 angle
= math
.copysign(angle
, sinus
)
248 return sorted(points
, key
=angles
.get
)
251 import matplotlib
.pyplot
as plt
252 from matplotlib
.path
import Path
253 import matplotlib
.patches
as patches
255 if len(self
.symbols
)> 3:
258 elif len(self
.symbols
) == 2:
259 verts
= self
.vertices()
261 codes
= [Path
.MOVETO
]
264 for sym
in sorted(vert
, key
=Symbol
.sortkey
):
266 pairs
= pairs
+ (num
,)
268 points
.append((0.0, 0.0))
271 codes
.append(Path
.LINETO
)
274 codes
.append(Path
.CLOSEPOLY
)
275 path
= Path(points
, codes
)
277 ax
= fig
.add_subplot(111)
278 patch
= patches
.PathPatch(path
, facecolor
='blue', lw
=2)
284 elif len(self
.symbols
)==3:
290 def _polymorphic(func
):
291 @functools.wraps(func
)
292 def wrapper(left
, right
):
293 if isinstance(left
, numbers
.Rational
):
294 left
= Rational(left
)
295 elif not isinstance(left
, Expression
):
296 raise TypeError('left must be a a rational number '
297 'or a linear expression')
298 if isinstance(right
, numbers
.Rational
):
299 right
= Rational(right
)
300 elif not isinstance(right
, Expression
):
301 raise TypeError('right must be a a rational number '
302 'or a linear expression')
303 return func(left
, right
)
308 return Polyhedron([], [right
- left
- 1])
312 return Polyhedron([], [right
- left
])
316 return Polyhedron([left
- right
], [])
320 return ~
Eq(left
, right
)
324 return Polyhedron([], [left
- right
- 1])
328 return Polyhedron([], [left
- right
])
333 Universe
= Polyhedron([])