5 from fractions
import Fraction
7 from . import islhelper
8 from .islhelper
import mainctx
, libisl
9 from .geometry
import GeometricObject
10 from .coordinates
import Point
11 from .linexprs
import Expression
, Symbol
20 @functools.total_ordering
21 class Domain(GeometricObject
):
29 def __new__(cls
, *polyhedra
):
30 from .polyhedra
import Polyhedron
31 if len(polyhedra
) == 1:
32 argument
= polyhedra
[0]
33 if isinstance(argument
, str):
34 return cls
.fromstring(argument
)
35 elif isinstance(argument
, GeometricObject
):
36 return argument
.aspolyhedron()
38 raise TypeError('argument must be a string '
39 'or a GeometricObject instance')
41 for polyhedron
in polyhedra
:
42 if not isinstance(polyhedron
, Polyhedron
):
43 raise TypeError('arguments must be Polyhedron instances')
44 symbols
= cls
._xsymbols
(polyhedra
)
45 islset
= cls
._toislset
(polyhedra
, symbols
)
46 return cls
._fromislset
(islset
, symbols
)
49 def _xsymbols(cls
, iterator
):
51 Return the ordered tuple of symbols present in iterator.
55 symbols
.update(item
.symbols
)
56 return tuple(sorted(symbols
, key
=Symbol
.sortkey
))
60 return self
._polyhedra
68 return self
._dimension
71 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
72 islset
= libisl
.isl_set_make_disjoint(mainctx
, islset
)
73 return self
._fromislset
(islset
, self
.symbols
)
76 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
77 empty
= bool(libisl
.isl_set_is_empty(islset
))
78 libisl
.isl_set_free(islset
)
82 return not self
.isempty()
85 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
86 universe
= bool(libisl
.isl_set_plain_is_universe(islset
))
87 libisl
.isl_set_free(islset
)
91 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
92 bounded
= bool(libisl
.isl_set_is_bounded(islset
))
93 libisl
.isl_set_free(islset
)
96 def __eq__(self
, other
):
97 symbols
= self
._xsymbols
([self
, other
])
98 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
99 islset2
= other
._toislset
(other
.polyhedra
, symbols
)
100 equal
= bool(libisl
.isl_set_is_equal(islset1
, islset2
))
101 libisl
.isl_set_free(islset1
)
102 libisl
.isl_set_free(islset2
)
105 def isdisjoint(self
, other
):
106 symbols
= self
._xsymbols
([self
, other
])
107 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
108 islset2
= self
._toislset
(other
.polyhedra
, symbols
)
109 equal
= bool(libisl
.isl_set_is_disjoint(islset1
, islset2
))
110 libisl
.isl_set_free(islset1
)
111 libisl
.isl_set_free(islset2
)
114 def issubset(self
, other
):
115 symbols
= self
._xsymbols
([self
, other
])
116 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
117 islset2
= self
._toislset
(other
.polyhedra
, symbols
)
118 equal
= bool(libisl
.isl_set_is_subset(islset1
, islset2
))
119 libisl
.isl_set_free(islset1
)
120 libisl
.isl_set_free(islset2
)
123 def __le__(self
, other
):
124 return self
.issubset(other
)
126 def __lt__(self
, other
):
127 symbols
= self
._xsymbols
([self
, other
])
128 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
129 islset2
= self
._toislset
(other
.polyhedra
, symbols
)
130 equal
= bool(libisl
.isl_set_is_strict_subset(islset1
, islset2
))
131 libisl
.isl_set_free(islset1
)
132 libisl
.isl_set_free(islset2
)
135 def complement(self
):
136 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
137 islset
= libisl
.isl_set_complement(islset
)
138 return self
._fromislset
(islset
, self
.symbols
)
140 def __invert__(self
):
141 return self
.complement()
144 #does not change anything in any of the examples
145 #isl seems to do this naturally
146 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
147 islset
= libisl
.isl_set_remove_redundancies(islset
)
148 return self
._fromislset
(islset
, self
.symbols
)
150 def aspolyhedron(self
):
151 # several types of hull are available
152 # polyhedral seems to be the more appropriate, to be checked
153 from .polyhedra
import Polyhedron
154 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
155 islbset
= libisl
.isl_set_polyhedral_hull(islset
)
156 return Polyhedron
._fromislbasicset
(islbset
, self
.symbols
)
161 def project(self
, dims
):
162 # use to remove certain variables
163 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
165 for index
, symbol
in reversed(list(enumerate(self
.symbols
))):
169 islset
= libisl
.isl_set_project_out(islset
, libisl
.isl_dim_set
, index
+ 1, n
)
172 islset
= libisl
.isl_set_project_out(islset
, libisl
.isl_dim_set
, 0, n
)
173 dims
= [symbol
for symbol
in self
.symbols
if symbol
not in dims
]
174 return Domain
._fromislset
(islset
, dims
)
177 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
178 islpoint
= libisl
.isl_set_sample_point(islset
)
179 if bool(libisl
.isl_point_is_void(islpoint
)):
180 libisl
.isl_point_free(islpoint
)
181 raise ValueError('domain must be non-empty')
183 for index
, symbol
in enumerate(self
.symbols
):
184 coordinate
= libisl
.isl_point_get_coordinate_val(islpoint
,
185 libisl
.isl_dim_set
, index
)
186 coordinate
= islhelper
.isl_val_to_int(coordinate
)
187 point
[symbol
] = coordinate
188 libisl
.isl_point_free(islpoint
)
191 def intersection(self
, *others
):
194 symbols
= self
._xsymbols
((self
,) + others
)
195 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
197 islset2
= other
._toislset
(other
.polyhedra
, symbols
)
198 islset1
= libisl
.isl_set_intersect(islset1
, islset2
)
199 return self
._fromislset
(islset1
, symbols
)
201 def __and__(self
, other
):
202 return self
.intersection(other
)
204 def union(self
, *others
):
207 symbols
= self
._xsymbols
((self
,) + others
)
208 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
210 islset2
= other
._toislset
(other
.polyhedra
, symbols
)
211 islset1
= libisl
.isl_set_union(islset1
, islset2
)
212 return self
._fromislset
(islset1
, symbols
)
214 def __or__(self
, other
):
215 return self
.union(other
)
217 def __add__(self
, other
):
218 return self
.union(other
)
220 def difference(self
, other
):
221 symbols
= self
._xsymbols
([self
, other
])
222 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
223 islset2
= other
._toislset
(other
.polyhedra
, symbols
)
224 islset
= libisl
.isl_set_subtract(islset1
, islset2
)
225 return self
._fromislset
(islset
, symbols
)
227 def __sub__(self
, other
):
228 return self
.difference(other
)
231 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
232 islset
= libisl
.isl_set_lexmin(islset
)
233 return self
._fromislset
(islset
, self
.symbols
)
236 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
237 islset
= libisl
.isl_set_lexmax(islset
)
238 return self
._fromislset
(islset
, self
.symbols
)
240 def num_parameters(self
):
241 #could be useful with large, complicated polyhedrons
242 islbset
= self
._toislbasicset
(self
.equalities
, self
.inequalities
, self
.symbols
)
243 num
= libisl
.isl_basic_set_dim(islbset
, libisl
.isl_dim_set
)
246 def involves_dims(self
, dims
):
247 #could be useful with large, complicated polyhedrons
248 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
250 symbols
= sorted(list(self
.symbols
))
255 first
= symbols
.index(dims
[0])
261 value
= bool(libisl
.isl_set_involves_dims(islset
, libisl
.isl_dim_set
, first
, n
))
262 libisl
.isl_set_free(islset
)
265 _RE_COORDINATE
= re
.compile(r
'\((?P<num>\-?\d+)\)(/(?P<den>\d+))?')
268 #returning list of verticies
269 from .polyhedra
import Polyhedron
270 islbset
= self
._toislbasicset
(self
.equalities
, self
.inequalities
, self
.symbols
)
271 vertices
= libisl
.isl_basic_set_compute_vertices(islbset
);
272 vertices
= islhelper
.isl_vertices_vertices(vertices
)
274 for vertex
in vertices
:
275 expr
= libisl
.isl_vertex_get_expr(vertex
)
277 if islhelper
.isl_version
< '0.13':
278 constraints
= islhelper
.isl_basic_set_constraints(expr
)
279 for constraint
in constraints
:
280 constant
= libisl
.isl_constraint_get_constant_val(constraint
)
281 constant
= islhelper
.isl_val_to_int(constant
)
282 for index
, symbol
in enumerate(self
.symbols
):
283 coefficient
= libisl
.isl_constraint_get_coefficient_val(constraint
,
284 libisl
.isl_dim_set
, index
)
285 coefficient
= islhelper
.isl_val_to_int(coefficient
)
287 coordinate
= -Fraction(constant
, coefficient
)
288 coordinates
.append((symbol
, coordinate
))
290 # horrible hack, find a cleaner solution
291 string
= islhelper
.isl_multi_aff_to_str(expr
)
292 matches
= self
._RE
_COORDINATE
.finditer(string
)
293 for symbol
, match
in zip(self
.symbols
, matches
):
294 numerator
= int(match
.group('num'))
295 denominator
= match
.group('den')
296 denominator
= 1 if denominator
is None else int(denominator
)
297 coordinate
= Fraction(numerator
, denominator
)
298 coordinates
.append((symbol
, coordinate
))
299 points
.append(Point(coordinates
))
303 if not self
.isbounded():
304 raise ValueError('domain must be bounded')
305 from .polyhedra
import Universe
, Eq
306 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
307 islpoints
= islhelper
.isl_set_points(islset
)
309 for islpoint
in islpoints
:
311 for index
, symbol
in enumerate(self
.symbols
):
312 coordinate
= libisl
.isl_point_get_coordinate_val(islpoint
,
313 libisl
.isl_dim_set
, index
)
314 coordinate
= islhelper
.isl_val_to_int(coordinate
)
315 coordinates
[symbol
] = coordinate
316 points
.append(Point(coordinates
))
319 def __contains__(self
, point
):
320 for polyhedron
in self
.polyhedra
:
321 if point
in polyhedron
:
325 def subs(self
, symbol
, expression
=None):
326 polyhedra
= [polyhedron
.subs(symbol
, expression
)
327 for polyhedron
in self
.polyhedra
]
328 return Domain(*polyhedra
)
331 def _fromislset(cls
, islset
, symbols
):
332 from .polyhedra
import Polyhedron
333 islset
= libisl
.isl_set_remove_divs(islset
)
334 islbsets
= islhelper
.isl_set_basic_sets(islset
)
335 libisl
.isl_set_free(islset
)
337 for islbset
in islbsets
:
338 polyhedron
= Polyhedron
._fromislbasicset
(islbset
, symbols
)
339 polyhedra
.append(polyhedron
)
340 if len(polyhedra
) == 0:
341 from .polyhedra
import Empty
343 elif len(polyhedra
) == 1:
346 self
= object().__new
__(Domain
)
347 self
._polyhedra
= tuple(polyhedra
)
348 self
._symbols
= cls
._xsymbols
(polyhedra
)
349 self
._dimension
= len(self
._symbols
)
353 def _toislset(cls
, polyhedra
, symbols
):
354 polyhedron
= polyhedra
[0]
355 islbset
= polyhedron
._toislbasicset
(polyhedron
.equalities
,
356 polyhedron
.inequalities
, symbols
)
357 islset1
= libisl
.isl_set_from_basic_set(islbset
)
358 for polyhedron
in polyhedra
[1:]:
359 islbset
= polyhedron
._toislbasicset
(polyhedron
.equalities
,
360 polyhedron
.inequalities
, symbols
)
361 islset2
= libisl
.isl_set_from_basic_set(islbset
)
362 islset1
= libisl
.isl_set_union(islset1
, islset2
)
366 def _fromast(cls
, node
):
367 from .polyhedra
import Polyhedron
368 if isinstance(node
, ast
.Module
) and len(node
.body
) == 1:
369 return cls
._fromast
(node
.body
[0])
370 elif isinstance(node
, ast
.Expr
):
371 return cls
._fromast
(node
.value
)
372 elif isinstance(node
, ast
.UnaryOp
):
373 domain
= cls
._fromast
(node
.operand
)
374 if isinstance(node
.operand
, ast
.invert
):
376 elif isinstance(node
, ast
.BinOp
):
377 domain1
= cls
._fromast
(node
.left
)
378 domain2
= cls
._fromast
(node
.right
)
379 if isinstance(node
.op
, ast
.BitAnd
):
380 return And(domain1
, domain2
)
381 elif isinstance(node
.op
, ast
.BitOr
):
382 return Or(domain1
, domain2
)
383 elif isinstance(node
, ast
.Compare
):
386 left
= Expression
._fromast
(node
.left
)
387 for i
in range(len(node
.ops
)):
389 right
= Expression
._fromast
(node
.comparators
[i
])
390 if isinstance(op
, ast
.Lt
):
391 inequalities
.append(right
- left
- 1)
392 elif isinstance(op
, ast
.LtE
):
393 inequalities
.append(right
- left
)
394 elif isinstance(op
, ast
.Eq
):
395 equalities
.append(left
- right
)
396 elif isinstance(op
, ast
.GtE
):
397 inequalities
.append(left
- right
)
398 elif isinstance(op
, ast
.Gt
):
399 inequalities
.append(left
- right
- 1)
404 return Polyhedron(equalities
, inequalities
)
405 raise SyntaxError('invalid syntax')
407 _RE_BRACES
= re
.compile(r
'^\{\s*|\s*\}$')
408 _RE_EQ
= re
.compile(r
'([^<=>])=([^<=>])')
409 _RE_AND
= re
.compile(r
'\band\b|,|&&|/\\|∧|∩')
410 _RE_OR
= re
.compile(r
'\bor\b|;|\|\||\\/|∨|∪')
411 _RE_NOT
= re
.compile(r
'\bnot\b|!|¬')
412 _RE_NUM_VAR
= Expression
._RE
_NUM
_VAR
413 _RE_OPERATORS
= re
.compile(r
'(&|\||~)')
416 def fromstring(cls
, string
):
417 # remove curly brackets
418 string
= cls
._RE
_BRACES
.sub(r
'', string
)
419 # replace '=' by '=='
420 string
= cls
._RE
_EQ
.sub(r
'\1==\2', string
)
421 # replace 'and', 'or', 'not'
422 string
= cls
._RE
_AND
.sub(r
' & ', string
)
423 string
= cls
._RE
_OR
.sub(r
' | ', string
)
424 string
= cls
._RE
_NOT
.sub(r
' ~', string
)
425 # add implicit multiplication operators, e.g. '5x' -> '5*x'
426 string
= cls
._RE
_NUM
_VAR
.sub(r
'\1*\2', string
)
427 # add parentheses to force precedence
428 tokens
= cls
._RE
_OPERATORS
.split(string
)
429 for i
, token
in enumerate(tokens
):
431 token
= '({})'.format(token
)
433 string
= ''.join(tokens
)
434 tree
= ast
.parse(string
, 'eval')
435 return cls
._fromast
(tree
)
438 assert len(self
.polyhedra
) >= 2
439 strings
= [repr(polyhedron
) for polyhedron
in self
.polyhedra
]
440 return 'Or({})'.format(', '.join(strings
))
443 def fromsympy(cls
, expr
):
445 from .polyhedra
import Lt
, Le
, Eq
, Ne
, Ge
, Gt
447 sympy
.And
: And
, sympy
.Or
: Or
, sympy
.Not
: Not
,
448 sympy
.Lt
: Lt
, sympy
.Le
: Le
,
449 sympy
.Eq
: Eq
, sympy
.Ne
: Ne
,
450 sympy
.Ge
: Ge
, sympy
.Gt
: Gt
,
452 if expr
.func
in funcmap
:
453 args
= [Domain
.fromsympy(arg
) for arg
in expr
.args
]
454 return funcmap
[expr
.func
](*args
)
455 elif isinstance(expr
, sympy
.Expr
):
456 return Expression
.fromsympy(expr
)
457 raise ValueError('non-domain expression: {!r}'.format(expr
))
461 polyhedra
= [polyhedron
.tosympy() for polyhedron
in polyhedra
]
462 return sympy
.Or(*polyhedra
)
466 if len(domains
) == 0:
467 from .polyhedra
import Universe
470 return domains
[0].intersection(*domains
[1:])
473 if len(domains
) == 0:
474 from .polyhedra
import Empty
477 return domains
[0].union(*domains
[1:])