5 from . import islhelper
7 from .islhelper
import mainctx
, libisl
, isl_set_basic_sets
8 from .linexprs
import Expression
, Symbol
17 @functools.total_ordering
26 def __new__(cls
, *polyhedra
):
27 from .polyhedra
import Polyhedron
28 if len(polyhedra
) == 1:
29 polyhedron
= polyhedra
[0]
30 if isinstance(polyhedron
, str):
31 return cls
.fromstring(polyhedron
)
32 elif isinstance(polyhedron
, Polyhedron
):
35 raise TypeError('argument must be a string '
36 'or a Polyhedron instance')
38 for polyhedron
in polyhedra
:
39 if not isinstance(polyhedron
, Polyhedron
):
40 raise TypeError('arguments must be Polyhedron instances')
41 symbols
= cls
._xsymbols
(polyhedra
)
42 islset
= cls
._toislset
(polyhedra
, symbols
)
43 return cls
._fromislset
(islset
, symbols
)
46 def _xsymbols(cls
, iterator
):
48 Return the ordered tuple of symbols present in iterator.
52 symbols
.update(item
.symbols
)
53 return tuple(sorted(symbols
, key
=Symbol
.sortkey
))
57 return self
._polyhedra
65 return self
._dimension
68 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
69 islset
= libisl
.isl_set_make_disjoint(mainctx
, islset
)
70 return self
._fromislset
(islset
, self
.symbols
)
73 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
74 empty
= bool(libisl
.isl_set_is_empty(islset
))
75 libisl
.isl_set_free(islset
)
79 return not self
.isempty()
82 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
83 universe
= bool(libisl
.isl_set_plain_is_universe(islset
))
84 libisl
.isl_set_free(islset
)
88 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
89 bounded
= bool(libisl
.isl_set_is_bounded(islset
))
90 libisl
.isl_set_free(islset
)
93 def __eq__(self
, other
):
94 symbols
= self
._xsymbols
([self
, other
])
95 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
96 islset2
= other
._toislset
(other
.polyhedra
, symbols
)
97 equal
= bool(libisl
.isl_set_is_equal(islset1
, islset2
))
98 libisl
.isl_set_free(islset1
)
99 libisl
.isl_set_free(islset2
)
102 def isdisjoint(self
, other
):
103 symbols
= self
._xsymbols
([self
, other
])
104 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
105 islset2
= self
._toislset
(other
.polyhedra
, symbols
)
106 equal
= bool(libisl
.isl_set_is_disjoint(islset1
, islset2
))
107 libisl
.isl_set_free(islset1
)
108 libisl
.isl_set_free(islset2
)
111 def issubset(self
, other
):
112 symbols
= self
._xsymbols
([self
, other
])
113 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
114 islset2
= self
._toislset
(other
.polyhedra
, symbols
)
115 equal
= bool(libisl
.isl_set_is_subset(islset1
, islset2
))
116 libisl
.isl_set_free(islset1
)
117 libisl
.isl_set_free(islset2
)
120 def __le__(self
, other
):
121 return self
.issubset(other
)
123 def __lt__(self
, other
):
124 symbols
= self
._xsymbols
([self
, other
])
125 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
126 islset2
= self
._toislset
(other
.polyhedra
, symbols
)
127 equal
= bool(libisl
.isl_set_is_strict_subset(islset1
, islset2
))
128 libisl
.isl_set_free(islset1
)
129 libisl
.isl_set_free(islset2
)
132 def complement(self
):
133 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
134 islset
= libisl
.isl_set_complement(islset
)
135 return self
._fromislset
(islset
, self
.symbols
)
137 def __invert__(self
):
138 return self
.complement()
141 #does not change anything in any of the examples
142 #isl seems to do this naturally
143 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
144 islset
= libisl
.isl_set_remove_redundancies(islset
)
145 return self
._fromislset
(islset
, self
.symbols
)
147 def aspolyhedron(self
):
148 # several types of hull are available
149 # polyhedral seems to be the more appropriate, to be checked
150 from .polyhedra
import Polyhedron
151 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
152 islbset
= libisl
.isl_set_polyhedral_hull(islset
)
153 return Polyhedron
._fromislbasicset
(islbset
, self
.symbols
)
155 def project(self
, dims
):
156 # use to remove certain variables
157 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
159 for index
, symbol
in reversed(list(enumerate(self
.symbols
))):
163 islset
= libisl
.isl_set_project_out(islset
, libisl
.isl_dim_set
, index
+ 1, n
)
166 islset
= libisl
.isl_set_project_out(islset
, libisl
.isl_dim_set
, 0, n
)
167 dims
= [symbol
for symbol
in self
.symbols
if symbol
not in dims
]
168 return Domain
._fromislset
(islset
, dims
)
171 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
172 islpoint
= libisl
.isl_set_sample_point(islset
)
173 if bool(libisl
.isl_point_is_void(islpoint
)):
174 libisl
.isl_point_free(islpoint
)
175 raise ValueError('domain must be non-empty')
177 for index
, symbol
in enumerate(self
.symbols
):
178 coordinate
= libisl
.isl_point_get_coordinate_val(islpoint
,
179 libisl
.isl_dim_set
, index
)
180 coordinate
= islhelper
.isl_val_to_int(coordinate
)
181 point
[symbol
] = coordinate
182 libisl
.isl_point_free(islpoint
)
185 def intersection(self
, *others
):
188 symbols
= self
._xsymbols
((self
,) + others
)
189 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
191 islset2
= other
._toislset
(other
.polyhedra
, symbols
)
192 islset1
= libisl
.isl_set_intersect(islset1
, islset2
)
193 return self
._fromislset
(islset1
, symbols
)
195 def __and__(self
, other
):
196 return self
.intersection(other
)
198 def union(self
, *others
):
201 symbols
= self
._xsymbols
((self
,) + others
)
202 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
204 islset2
= other
._toislset
(other
.polyhedra
, symbols
)
205 islset1
= libisl
.isl_set_union(islset1
, islset2
)
206 return self
._fromislset
(islset1
, symbols
)
208 def __or__(self
, other
):
209 return self
.union(other
)
211 def __add__(self
, other
):
212 return self
.union(other
)
214 def difference(self
, other
):
215 symbols
= self
._xsymbols
([self
, other
])
216 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
217 islset2
= other
._toislset
(other
.polyhedra
, symbols
)
218 islset
= libisl
.isl_set_subtract(islset1
, islset2
)
219 return self
._fromislset
(islset
, symbols
)
221 def __sub__(self
, other
):
222 return self
.difference(other
)
225 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
226 islset
= libisl
.isl_set_lexmin(islset
)
227 return self
._fromislset
(islset
, self
.symbols
)
230 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
231 islset
= libisl
.isl_set_lexmax(islset
)
232 return self
._fromislset
(islset
, self
.symbols
)
234 def num_parameters(self
):
235 #could be useful with large, complicated polyhedrons
236 islbset
= self
._toislbasicset
(self
.equalities
, self
.inequalities
, self
.symbols
)
237 num
= libisl
.isl_basic_set_dim(islbset
, libisl
.isl_dim_set
)
240 def involves_dims(self
, dims
):
241 #could be useful with large, complicated polyhedrons
242 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
244 symbols
= sorted(list(self
.symbols
))
249 first
= symbols
.index(dims
[0])
255 value
= bool(libisl
.isl_set_involves_dims(islset
, libisl
.isl_dim_set
, first
, n
))
256 libisl
.isl_set_free(islset
)
260 islbset
= self
._toislbasicset
(self
.equalities
, self
.inequalities
, self
.symbols
)
261 vertices
= libisl
.isl_basic_set_compute_vertices(islbset
);
262 vertices
= islhelper
.isl_vertices_vertices(vertices
)
263 for vertex
in vertices
:
264 expr
= libisl
.isl_vertex_get_expr(vertex
);
265 if islhelper
.isl_version
< '0.13':
266 string
= islhelper
.isl_set_to_str(expr
)
268 string
= islhelper
.isl_multi_aff_to_str(expr
)
272 if not self
.isbounded():
273 raise ValueError('domain must be unbounded')
274 from .polyhedra
import Universe
, Eq
275 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
276 islpoints
= islhelper
.isl_set_points(islset
)
278 for islpoint
in islpoints
:
280 for index
, symbol
in enumerate(self
.symbols
):
281 coordinate
= libisl
.isl_point_get_coordinate_val(islpoint
,
282 libisl
.isl_dim_set
, index
)
283 coordinate
= islhelper
.isl_val_to_int(coordinate
)
284 point
[symbol
] = coordinate
289 def _fromislset(cls
, islset
, symbols
):
290 from .polyhedra
import Polyhedron
291 islset
= libisl
.isl_set_remove_divs(islset
)
292 islbsets
= isl_set_basic_sets(islset
)
293 libisl
.isl_set_free(islset
)
295 for islbset
in islbsets
:
296 polyhedron
= Polyhedron
._fromislbasicset
(islbset
, symbols
)
297 polyhedra
.append(polyhedron
)
298 if len(polyhedra
) == 0:
299 from .polyhedra
import Empty
301 elif len(polyhedra
) == 1:
304 self
= object().__new
__(Domain
)
305 self
._polyhedra
= tuple(polyhedra
)
306 self
._symbols
= cls
._xsymbols
(polyhedra
)
307 self
._dimension
= len(self
._symbols
)
310 def _toislset(cls
, polyhedra
, symbols
):
311 polyhedron
= polyhedra
[0]
312 islbset
= polyhedron
._toislbasicset
(polyhedron
.equalities
,
313 polyhedron
.inequalities
, symbols
)
314 islset1
= libisl
.isl_set_from_basic_set(islbset
)
315 for polyhedron
in polyhedra
[1:]:
316 islbset
= polyhedron
._toislbasicset
(polyhedron
.equalities
,
317 polyhedron
.inequalities
, symbols
)
318 islset2
= libisl
.isl_set_from_basic_set(islbset
)
319 islset1
= libisl
.isl_set_union(islset1
, islset2
)
323 def _fromast(cls
, node
):
324 from .polyhedra
import Polyhedron
325 if isinstance(node
, ast
.Module
) and len(node
.body
) == 1:
326 return cls
._fromast
(node
.body
[0])
327 elif isinstance(node
, ast
.Expr
):
328 return cls
._fromast
(node
.value
)
329 elif isinstance(node
, ast
.UnaryOp
):
330 domain
= cls
._fromast
(node
.operand
)
331 if isinstance(node
.operand
, ast
.invert
):
333 elif isinstance(node
, ast
.BinOp
):
334 domain1
= cls
._fromast
(node
.left
)
335 domain2
= cls
._fromast
(node
.right
)
336 if isinstance(node
.op
, ast
.BitAnd
):
337 return And(domain1
, domain2
)
338 elif isinstance(node
.op
, ast
.BitOr
):
339 return Or(domain1
, domain2
)
340 elif isinstance(node
, ast
.Compare
):
343 left
= Expression
._fromast
(node
.left
)
344 for i
in range(len(node
.ops
)):
346 right
= Expression
._fromast
(node
.comparators
[i
])
347 if isinstance(op
, ast
.Lt
):
348 inequalities
.append(right
- left
- 1)
349 elif isinstance(op
, ast
.LtE
):
350 inequalities
.append(right
- left
)
351 elif isinstance(op
, ast
.Eq
):
352 equalities
.append(left
- right
)
353 elif isinstance(op
, ast
.GtE
):
354 inequalities
.append(left
- right
)
355 elif isinstance(op
, ast
.Gt
):
356 inequalities
.append(left
- right
- 1)
361 return Polyhedron(equalities
, inequalities
)
362 raise SyntaxError('invalid syntax')
364 _RE_BRACES
= re
.compile(r
'^\{\s*|\s*\}$')
365 _RE_EQ
= re
.compile(r
'([^<=>])=([^<=>])')
366 _RE_AND
= re
.compile(r
'\band\b|,|&&|/\\|∧|∩')
367 _RE_OR
= re
.compile(r
'\bor\b|;|\|\||\\/|∨|∪')
368 _RE_NOT
= re
.compile(r
'\bnot\b|!|¬')
369 _RE_NUM_VAR
= Expression
._RE
_NUM
_VAR
370 _RE_OPERATORS
= re
.compile(r
'(&|\||~)')
373 def fromstring(cls
, string
):
374 # remove curly brackets
375 string
= cls
._RE
_BRACES
.sub(r
'', string
)
376 # replace '=' by '=='
377 string
= cls
._RE
_EQ
.sub(r
'\1==\2', string
)
378 # replace 'and', 'or', 'not'
379 string
= cls
._RE
_AND
.sub(r
' & ', string
)
380 string
= cls
._RE
_OR
.sub(r
' | ', string
)
381 string
= cls
._RE
_NOT
.sub(r
' ~', string
)
382 # add implicit multiplication operators, e.g. '5x' -> '5*x'
383 string
= cls
._RE
_NUM
_VAR
.sub(r
'\1*\2', string
)
384 # add parentheses to force precedence
385 tokens
= cls
._RE
_OPERATORS
.split(string
)
386 for i
, token
in enumerate(tokens
):
388 token
= '({})'.format(token
)
390 string
= ''.join(tokens
)
391 tree
= ast
.parse(string
, 'eval')
392 return cls
._fromast
(tree
)
395 assert len(self
.polyhedra
) >= 2
396 strings
= [repr(polyhedron
) for polyhedron
in self
.polyhedra
]
397 return 'Or({})'.format(', '.join(strings
))
400 def fromsympy(cls
, expr
):
402 from .polyhedra
import Lt
, Le
, Eq
, Ne
, Ge
, Gt
404 sympy
.And
: And
, sympy
.Or
: Or
, sympy
.Not
: Not
,
405 sympy
.Lt
: Lt
, sympy
.Le
: Le
,
406 sympy
.Eq
: Eq
, sympy
.Ne
: Ne
,
407 sympy
.Ge
: Ge
, sympy
.Gt
: Gt
,
409 if expr
.func
in funcmap
:
410 args
= [Domain
.fromsympy(arg
) for arg
in expr
.args
]
411 return funcmap
[expr
.func
](*args
)
412 elif isinstance(expr
, sympy
.Expr
):
413 return Expression
.fromsympy(expr
)
414 raise ValueError('non-domain expression: {!r}'.format(expr
))
418 polyhedra
= [polyhedron
.tosympy() for polyhedron
in polyhedra
]
419 return sympy
.Or(*polyhedra
)
423 if len(domains
) == 0:
424 from .polyhedra
import Universe
427 return domains
[0].intersection(*domains
[1:])
430 if len(domains
) == 0:
431 from .polyhedra
import Empty
434 return domains
[0].union(*domains
[1:])