5 from . import islhelper
7 from .islhelper
import mainctx
, libisl
8 from .geometry
import GeometricObject
, Point
, Vector
9 from .linexprs
import Expression
, Symbol
, 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
)
59 return self
._equalities
62 def inequalities(self
):
63 return self
._inequalities
66 def constraints(self
):
67 return self
._constraints
77 islbset
= self
._toislbasicset
(self
.equalities
, self
.inequalities
,
79 universe
= bool(libisl
.isl_basic_set_is_universe(islbset
))
80 libisl
.isl_basic_set_free(islbset
)
83 def aspolyhedron(self
):
86 def __contains__(self
, point
):
87 if not isinstance(point
, Point
):
88 raise TypeError('point must be a Point instance')
89 if self
.symbols
!= point
.symbols
:
90 raise ValueError('arguments must belong to the same space')
91 for equality
in self
.equalities
:
92 if equality
.subs(point
.coordinates()) != 0:
94 for inequality
in self
.inequalities
:
95 if inequality
.subs(point
.coordinates()) < 0:
99 def subs(self
, symbol
, expression
=None):
100 equalities
= [equality
.subs(symbol
, expression
)
101 for equality
in self
.equalities
]
102 inequalities
= [inequality
.subs(symbol
, expression
)
103 for inequality
in self
.inequalities
]
104 return Polyhedron(equalities
, inequalities
)
107 def _fromislbasicset(cls
, islbset
, symbols
):
108 islconstraints
= islhelper
.isl_basic_set_constraints(islbset
)
111 for islconstraint
in islconstraints
:
112 constant
= libisl
.isl_constraint_get_constant_val(islconstraint
)
113 constant
= islhelper
.isl_val_to_int(constant
)
115 for index
, symbol
in enumerate(symbols
):
116 coefficient
= libisl
.isl_constraint_get_coefficient_val(islconstraint
,
117 libisl
.isl_dim_set
, index
)
118 coefficient
= islhelper
.isl_val_to_int(coefficient
)
120 coefficients
[symbol
] = coefficient
121 expression
= Expression(coefficients
, constant
)
122 if libisl
.isl_constraint_is_equality(islconstraint
):
123 equalities
.append(expression
)
125 inequalities
.append(expression
)
126 libisl
.isl_basic_set_free(islbset
)
127 self
= object().__new
__(Polyhedron
)
128 self
._equalities
= tuple(equalities
)
129 self
._inequalities
= tuple(inequalities
)
130 self
._constraints
= tuple(equalities
+ inequalities
)
131 self
._symbols
= cls
._xsymbols
(self
._constraints
)
132 self
._dimension
= len(self
._symbols
)
136 def _toislbasicset(cls
, equalities
, inequalities
, symbols
):
137 dimension
= len(symbols
)
138 indices
= {symbol
: index
for index
, symbol
in enumerate(symbols
)}
139 islsp
= libisl
.isl_space_set_alloc(mainctx
, 0, dimension
)
140 islbset
= libisl
.isl_basic_set_universe(libisl
.isl_space_copy(islsp
))
141 islls
= libisl
.isl_local_space_from_space(islsp
)
142 for equality
in equalities
:
143 isleq
= libisl
.isl_equality_alloc(libisl
.isl_local_space_copy(islls
))
144 for symbol
, coefficient
in equality
.coefficients():
145 islval
= str(coefficient
).encode()
146 islval
= libisl
.isl_val_read_from_str(mainctx
, islval
)
147 index
= indices
[symbol
]
148 isleq
= libisl
.isl_constraint_set_coefficient_val(isleq
,
149 libisl
.isl_dim_set
, index
, islval
)
150 if equality
.constant
!= 0:
151 islval
= str(equality
.constant
).encode()
152 islval
= libisl
.isl_val_read_from_str(mainctx
, islval
)
153 isleq
= libisl
.isl_constraint_set_constant_val(isleq
, islval
)
154 islbset
= libisl
.isl_basic_set_add_constraint(islbset
, isleq
)
155 for inequality
in inequalities
:
156 islin
= libisl
.isl_inequality_alloc(libisl
.isl_local_space_copy(islls
))
157 for symbol
, coefficient
in inequality
.coefficients():
158 islval
= str(coefficient
).encode()
159 islval
= libisl
.isl_val_read_from_str(mainctx
, islval
)
160 index
= indices
[symbol
]
161 islin
= libisl
.isl_constraint_set_coefficient_val(islin
,
162 libisl
.isl_dim_set
, index
, islval
)
163 if inequality
.constant
!= 0:
164 islval
= str(inequality
.constant
).encode()
165 islval
= libisl
.isl_val_read_from_str(mainctx
, islval
)
166 islin
= libisl
.isl_constraint_set_constant_val(islin
, islval
)
167 islbset
= libisl
.isl_basic_set_add_constraint(islbset
, islin
)
171 def fromstring(cls
, string
):
172 domain
= Domain
.fromstring(string
)
173 if not isinstance(domain
, Polyhedron
):
174 raise ValueError('non-polyhedral expression: {!r}'.format(string
))
180 elif self
.isuniverse():
184 for equality
in self
.equalities
:
185 strings
.append('0 == {}'.format(equality
))
186 for inequality
in self
.inequalities
:
187 strings
.append('0 <= {}'.format(inequality
))
188 if len(strings
) == 1:
191 return 'And({})'.format(', '.join(strings
))
194 def fromsympy(cls
, expr
):
195 domain
= Domain
.fromsympy(expr
)
196 if not isinstance(domain
, Polyhedron
):
197 raise ValueError('non-polyhedral expression: {!r}'.format(expr
))
203 for equality
in self
.equalities
:
204 constraints
.append(sympy
.Eq(equality
.tosympy(), 0))
205 for inequality
in self
.inequalities
:
206 constraints
.append(sympy
.Ge(inequality
.tosympy(), 0))
207 return sympy
.And(*constraints
)
210 def _polygon_inner_point(cls
, points
):
211 symbols
= points
[0].symbols
212 coordinates
= {symbol
: 0 for symbol
in symbols
}
214 for symbol
, coordinate
in point
.coordinates():
215 coordinates
[symbol
] += coordinate
216 for symbol
in symbols
:
217 coordinates
[symbol
] /= len(points
)
218 return Point(coordinates
)
221 def _sort_polygon_2d(cls
, points
):
224 o
= cls
._polygon
_inner
_point
(points
)
228 dx
, dy
= (coordinate
for symbol
, coordinate
in om
.coordinates())
229 angle
= math
.atan2(dy
, dx
)
231 return sorted(points
, key
=angles
.get
)
234 def _sort_polygon_3d(cls
, points
):
237 o
= cls
._polygon
_inner
_point
(points
)
248 raise ValueError('degenerate polygon')
252 normprod
= norm_oa
* om
.norm()
253 cosinus
= oa
.dot(om
) / normprod
254 sinus
= u
.dot(oa
.cross(om
)) / normprod
255 angle
= math
.acos(cosinus
)
256 angle
= math
.copysign(angle
, sinus
)
258 return sorted(points
, key
=angles
.get
)
261 vertices
= self
.vertices()
263 for constraint
in self
.constraints
:
265 for vertex
in vertices
:
266 if constraint
.subs(vertex
.coordinates()) == 0:
272 import matplotlib
.pyplot
as plt
273 from matplotlib
.path
import Path
274 import matplotlib
.patches
as patches
276 if len(self
.symbols
)> 3:
279 elif len(self
.symbols
) == 2:
280 verts
= self
.vertices()
282 codes
= [Path
.MOVETO
]
285 for sym
in sorted(vert
, key
=Symbol
.sortkey
):
287 pairs
= pairs
+ (num
,)
289 points
.append((0.0, 0.0))
292 codes
.append(Path
.LINETO
)
295 codes
.append(Path
.CLOSEPOLY
)
296 path
= Path(points
, codes
)
298 ax
= fig
.add_subplot(111)
299 patch
= patches
.PathPatch(path
, facecolor
='blue', lw
=2)
305 elif len(self
.symbols
)==3:
311 def _polymorphic(func
):
312 @functools.wraps(func
)
313 def wrapper(left
, right
):
314 if not isinstance(left
, Expression
):
315 if isinstance(left
, numbers
.Rational
):
316 left
= Rational(left
)
318 raise TypeError('left must be a a rational number '
319 'or a linear expression')
320 if not isinstance(right
, Expression
):
321 if isinstance(right
, numbers
.Rational
):
322 right
= Rational(right
)
324 raise TypeError('right must be a a rational number '
325 'or a linear expression')
326 return func(left
, right
)
331 return Polyhedron([], [right
- left
- 1])
335 return Polyhedron([], [right
- left
])
339 return Polyhedron([left
- right
], [])
343 return ~
Eq(left
, right
)
347 return Polyhedron([], [left
- right
- 1])
351 return Polyhedron([], [left
- right
])
356 Universe
= Polyhedron([])