5 from fractions
import Fraction
7 from . import islhelper
8 from .islhelper
import mainctx
, libisl
, isl_set_basic_sets
9 from .coordinates
import Point
10 from .linexprs
import Expression
, Symbol
19 @functools.total_ordering
28 def __new__(cls
, *polyhedra
):
29 from .polyhedra
import Polyhedron
30 if len(polyhedra
) == 1:
31 polyhedron
= polyhedra
[0]
32 if isinstance(polyhedron
, str):
33 return cls
.fromstring(polyhedron
)
34 elif isinstance(polyhedron
, Polyhedron
):
37 raise TypeError('argument must be a string '
38 'or a Polyhedron instance')
40 for polyhedron
in polyhedra
:
41 if not isinstance(polyhedron
, Polyhedron
):
42 raise TypeError('arguments must be Polyhedron instances')
43 symbols
= cls
._xsymbols
(polyhedra
)
44 islset
= cls
._toislset
(polyhedra
, symbols
)
45 return cls
._fromislset
(islset
, symbols
)
48 def _xsymbols(cls
, iterator
):
50 Return the ordered tuple of symbols present in iterator.
54 symbols
.update(item
.symbols
)
55 return tuple(sorted(symbols
, key
=Symbol
.sortkey
))
59 return self
._polyhedra
67 return self
._dimension
70 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
71 islset
= libisl
.isl_set_make_disjoint(mainctx
, islset
)
72 return self
._fromislset
(islset
, self
.symbols
)
75 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
76 empty
= bool(libisl
.isl_set_is_empty(islset
))
77 libisl
.isl_set_free(islset
)
81 return not self
.isempty()
84 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
85 universe
= bool(libisl
.isl_set_plain_is_universe(islset
))
86 libisl
.isl_set_free(islset
)
90 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
91 bounded
= bool(libisl
.isl_set_is_bounded(islset
))
92 libisl
.isl_set_free(islset
)
95 def __eq__(self
, other
):
96 symbols
= self
._xsymbols
([self
, other
])
97 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
98 islset2
= other
._toislset
(other
.polyhedra
, symbols
)
99 equal
= bool(libisl
.isl_set_is_equal(islset1
, islset2
))
100 libisl
.isl_set_free(islset1
)
101 libisl
.isl_set_free(islset2
)
104 def isdisjoint(self
, other
):
105 symbols
= self
._xsymbols
([self
, other
])
106 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
107 islset2
= self
._toislset
(other
.polyhedra
, symbols
)
108 equal
= bool(libisl
.isl_set_is_disjoint(islset1
, islset2
))
109 libisl
.isl_set_free(islset1
)
110 libisl
.isl_set_free(islset2
)
113 def issubset(self
, other
):
114 symbols
= self
._xsymbols
([self
, other
])
115 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
116 islset2
= self
._toislset
(other
.polyhedra
, symbols
)
117 equal
= bool(libisl
.isl_set_is_subset(islset1
, islset2
))
118 libisl
.isl_set_free(islset1
)
119 libisl
.isl_set_free(islset2
)
122 def __le__(self
, other
):
123 return self
.issubset(other
)
125 def __lt__(self
, other
):
126 symbols
= self
._xsymbols
([self
, other
])
127 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
128 islset2
= self
._toislset
(other
.polyhedra
, symbols
)
129 equal
= bool(libisl
.isl_set_is_strict_subset(islset1
, islset2
))
130 libisl
.isl_set_free(islset1
)
131 libisl
.isl_set_free(islset2
)
134 def complement(self
):
135 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
136 islset
= libisl
.isl_set_complement(islset
)
137 return self
._fromislset
(islset
, self
.symbols
)
139 def __invert__(self
):
140 return self
.complement()
143 #does not change anything in any of the examples
144 #isl seems to do this naturally
145 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
146 islset
= libisl
.isl_set_remove_redundancies(islset
)
147 return self
._fromislset
(islset
, self
.symbols
)
149 def aspolyhedron(self
):
150 # several types of hull are available
151 # polyhedral seems to be the more appropriate, to be checked
152 from .polyhedra
import Polyhedron
153 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
154 islbset
= libisl
.isl_set_polyhedral_hull(islset
)
155 return Polyhedron
._fromislbasicset
(islbset
, self
.symbols
)
157 def project(self
, dims
):
158 # use to remove certain variables
159 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
161 for index
, symbol
in reversed(list(enumerate(self
.symbols
))):
165 islset
= libisl
.isl_set_project_out(islset
, libisl
.isl_dim_set
, index
+ 1, n
)
168 islset
= libisl
.isl_set_project_out(islset
, libisl
.isl_dim_set
, 0, n
)
169 dims
= [symbol
for symbol
in self
.symbols
if symbol
not in dims
]
170 return Domain
._fromislset
(islset
, dims
)
173 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
174 islpoint
= libisl
.isl_set_sample_point(islset
)
175 if bool(libisl
.isl_point_is_void(islpoint
)):
176 libisl
.isl_point_free(islpoint
)
177 raise ValueError('domain must be non-empty')
179 for index
, symbol
in enumerate(self
.symbols
):
180 coordinate
= libisl
.isl_point_get_coordinate_val(islpoint
,
181 libisl
.isl_dim_set
, index
)
182 coordinate
= islhelper
.isl_val_to_int(coordinate
)
183 point
[symbol
] = coordinate
184 libisl
.isl_point_free(islpoint
)
187 def intersection(self
, *others
):
190 symbols
= self
._xsymbols
((self
,) + others
)
191 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
193 islset2
= other
._toislset
(other
.polyhedra
, symbols
)
194 islset1
= libisl
.isl_set_intersect(islset1
, islset2
)
195 return self
._fromislset
(islset1
, symbols
)
197 def __and__(self
, other
):
198 return self
.intersection(other
)
200 def union(self
, *others
):
203 symbols
= self
._xsymbols
((self
,) + others
)
204 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
206 islset2
= other
._toislset
(other
.polyhedra
, symbols
)
207 islset1
= libisl
.isl_set_union(islset1
, islset2
)
208 return self
._fromislset
(islset1
, symbols
)
210 def __or__(self
, other
):
211 return self
.union(other
)
213 def __add__(self
, other
):
214 return self
.union(other
)
216 def difference(self
, other
):
217 symbols
= self
._xsymbols
([self
, other
])
218 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
219 islset2
= other
._toislset
(other
.polyhedra
, symbols
)
220 islset
= libisl
.isl_set_subtract(islset1
, islset2
)
221 return self
._fromislset
(islset
, symbols
)
223 def __sub__(self
, other
):
224 return self
.difference(other
)
227 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
228 islset
= libisl
.isl_set_lexmin(islset
)
229 return self
._fromislset
(islset
, self
.symbols
)
232 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
233 islset
= libisl
.isl_set_lexmax(islset
)
234 return self
._fromislset
(islset
, self
.symbols
)
236 def num_parameters(self
):
237 #could be useful with large, complicated polyhedrons
238 islbset
= self
._toislbasicset
(self
.equalities
, self
.inequalities
, self
.symbols
)
239 num
= libisl
.isl_basic_set_dim(islbset
, libisl
.isl_dim_set
)
242 def involves_dims(self
, dims
):
243 #could be useful with large, complicated polyhedrons
244 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
246 symbols
= sorted(list(self
.symbols
))
251 first
= symbols
.index(dims
[0])
257 value
= bool(libisl
.isl_set_involves_dims(islset
, libisl
.isl_dim_set
, first
, n
))
258 libisl
.isl_set_free(islset
)
261 _RE_COORDINATE
= re
.compile(r
'\((?P<num>\-?\d+)\)(/(?P<den>\d+))?')
264 #returning list of verticies
265 from .polyhedra
import Polyhedron
266 islbset
= self
._toislbasicset
(self
.equalities
, self
.inequalities
, self
.symbols
)
267 vertices
= libisl
.isl_basic_set_compute_vertices(islbset
);
268 vertices
= islhelper
.isl_vertices_vertices(vertices
)
270 for vertex
in vertices
:
271 expr
= libisl
.isl_vertex_get_expr(vertex
)
273 if islhelper
.isl_version
< '0.13':
274 constraints
= islhelper
.isl_basic_set_constraints(expr
)
275 for constraint
in constraints
:
276 constant
= libisl
.isl_constraint_get_constant_val(constraint
)
277 constant
= islhelper
.isl_val_to_int(constant
)
278 for index
, symbol
in enumerate(self
.symbols
):
279 coefficient
= libisl
.isl_constraint_get_coefficient_val(constraint
,
280 libisl
.isl_dim_set
, index
)
281 coefficient
= islhelper
.isl_val_to_int(coefficient
)
283 coordinate
= -Fraction(constant
, coefficient
)
284 coordinates
.append((symbol
, coordinate
))
286 # horrible hack, find a cleaner solution
287 string
= islhelper
.isl_multi_aff_to_str(expr
)
288 matches
= self
._RE
_COORDINATE
.finditer(string
)
289 for symbol
, match
in zip(self
.symbols
, matches
):
290 numerator
= int(match
.group('num'))
291 denominator
= match
.group('den')
292 denominator
= 1 if denominator
is None else int(denominator
)
293 coordinate
= Fraction(numerator
, denominator
)
294 coordinates
.append((symbol
, coordinate
))
295 points
.append(Point(coordinates
))
299 if not self
.isbounded():
300 raise ValueError('domain must be bounded')
301 from .polyhedra
import Universe
, Eq
302 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
303 islpoints
= islhelper
.isl_set_points(islset
)
305 for islpoint
in islpoints
:
307 for index
, symbol
in enumerate(self
.symbols
):
308 coordinate
= libisl
.isl_point_get_coordinate_val(islpoint
,
309 libisl
.isl_dim_set
, index
)
310 coordinate
= islhelper
.isl_val_to_int(coordinate
)
311 coordinates
[symbol
] = coordinate
312 points
.append(Point(coordinates
))
315 def subs(self
, symbol
, expression
=None):
316 polyhedra
= [polyhedron
.subs(symbol
, expression
)
317 for polyhedron
in self
.polyhedra
]
318 return Domain(*polyhedra
)
321 def _fromislset(cls
, islset
, symbols
):
322 from .polyhedra
import Polyhedron
323 islset
= libisl
.isl_set_remove_divs(islset
)
324 islbsets
= isl_set_basic_sets(islset
)
325 libisl
.isl_set_free(islset
)
327 for islbset
in islbsets
:
328 polyhedron
= Polyhedron
._fromislbasicset
(islbset
, symbols
)
329 polyhedra
.append(polyhedron
)
330 if len(polyhedra
) == 0:
331 from .polyhedra
import Empty
333 elif len(polyhedra
) == 1:
336 self
= object().__new
__(Domain
)
337 self
._polyhedra
= tuple(polyhedra
)
338 self
._symbols
= cls
._xsymbols
(polyhedra
)
339 self
._dimension
= len(self
._symbols
)
343 def _toislset(cls
, polyhedra
, symbols
):
344 polyhedron
= polyhedra
[0]
345 islbset
= polyhedron
._toislbasicset
(polyhedron
.equalities
,
346 polyhedron
.inequalities
, symbols
)
347 islset1
= libisl
.isl_set_from_basic_set(islbset
)
348 for polyhedron
in polyhedra
[1:]:
349 islbset
= polyhedron
._toislbasicset
(polyhedron
.equalities
,
350 polyhedron
.inequalities
, symbols
)
351 islset2
= libisl
.isl_set_from_basic_set(islbset
)
352 islset1
= libisl
.isl_set_union(islset1
, islset2
)
356 def _fromast(cls
, node
):
357 from .polyhedra
import Polyhedron
358 if isinstance(node
, ast
.Module
) and len(node
.body
) == 1:
359 return cls
._fromast
(node
.body
[0])
360 elif isinstance(node
, ast
.Expr
):
361 return cls
._fromast
(node
.value
)
362 elif isinstance(node
, ast
.UnaryOp
):
363 domain
= cls
._fromast
(node
.operand
)
364 if isinstance(node
.operand
, ast
.invert
):
366 elif isinstance(node
, ast
.BinOp
):
367 domain1
= cls
._fromast
(node
.left
)
368 domain2
= cls
._fromast
(node
.right
)
369 if isinstance(node
.op
, ast
.BitAnd
):
370 return And(domain1
, domain2
)
371 elif isinstance(node
.op
, ast
.BitOr
):
372 return Or(domain1
, domain2
)
373 elif isinstance(node
, ast
.Compare
):
376 left
= Expression
._fromast
(node
.left
)
377 for i
in range(len(node
.ops
)):
379 right
= Expression
._fromast
(node
.comparators
[i
])
380 if isinstance(op
, ast
.Lt
):
381 inequalities
.append(right
- left
- 1)
382 elif isinstance(op
, ast
.LtE
):
383 inequalities
.append(right
- left
)
384 elif isinstance(op
, ast
.Eq
):
385 equalities
.append(left
- right
)
386 elif isinstance(op
, ast
.GtE
):
387 inequalities
.append(left
- right
)
388 elif isinstance(op
, ast
.Gt
):
389 inequalities
.append(left
- right
- 1)
394 return Polyhedron(equalities
, inequalities
)
395 raise SyntaxError('invalid syntax')
397 _RE_BRACES
= re
.compile(r
'^\{\s*|\s*\}$')
398 _RE_EQ
= re
.compile(r
'([^<=>])=([^<=>])')
399 _RE_AND
= re
.compile(r
'\band\b|,|&&|/\\|∧|∩')
400 _RE_OR
= re
.compile(r
'\bor\b|;|\|\||\\/|∨|∪')
401 _RE_NOT
= re
.compile(r
'\bnot\b|!|¬')
402 _RE_NUM_VAR
= Expression
._RE
_NUM
_VAR
403 _RE_OPERATORS
= re
.compile(r
'(&|\||~)')
406 def fromstring(cls
, string
):
407 # remove curly brackets
408 string
= cls
._RE
_BRACES
.sub(r
'', string
)
409 # replace '=' by '=='
410 string
= cls
._RE
_EQ
.sub(r
'\1==\2', string
)
411 # replace 'and', 'or', 'not'
412 string
= cls
._RE
_AND
.sub(r
' & ', string
)
413 string
= cls
._RE
_OR
.sub(r
' | ', string
)
414 string
= cls
._RE
_NOT
.sub(r
' ~', string
)
415 # add implicit multiplication operators, e.g. '5x' -> '5*x'
416 string
= cls
._RE
_NUM
_VAR
.sub(r
'\1*\2', string
)
417 # add parentheses to force precedence
418 tokens
= cls
._RE
_OPERATORS
.split(string
)
419 for i
, token
in enumerate(tokens
):
421 token
= '({})'.format(token
)
423 string
= ''.join(tokens
)
424 tree
= ast
.parse(string
, 'eval')
425 return cls
._fromast
(tree
)
428 assert len(self
.polyhedra
) >= 2
429 strings
= [repr(polyhedron
) for polyhedron
in self
.polyhedra
]
430 return 'Or({})'.format(', '.join(strings
))
433 def fromsympy(cls
, expr
):
435 from .polyhedra
import Lt
, Le
, Eq
, Ne
, Ge
, Gt
437 sympy
.And
: And
, sympy
.Or
: Or
, sympy
.Not
: Not
,
438 sympy
.Lt
: Lt
, sympy
.Le
: Le
,
439 sympy
.Eq
: Eq
, sympy
.Ne
: Ne
,
440 sympy
.Ge
: Ge
, sympy
.Gt
: Gt
,
442 if expr
.func
in funcmap
:
443 args
= [Domain
.fromsympy(arg
) for arg
in expr
.args
]
444 return funcmap
[expr
.func
](*args
)
445 elif isinstance(expr
, sympy
.Expr
):
446 return Expression
.fromsympy(expr
)
447 raise ValueError('non-domain expression: {!r}'.format(expr
))
451 polyhedra
= [polyhedron
.tosympy() for polyhedron
in polyhedra
]
452 return sympy
.Or(*polyhedra
)
456 if len(domains
) == 0:
457 from .polyhedra
import Universe
460 return domains
[0].intersection(*domains
[1:])
463 if len(domains
) == 0:
464 from .polyhedra
import Empty
467 return domains
[0].union(*domains
[1:])