7f4fa65408c154a1e0fcc1342b1733d4d32de78c
1 # Copyright 2014 MINES ParisTech
3 # This file is part of LinPy.
5 # LinPy is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
10 # LinPy is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with LinPy. If not, see <http://www.gnu.org/licenses/>.
23 from fractions
import Fraction
25 from . import islhelper
26 from .geometry
import GeometricObject
, Point
, Vector
27 from .islhelper
import libisl
28 from .linexprs
import LinExpr
, Symbol
39 @functools.total_ordering
40 class Domain(GeometricObject
):
42 A domain is a union of polyhedra. Unlike polyhedra, domains allow exact
43 computation of union, subtraction and complementary operations.
45 A domain with a unique polyhedron is automatically subclassed as a
55 def __new__(cls
, *polyhedra
):
57 Return a domain from a sequence of polyhedra.
59 >>> square1 = Polyhedron('0 <= x <= 2, 0 <= y <= 2')
60 >>> square2 = Polyhedron('1 <= x <= 3, 1 <= y <= 3')
61 >>> dom = Domain(square1, square2)
63 Or(And(x <= 2, 0 <= x, y <= 2, 0 <= y),
64 And(x <= 3, 1 <= x, y <= 3, 1 <= y))
66 It is also possible to build domains from polyhedra using arithmetic
67 operators Domain.__or__(), Domain.__invert__() or functions Or() and
68 Not(), using one of the following instructions:
70 >>> dom = square1 | square2
71 >>> dom = Or(square1, square2)
73 Alternatively, a domain can be built from a string:
75 >>> dom = Domain('0 <= x <= 2, 0 <= y <= 2; 1 <= x <= 3, 1 <= y <= 3')
77 Finally, a domain can be built from a GeometricObject instance, calling
78 the GeometricObject.asdomain() method.
80 from .polyhedra
import Polyhedron
81 if len(polyhedra
) == 1:
82 argument
= polyhedra
[0]
83 if isinstance(argument
, str):
84 return cls
.fromstring(argument
)
85 elif isinstance(argument
, GeometricObject
):
86 return argument
.aspolyhedron()
88 raise TypeError('argument must be a string '
89 'or a GeometricObject instance')
91 for polyhedron
in polyhedra
:
92 if not isinstance(polyhedron
, Polyhedron
):
93 raise TypeError('arguments must be Polyhedron instances')
94 symbols
= cls
._xsymbols
(polyhedra
)
95 islset
= cls
._toislset
(polyhedra
, symbols
)
96 return cls
._fromislset
(islset
, symbols
)
99 def _xsymbols(cls
, iterator
):
101 Return the ordered tuple of symbols present in iterator.
104 for item
in iterator
:
105 symbols
.update(item
.symbols
)
106 return tuple(sorted(symbols
, key
=Symbol
.sortkey
))
111 The tuple of polyhedra present in the domain.
113 return self
._polyhedra
118 The tuple of symbols present in the domain equations, sorted according
126 The dimension of the domain, i.e. the number of symbols present in it.
128 return self
._dimension
132 Return True if the domain is empty, that is, equal to Empty.
134 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
135 empty
= bool(libisl
.isl_set_is_empty(islset
))
136 libisl
.isl_set_free(islset
)
141 Return True if the domain is non-empty.
143 return not self
.isempty()
145 def isuniverse(self
):
147 Return True if the domain is universal, that is, equal to Universe.
149 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
150 universe
= bool(libisl
.isl_set_plain_is_universe(islset
))
151 libisl
.isl_set_free(islset
)
156 Return True if the domain is bounded.
158 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
159 bounded
= bool(libisl
.isl_set_is_bounded(islset
))
160 libisl
.isl_set_free(islset
)
163 def __eq__(self
, other
):
165 Return True if two domains are equal.
167 if isinstance(other
, Domain
):
168 symbols
= self
._xsymbols
([self
, other
])
169 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
170 islset2
= other
._toislset
(other
.polyhedra
, symbols
)
171 equal
= bool(libisl
.isl_set_is_equal(islset1
, islset2
))
172 libisl
.isl_set_free(islset1
)
173 libisl
.isl_set_free(islset2
)
175 return NotImplemented
177 def isdisjoint(self
, other
):
179 Return True if two domains have a null intersection.
181 if not isinstance(other
, Domain
):
182 raise TypeError('other must be a Domain instance')
183 symbols
= self
._xsymbols
([self
, other
])
184 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
185 islset2
= self
._toislset
(other
.polyhedra
, symbols
)
186 equal
= bool(libisl
.isl_set_is_disjoint(islset1
, islset2
))
187 libisl
.isl_set_free(islset1
)
188 libisl
.isl_set_free(islset2
)
191 def issubset(self
, other
):
193 Report whether another domain contains the domain.
197 def __le__(self
, other
):
198 if isinstance(other
, Domain
):
199 symbols
= self
._xsymbols
([self
, other
])
200 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
201 islset2
= self
._toislset
(other
.polyhedra
, symbols
)
202 equal
= bool(libisl
.isl_set_is_subset(islset1
, islset2
))
203 libisl
.isl_set_free(islset1
)
204 libisl
.isl_set_free(islset2
)
206 return NotImplemented
207 __le__
.__doc
__ = issubset
.__doc
__
209 def __lt__(self
, other
):
211 Report whether another domain is contained within the domain.
213 if isinstance(other
, Domain
):
214 symbols
= self
._xsymbols
([self
, other
])
215 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
216 islset2
= self
._toislset
(other
.polyhedra
, symbols
)
217 equal
= bool(libisl
.isl_set_is_strict_subset(islset1
, islset2
))
218 libisl
.isl_set_free(islset1
)
219 libisl
.isl_set_free(islset2
)
221 return NotImplemented
223 def complement(self
):
225 Return the complementary domain of the domain.
227 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
228 islset
= libisl
.isl_set_complement(islset
)
229 return self
._fromislset
(islset
, self
.symbols
)
231 def __invert__(self
):
232 return self
.complement()
233 __invert__
.__doc
__ = complement
.__doc
__
235 def make_disjoint(self
):
237 Return an equivalent domain, whose polyhedra are disjoint.
239 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
240 islset
= libisl
.isl_set_make_disjoint(islset
)
241 return self
._fromislset
(islset
, self
.symbols
)
245 Simplify the representation of the domain by trying to combine pairs of
246 polyhedra into a single polyhedron, and return the resulting domain.
248 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
249 islset
= libisl
.isl_set_coalesce(islset
)
250 return self
._fromislset
(islset
, self
.symbols
)
252 def detect_equalities(self
):
254 Simplify the representation of the domain by detecting implicit
255 equalities, and return the resulting domain.
257 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
258 islset
= libisl
.isl_set_detect_equalities(islset
)
259 return self
._fromislset
(islset
, self
.symbols
)
261 def remove_redundancies(self
):
263 Remove redundant constraints in the domain, and return the resulting
266 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
267 islset
= libisl
.isl_set_remove_redundancies(islset
)
268 return self
._fromislset
(islset
, self
.symbols
)
270 def aspolyhedron(self
):
271 from .polyhedra
import Polyhedron
272 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
273 islbset
= libisl
.isl_set_polyhedral_hull(islset
)
274 return Polyhedron
._fromislbasicset
(islbset
, self
.symbols
)
279 def project(self
, symbols
):
281 Project out the sequence of symbols given in arguments, and return the
284 symbols
= list(symbols
)
285 for symbol
in symbols
:
286 if not isinstance(symbol
, Symbol
):
287 raise TypeError('symbols must be Symbol instances')
288 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
290 for index
, symbol
in reversed(list(enumerate(self
.symbols
))):
291 if symbol
in symbols
:
294 islset
= libisl
.isl_set_project_out(
295 islset
, libisl
.isl_dim_set
, index
+ 1, n
)
298 islset
= libisl
.isl_set_project_out(
299 islset
, libisl
.isl_dim_set
, 0, n
)
300 symbols
= [symbol
for symbol
in self
.symbols
if symbol
not in symbols
]
301 return Domain
._fromislset
(islset
, symbols
)
305 Return a sample of the domain, as an integer instance of Point. If the
306 domain is empty, a ValueError exception is raised.
308 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
309 islpoint
= libisl
.isl_set_sample_point(islset
)
310 if bool(libisl
.isl_point_is_void(islpoint
)):
311 libisl
.isl_point_free(islpoint
)
312 raise ValueError('domain must be non-empty')
314 for index
, symbol
in enumerate(self
.symbols
):
315 coordinate
= libisl
.isl_point_get_coordinate_val(
316 islpoint
, libisl
.isl_dim_set
, index
)
317 coordinate
= islhelper
.isl_val_to_int(coordinate
)
318 point
[symbol
] = coordinate
319 libisl
.isl_point_free(islpoint
)
322 def intersection(self
, *others
):
324 Return the intersection of two or more domains as a new domain. As an
325 alternative, function And() can be used.
332 def __and__(self
, other
):
333 if isinstance(other
, Domain
):
334 symbols
= self
._xsymbols
([self
, other
])
335 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
336 islset2
= other
._toislset
(other
.polyhedra
, symbols
)
337 islset
= libisl
.isl_set_intersect(islset1
, islset2
)
338 return self
._fromislset
(islset
, symbols
)
339 return NotImplemented
340 __and__
.__doc
__ = intersection
.__doc
__
342 def union(self
, *others
):
344 Return the union of two or more domains as a new domain. As an
345 alternative, function Or() can be used.
352 def __or__(self
, other
):
353 if isinstance(other
, Domain
):
354 symbols
= self
._xsymbols
([self
, other
])
355 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
356 islset2
= other
._toislset
(other
.polyhedra
, symbols
)
357 islset
= libisl
.isl_set_union(islset1
, islset2
)
358 return self
._fromislset
(islset
, symbols
)
359 return NotImplemented
360 __or__
.__doc
__ = union
.__doc
__
362 def __add__(self
, other
):
364 __add__
.__doc
__ = union
.__doc
__
366 def difference(self
, other
):
368 Return the difference of two domains as a new domain.
372 def __sub__(self
, other
):
373 if isinstance(other
, Domain
):
374 symbols
= self
._xsymbols
([self
, other
])
375 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
376 islset2
= other
._toislset
(other
.polyhedra
, symbols
)
377 islset
= libisl
.isl_set_subtract(islset1
, islset2
)
378 return self
._fromislset
(islset
, symbols
)
379 return NotImplemented
380 __sub__
.__doc
__ = difference
.__doc
__
384 Return the lexicographic minimum of the elements in the domain.
386 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
387 islset
= libisl
.isl_set_lexmin(islset
)
388 return self
._fromislset
(islset
, self
.symbols
)
392 Return the lexicographic maximum of the elements in the domain.
394 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
395 islset
= libisl
.isl_set_lexmax(islset
)
396 return self
._fromislset
(islset
, self
.symbols
)
398 if islhelper
.isl_version
>= '0.13':
399 _RE_COORDINATE
= re
.compile(r
'\((?P<num>\-?\d+)\)(/(?P<den>\d+))?')
401 _RE_COORDINATE
= None
405 Return the vertices of the domain, as a list of rational instances of
408 if not self
.isbounded():
409 raise ValueError('domain must be bounded')
410 islbset
= self
._toislbasicset
(self
.equalities
, self
.inequalities
,
412 vertices
= libisl
.isl_basic_set_compute_vertices(islbset
)
413 vertices
= islhelper
.isl_vertices_vertices(vertices
)
415 for vertex
in vertices
:
416 expression
= libisl
.isl_vertex_get_expr(vertex
)
418 if self
._RE
_COORDINATE
is None:
419 constraints
= islhelper
.isl_basic_set_constraints(expression
)
420 for constraint
in constraints
:
421 constant
= libisl
.isl_constraint_get_constant_val(
423 constant
= islhelper
.isl_val_to_int(constant
)
424 for index
, symbol
in enumerate(self
.symbols
):
426 libisl
.isl_constraint_get_coefficient_val(
427 constraint
, libisl
.isl_dim_set
, index
)
428 coefficient
= islhelper
.isl_val_to_int(coefficient
)
430 coordinate
= -Fraction(constant
, coefficient
)
431 coordinates
.append((symbol
, coordinate
))
433 string
= islhelper
.isl_multi_aff_to_str(expression
)
434 matches
= self
._RE
_COORDINATE
.finditer(string
)
435 for symbol
, match
in zip(self
.symbols
, matches
):
436 numerator
= int(match
.group('num'))
437 denominator
= match
.group('den')
439 1 if denominator
is None else int(denominator
)
440 coordinate
= Fraction(numerator
, denominator
)
441 coordinates
.append((symbol
, coordinate
))
442 points
.append(Point(coordinates
))
447 Return the integer points of a bounded domain, as a list of integer
448 instances of Point. If the domain is not bounded, a ValueError
451 if not self
.isbounded():
452 raise ValueError('domain must be bounded')
453 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
454 islpoints
= islhelper
.isl_set_points(islset
)
456 for islpoint
in islpoints
:
458 for index
, symbol
in enumerate(self
.symbols
):
459 coordinate
= libisl
.isl_point_get_coordinate_val(
460 islpoint
, libisl
.isl_dim_set
, index
)
461 coordinate
= islhelper
.isl_val_to_int(coordinate
)
462 coordinates
[symbol
] = coordinate
463 points
.append(Point(coordinates
))
466 def __contains__(self
, point
):
468 Return True if the point if contained within the domain.
470 for polyhedron
in self
.polyhedra
:
471 if point
in polyhedron
:
476 def _polygon_inner_point(cls
, points
):
477 symbols
= points
[0].symbols
478 coordinates
= {symbol
: 0 for symbol
in symbols
}
480 for symbol
, coordinate
in point
.coordinates():
481 coordinates
[symbol
] += coordinate
482 for symbol
in symbols
:
483 coordinates
[symbol
] /= len(points
)
484 return Point(coordinates
)
487 def _sort_polygon_2d(cls
, points
):
490 o
= cls
._polygon
_inner
_point
(points
)
494 dx
, dy
= (coordinate
for symbol
, coordinate
in om
.coordinates())
495 angle
= math
.atan2(dy
, dx
)
497 return sorted(points
, key
=angles
.get
)
500 def _sort_polygon_3d(cls
, points
):
503 o
= cls
._polygon
_inner
_point
(points
)
514 raise ValueError('degenerate polygon')
518 normprod
= norm_oa
* om
.norm()
519 cosinus
= max(oa
.dot(om
) / normprod
, -1.)
520 sinus
= u
.dot(oa
.cross(om
)) / normprod
521 angle
= math
.acos(cosinus
)
522 angle
= math
.copysign(angle
, sinus
)
524 return sorted(points
, key
=angles
.get
)
528 Return the list of faces of a bounded domain. Each face is represented
529 by a list of vertices, in the form of rational instances of Point. If
530 the domain is not bounded, a ValueError exception is raised.
533 for polyhedron
in self
.polyhedra
:
534 vertices
= polyhedron
.vertices()
535 for constraint
in polyhedron
.constraints
:
537 for vertex
in vertices
:
538 if constraint
.subs(vertex
.coordinates()) == 0:
544 def _plot_2d(self
, plot
=None, **kwargs
):
545 import matplotlib
.pyplot
as plt
546 from matplotlib
.patches
import Polygon
549 plot
= fig
.add_subplot(1, 1, 1)
550 xmin
, xmax
= plot
.get_xlim()
551 ymin
, ymax
= plot
.get_ylim()
552 for polyhedron
in self
.polyhedra
:
553 vertices
= polyhedron
._sort
_polygon
_2d
(polyhedron
.vertices())
554 xys
= [tuple(vertex
.values()) for vertex
in vertices
]
556 xmin
, xmax
= min(xmin
, float(min(xs
))), max(xmax
, float(max(xs
)))
557 ymin
, ymax
= min(ymin
, float(min(ys
))), max(ymax
, float(max(ys
)))
558 plot
.add_patch(Polygon(xys
, closed
=True, **kwargs
))
559 plot
.set_xlim(xmin
, xmax
)
560 plot
.set_ylim(ymin
, ymax
)
563 def _plot_3d(self
, plot
=None, **kwargs
):
564 import matplotlib
.pyplot
as plt
565 from mpl_toolkits
.mplot3d
import Axes3D
566 from mpl_toolkits
.mplot3d
.art3d
import Poly3DCollection
572 xmin
, xmax
= axes
.get_xlim()
573 ymin
, ymax
= axes
.get_ylim()
574 zmin
, zmax
= axes
.get_zlim()
576 for vertices
in self
.faces():
577 vertices
= self
._sort
_polygon
_3d
(vertices
)
578 vertices
.append(vertices
[0])
579 face_xyzs
= [tuple(vertex
.values()) for vertex
in vertices
]
580 xs
, ys
, zs
= zip(*face_xyzs
)
581 xmin
, xmax
= min(xmin
, float(min(xs
))), max(xmax
, float(max(xs
)))
582 ymin
, ymax
= min(ymin
, float(min(ys
))), max(ymax
, float(max(ys
)))
583 zmin
, zmax
= min(zmin
, float(min(zs
))), max(zmax
, float(max(zs
)))
584 poly_xyzs
.append(face_xyzs
)
585 collection
= Poly3DCollection(poly_xyzs
, **kwargs
)
586 axes
.add_collection3d(collection
)
587 axes
.set_xlim(xmin
, xmax
)
588 axes
.set_ylim(ymin
, ymax
)
589 axes
.set_zlim(zmin
, zmax
)
592 def plot(self
, plot
=None, **kwargs
):
594 Plot a 2D or 3D domain using matplotlib. Draw it to the current plot
595 object if present, otherwise create a new one. options are keyword
596 arguments passed to the matplotlib drawing functions, they can be used
597 to set the drawing color for example. Raise ValueError is the domain is
600 if not self
.isbounded():
601 raise ValueError('domain must be bounded')
602 elif self
.dimension
== 2:
603 return self
._plot
_2d
(plot
=plot
, **kwargs
)
604 elif self
.dimension
== 3:
605 return self
._plot
_3d
(plot
=plot
, **kwargs
)
607 raise ValueError('domain must be 2 or 3-dimensional')
609 def subs(self
, symbol
, expression
=None):
611 Substitute the given symbol by an expression in the domain constraints.
612 To perform multiple substitutions at once, pass a sequence or a
613 dictionary of (old, new) pairs to subs. The syntax of this function is
614 similar to LinExpr.subs().
616 polyhedra
= [polyhedron
.subs(symbol
, expression
)
617 for polyhedron
in self
.polyhedra
]
618 return Domain(*polyhedra
)
621 def _fromislset(cls
, islset
, symbols
):
622 from .polyhedra
import Polyhedron
623 islset
= libisl
.isl_set_remove_divs(islset
)
624 islbsets
= islhelper
.isl_set_basic_sets(islset
)
625 libisl
.isl_set_free(islset
)
627 for islbset
in islbsets
:
628 polyhedron
= Polyhedron
._fromislbasicset
(islbset
, symbols
)
629 polyhedra
.append(polyhedron
)
630 if len(polyhedra
) == 0:
631 from .polyhedra
import Empty
633 elif len(polyhedra
) == 1:
636 self
= object().__new
__(Domain
)
637 self
._polyhedra
= tuple(polyhedra
)
638 self
._symbols
= cls
._xsymbols
(polyhedra
)
639 self
._dimension
= len(self
._symbols
)
643 def _toislset(cls
, polyhedra
, symbols
):
644 polyhedron
= polyhedra
[0]
645 islbset
= polyhedron
._toislbasicset
(
646 polyhedron
.equalities
, polyhedron
.inequalities
, symbols
)
647 islset1
= libisl
.isl_set_from_basic_set(islbset
)
648 for polyhedron
in polyhedra
[1:]:
649 islbset
= polyhedron
._toislbasicset
(
650 polyhedron
.equalities
, polyhedron
.inequalities
, symbols
)
651 islset2
= libisl
.isl_set_from_basic_set(islbset
)
652 islset1
= libisl
.isl_set_union(islset1
, islset2
)
656 def _fromast(cls
, node
):
657 from .polyhedra
import Polyhedron
658 if isinstance(node
, ast
.Module
) and len(node
.body
) == 1:
659 return cls
._fromast
(node
.body
[0])
660 elif isinstance(node
, ast
.Expr
):
661 return cls
._fromast
(node
.value
)
662 elif isinstance(node
, ast
.UnaryOp
):
663 domain
= cls
._fromast
(node
.operand
)
664 if isinstance(node
.operand
, ast
.invert
):
666 elif isinstance(node
, ast
.BinOp
):
667 domain1
= cls
._fromast
(node
.left
)
668 domain2
= cls
._fromast
(node
.right
)
669 if isinstance(node
.op
, ast
.BitAnd
):
670 return And(domain1
, domain2
)
671 elif isinstance(node
.op
, ast
.BitOr
):
672 return Or(domain1
, domain2
)
673 elif isinstance(node
, ast
.Compare
):
676 left
= LinExpr
._fromast
(node
.left
)
677 for i
in range(len(node
.ops
)):
679 right
= LinExpr
._fromast
(node
.comparators
[i
])
680 if isinstance(op
, ast
.Lt
):
681 inequalities
.append(right
- left
- 1)
682 elif isinstance(op
, ast
.LtE
):
683 inequalities
.append(right
- left
)
684 elif isinstance(op
, ast
.Eq
):
685 equalities
.append(left
- right
)
686 elif isinstance(op
, ast
.GtE
):
687 inequalities
.append(left
- right
)
688 elif isinstance(op
, ast
.Gt
):
689 inequalities
.append(left
- right
- 1)
694 return Polyhedron(equalities
, inequalities
)
695 raise SyntaxError('invalid syntax')
697 _RE_BRACES
= re
.compile(r
'^\{\s*|\s*\}$')
698 _RE_EQ
= re
.compile(r
'([^<=>])=([^<=>])')
699 _RE_AND
= re
.compile(r
'\band\b|,|&&|/\\|∧|∩')
700 _RE_OR
= re
.compile(r
'\bor\b|;|\|\||\\/|∨|∪')
701 _RE_NOT
= re
.compile(r
'\bnot\b|!|¬')
702 _RE_NUM_VAR
= LinExpr
._RE
_NUM
_VAR
703 _RE_OPERATORS
= re
.compile(r
'(&|\||~)')
706 def fromstring(cls
, string
):
708 Create a domain from a string. Raise SyntaxError if the string is not
711 # Remove curly brackets.
712 string
= cls
._RE
_BRACES
.sub(r
'', string
)
713 # Replace '=' by '=='.
714 string
= cls
._RE
_EQ
.sub(r
'\1==\2', string
)
715 # Replace 'and', 'or', 'not'.
716 string
= cls
._RE
_AND
.sub(r
' & ', string
)
717 string
= cls
._RE
_OR
.sub(r
' | ', string
)
718 string
= cls
._RE
_NOT
.sub(r
' ~', string
)
719 # Add implicit multiplication operators, e.g. '5x' -> '5*x'.
720 string
= cls
._RE
_NUM
_VAR
.sub(r
'\1*\2', string
)
721 # Add parentheses to force precedence.
722 tokens
= cls
._RE
_OPERATORS
.split(string
)
723 for i
, token
in enumerate(tokens
):
725 token
= '({})'.format(token
)
727 string
= ''.join(tokens
)
728 tree
= ast
.parse(string
, 'eval')
729 return cls
._fromast
(tree
)
732 assert len(self
.polyhedra
) >= 2
733 strings
= [repr(polyhedron
) for polyhedron
in self
.polyhedra
]
734 return 'Or({})'.format(', '.join(strings
))
737 def fromsympy(cls
, expression
):
739 Create a domain from a SymPy expression.
742 from .polyhedra
import Lt
, Le
, Eq
, Ne
, Ge
, Gt
744 sympy
.And
: And
, sympy
.Or
: Or
, sympy
.Not
: Not
,
745 sympy
.Lt
: Lt
, sympy
.Le
: Le
,
746 sympy
.Eq
: Eq
, sympy
.Ne
: Ne
,
747 sympy
.Ge
: Ge
, sympy
.Gt
: Gt
,
749 if expression
.func
in funcmap
:
750 args
= [Domain
.fromsympy(arg
) for arg
in expression
.args
]
751 return funcmap
[expression
.func
](*args
)
752 elif isinstance(expression
, sympy
.Expr
):
753 return LinExpr
.fromsympy(expression
)
754 raise ValueError('non-domain expression: {!r}'.format(expression
))
758 Convert the domain to a SymPy expression.
761 polyhedra
= [polyhedron
.tosympy() for polyhedron
in self
.polyhedra
]
762 return sympy
.Or(*polyhedra
)
767 Create the intersection domain of the domains given in arguments.
769 if len(domains
) == 0:
770 from .polyhedra
import Universe
773 return domains
[0].intersection(*domains
[1:])
778 Create the union domain of the domains given in arguments.
780 if len(domains
) == 0:
781 from .polyhedra
import Empty
784 return domains
[0].union(*domains
[1:])
789 Create the complementary domain of the domain given in argument.