70152523d9b3373d2f1aed5912655d3abb3d36aa
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 .islhelper
import mainctx
, libisl
27 from .linexprs
import LinExpr
, Symbol
, Rational
28 from .geometry
import GeometricObject
, Point
, Vector
37 @functools.total_ordering
38 class Domain(GeometricObject
):
40 A domain is a union of polyhedra. Unlike polyhedra, domains allow exact
41 computation of union and complementary operations.
43 A domain with a unique polyhedron is automatically subclassed as a
53 def __new__(cls
, *polyhedra
):
55 Return a domain from a sequence of polyhedra.
57 >>> square = Polyhedron('0 <= x <= 2, 0 <= y <= 2')
58 >>> square2 = Polyhedron('2 <= x <= 4, 2 <= y <= 4')
59 >>> dom = Domain([square, square2])
61 It is also possible to build domains from polyhedra using arithmetic
62 operators Domain.__and__(), Domain.__or__() or functions And() and Or(),
63 using one of the following instructions:
65 >>> square = Polyhedron('0 <= x <= 2, 0 <= y <= 2')
66 >>> square2 = Polyhedron('2 <= x <= 4, 2 <= y <= 4')
67 >>> dom = square | square2
68 >>> dom = Or(square, square2)
70 Alternatively, a domain can be built from a string:
72 >>> dom = Domain('0 <= x <= 2, 0 <= y <= 2; 2 <= x <= 4, 2 <= y <= 4')
74 Finally, a domain can be built from a GeometricObject instance, calling
75 the GeometricObject.asdomain() method.
77 from .polyhedra
import Polyhedron
78 if len(polyhedra
) == 1:
79 argument
= polyhedra
[0]
80 if isinstance(argument
, str):
81 return cls
.fromstring(argument
)
82 elif isinstance(argument
, GeometricObject
):
83 return argument
.aspolyhedron()
85 raise TypeError('argument must be a string '
86 'or a GeometricObject instance')
88 for polyhedron
in polyhedra
:
89 if not isinstance(polyhedron
, Polyhedron
):
90 raise TypeError('arguments must be Polyhedron instances')
91 symbols
= cls
._xsymbols
(polyhedra
)
92 islset
= cls
._toislset
(polyhedra
, symbols
)
93 return cls
._fromislset
(islset
, symbols
)
96 def _xsymbols(cls
, iterator
):
98 Return the ordered tuple of symbols present in iterator.
101 for item
in iterator
:
102 symbols
.update(item
.symbols
)
103 return tuple(sorted(symbols
, key
=Symbol
.sortkey
))
108 The tuple of polyhedra present in the domain.
110 return self
._polyhedra
115 The tuple of symbols present in the domain equations, sorted according
123 The dimension of the domain, i.e. the number of symbols present in it.
125 return self
._dimension
129 Return True if the domain is empty, that is, equal to Empty.
131 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
132 empty
= bool(libisl
.isl_set_is_empty(islset
))
133 libisl
.isl_set_free(islset
)
138 Return True if the domain is non-empty.
140 return not self
.isempty()
142 def isuniverse(self
):
144 Return True if the domain is universal, that is, equal to Universe.
146 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
147 universe
= bool(libisl
.isl_set_plain_is_universe(islset
))
148 libisl
.isl_set_free(islset
)
153 Return True if the domain is bounded.
155 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
156 bounded
= bool(libisl
.isl_set_is_bounded(islset
))
157 libisl
.isl_set_free(islset
)
160 def __eq__(self
, other
):
162 Return True if two domains are equal.
164 if isinstance(other
, Domain
):
165 symbols
= self
._xsymbols
([self
, other
])
166 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
167 islset2
= other
._toislset
(other
.polyhedra
, symbols
)
168 equal
= bool(libisl
.isl_set_is_equal(islset1
, islset2
))
169 libisl
.isl_set_free(islset1
)
170 libisl
.isl_set_free(islset2
)
172 return NotImplemented
174 def isdisjoint(self
, other
):
176 Return True if two domains have a null intersection.
178 if not isinstance(other
, Domain
):
179 raise TypeError('other must be a Domain instance')
180 symbols
= self
._xsymbols
([self
, other
])
181 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
182 islset2
= self
._toislset
(other
.polyhedra
, symbols
)
183 equal
= bool(libisl
.isl_set_is_disjoint(islset1
, islset2
))
184 libisl
.isl_set_free(islset1
)
185 libisl
.isl_set_free(islset2
)
188 def issubset(self
, other
):
190 Report whether another domain contains the domain.
194 def __le__(self
, other
):
195 if isinstance(other
, Domain
):
196 symbols
= self
._xsymbols
([self
, other
])
197 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
198 islset2
= self
._toislset
(other
.polyhedra
, symbols
)
199 equal
= bool(libisl
.isl_set_is_subset(islset1
, islset2
))
200 libisl
.isl_set_free(islset1
)
201 libisl
.isl_set_free(islset2
)
203 return NotImplemented
204 __le__
.__doc
__ = issubset
.__doc
__
206 def __lt__(self
, other
):
208 Report whether another domain is contained within the domain.
210 if isinstance(other
, Domain
):
211 symbols
= self
._xsymbols
([self
, other
])
212 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
213 islset2
= self
._toislset
(other
.polyhedra
, symbols
)
214 equal
= bool(libisl
.isl_set_is_strict_subset(islset1
, islset2
))
215 libisl
.isl_set_free(islset1
)
216 libisl
.isl_set_free(islset2
)
218 return NotImplemented
220 def complement(self
):
222 Return the complementary domain of the domain.
224 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
225 islset
= libisl
.isl_set_complement(islset
)
226 return self
._fromislset
(islset
, self
.symbols
)
228 def __invert__(self
):
229 return self
.complement()
230 __invert__
.__doc
__ = complement
.__doc
__
232 def make_disjoint(self
):
234 Return an equivalent domain, whose polyhedra are disjoint.
236 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
237 islset
= libisl
.isl_set_make_disjoint(mainctx
, islset
)
238 return self
._fromislset
(islset
, self
.symbols
)
242 Simplify the representation of the domain by trying to combine pairs of
243 polyhedra into a single polyhedron, and return the resulting domain.
245 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
246 islset
= libisl
.isl_set_coalesce(islset
)
247 return self
._fromislset
(islset
, self
.symbols
)
249 def detect_equalities(self
):
251 Simplify the representation of the domain by detecting implicit
252 equalities, and return the resulting domain.
254 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
255 islset
= libisl
.isl_set_detect_equalities(islset
)
256 return self
._fromislset
(islset
, self
.symbols
)
258 def remove_redundancies(self
):
260 Remove redundant constraints in the domain, and return the resulting
263 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
264 islset
= libisl
.isl_set_remove_redundancies(islset
)
265 return self
._fromislset
(islset
, self
.symbols
)
267 def aspolyhedron(self
):
268 from .polyhedra
import Polyhedron
269 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
270 islbset
= libisl
.isl_set_polyhedral_hull(islset
)
271 return Polyhedron
._fromislbasicset
(islbset
, self
.symbols
)
276 def project(self
, symbols
):
278 Project out the sequence of symbols given in arguments, and return the
281 symbols
= list(symbols
)
282 for symbol
in symbols
:
283 if not isinstance(symbol
, Symbol
):
284 raise TypeError('symbols must be Symbol instances')
285 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
287 for index
, symbol
in reversed(list(enumerate(self
.symbols
))):
288 if symbol
in symbols
:
291 islset
= libisl
.isl_set_project_out(islset
,
292 libisl
.isl_dim_set
, index
+ 1, n
)
295 islset
= libisl
.isl_set_project_out(islset
, libisl
.isl_dim_set
, 0, n
)
296 symbols
= [symbol
for symbol
in self
.symbols
if symbol
not in symbols
]
297 return Domain
._fromislset
(islset
, symbols
)
301 Return a sample of the domain, as an integer instance of Point. If the
302 domain is empty, a ValueError exception is raised.
304 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
305 islpoint
= libisl
.isl_set_sample_point(islset
)
306 if bool(libisl
.isl_point_is_void(islpoint
)):
307 libisl
.isl_point_free(islpoint
)
308 raise ValueError('domain must be non-empty')
310 for index
, symbol
in enumerate(self
.symbols
):
311 coordinate
= libisl
.isl_point_get_coordinate_val(islpoint
,
312 libisl
.isl_dim_set
, index
)
313 coordinate
= islhelper
.isl_val_to_int(coordinate
)
314 point
[symbol
] = coordinate
315 libisl
.isl_point_free(islpoint
)
318 def intersection(self
, *others
):
320 Return the intersection of two or more domains as a new domain. As an
321 alternative, function And() can be used.
325 symbols
= self
._xsymbols
((self
,) + others
)
326 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
328 islset2
= other
._toislset
(other
.polyhedra
, symbols
)
329 islset1
= libisl
.isl_set_intersect(islset1
, islset2
)
330 return self
._fromislset
(islset1
, symbols
)
332 def __and__(self
, other
):
333 return self
.intersection(other
)
334 __and__
.__doc
__ = intersection
.__doc
__
336 def union(self
, *others
):
338 Return the union of two or more domains as a new domain. As an
339 alternative, function Or() can be used.
343 symbols
= self
._xsymbols
((self
,) + others
)
344 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
346 islset2
= other
._toislset
(other
.polyhedra
, symbols
)
347 islset1
= libisl
.isl_set_union(islset1
, islset2
)
348 return self
._fromislset
(islset1
, symbols
)
350 def __or__(self
, other
):
351 return self
.union(other
)
352 __or__
.__doc
__ = union
.__doc
__
354 def __add__(self
, other
):
355 return self
.union(other
)
356 __add__
.__doc
__ = union
.__doc
__
358 def difference(self
, other
):
360 Return the difference of two domains as a new domain.
362 symbols
= self
._xsymbols
([self
, other
])
363 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
364 islset2
= other
._toislset
(other
.polyhedra
, symbols
)
365 islset
= libisl
.isl_set_subtract(islset1
, islset2
)
366 return self
._fromislset
(islset
, symbols
)
368 def __sub__(self
, other
):
369 return self
.difference(other
)
370 __sub__
.__doc
__ = difference
.__doc
__
374 Return the lexicographic minimum of the elements in the domain.
376 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
377 islset
= libisl
.isl_set_lexmin(islset
)
378 return self
._fromislset
(islset
, self
.symbols
)
382 Return the lexicographic maximum of the elements in the domain.
384 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
385 islset
= libisl
.isl_set_lexmax(islset
)
386 return self
._fromislset
(islset
, self
.symbols
)
388 _RE_COORDINATE
= re
.compile(r
'\((?P<num>\-?\d+)\)(/(?P<den>\d+))?')
392 Return the vertices of the domain, as a list of rational instances of
395 from .polyhedra
import Polyhedron
396 if not self
.isbounded():
397 raise ValueError('domain must be bounded')
398 islbset
= self
._toislbasicset
(self
.equalities
, self
.inequalities
,
400 vertices
= libisl
.isl_basic_set_compute_vertices(islbset
);
401 vertices
= islhelper
.isl_vertices_vertices(vertices
)
403 for vertex
in vertices
:
404 expr
= libisl
.isl_vertex_get_expr(vertex
)
406 if islhelper
.isl_version
< '0.13':
407 constraints
= islhelper
.isl_basic_set_constraints(expr
)
408 for constraint
in constraints
:
409 constant
= libisl
.isl_constraint_get_constant_val(constraint
)
410 constant
= islhelper
.isl_val_to_int(constant
)
411 for index
, symbol
in enumerate(self
.symbols
):
412 coefficient
= libisl
.isl_constraint_get_coefficient_val(constraint
,
413 libisl
.isl_dim_set
, index
)
414 coefficient
= islhelper
.isl_val_to_int(coefficient
)
416 coordinate
= -Fraction(constant
, coefficient
)
417 coordinates
.append((symbol
, coordinate
))
419 string
= islhelper
.isl_multi_aff_to_str(expr
)
420 matches
= self
._RE
_COORDINATE
.finditer(string
)
421 for symbol
, match
in zip(self
.symbols
, matches
):
422 numerator
= int(match
.group('num'))
423 denominator
= match
.group('den')
424 denominator
= 1 if denominator
is None else int(denominator
)
425 coordinate
= Fraction(numerator
, denominator
)
426 coordinates
.append((symbol
, coordinate
))
427 points
.append(Point(coordinates
))
432 Return the integer points of a bounded domain, as a list of integer
433 instances of Point. If the domain is not bounded, a ValueError exception
436 if not self
.isbounded():
437 raise ValueError('domain must be bounded')
438 from .polyhedra
import Universe
, Eq
439 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
440 islpoints
= islhelper
.isl_set_points(islset
)
442 for islpoint
in islpoints
:
444 for index
, symbol
in enumerate(self
.symbols
):
445 coordinate
= libisl
.isl_point_get_coordinate_val(islpoint
,
446 libisl
.isl_dim_set
, index
)
447 coordinate
= islhelper
.isl_val_to_int(coordinate
)
448 coordinates
[symbol
] = coordinate
449 points
.append(Point(coordinates
))
452 def __contains__(self
, point
):
454 Return True if the point if contained within the domain.
456 for polyhedron
in self
.polyhedra
:
457 if point
in polyhedron
:
462 def _polygon_inner_point(cls
, points
):
463 symbols
= points
[0].symbols
464 coordinates
= {symbol
: 0 for symbol
in symbols
}
466 for symbol
, coordinate
in point
.coordinates():
467 coordinates
[symbol
] += coordinate
468 for symbol
in symbols
:
469 coordinates
[symbol
] /= len(points
)
470 return Point(coordinates
)
473 def _sort_polygon_2d(cls
, points
):
476 o
= cls
._polygon
_inner
_point
(points
)
480 dx
, dy
= (coordinate
for symbol
, coordinate
in om
.coordinates())
481 angle
= math
.atan2(dy
, dx
)
483 return sorted(points
, key
=angles
.get
)
486 def _sort_polygon_3d(cls
, points
):
489 o
= cls
._polygon
_inner
_point
(points
)
500 raise ValueError('degenerate polygon')
504 normprod
= norm_oa
* om
.norm()
505 cosinus
= max(oa
.dot(om
) / normprod
, -1.)
506 sinus
= u
.dot(oa
.cross(om
)) / normprod
507 angle
= math
.acos(cosinus
)
508 angle
= math
.copysign(angle
, sinus
)
510 return sorted(points
, key
=angles
.get
)
514 Return the list of faces of a bounded domain. Each face is represented
515 by a list of vertices, in the form of rational instances of Point. If
516 the domain is not bounded, a ValueError exception is raised.
519 for polyhedron
in self
.polyhedra
:
520 vertices
= polyhedron
.vertices()
521 for constraint
in polyhedron
.constraints
:
523 for vertex
in vertices
:
524 if constraint
.subs(vertex
.coordinates()) == 0:
530 def _plot_2d(self
, plot
=None, **kwargs
):
531 import matplotlib
.pyplot
as plt
532 from matplotlib
.patches
import Polygon
535 plot
= fig
.add_subplot(1, 1, 1)
536 xmin
, xmax
= plot
.get_xlim()
537 ymin
, ymax
= plot
.get_ylim()
538 for polyhedron
in self
.polyhedra
:
539 vertices
= polyhedron
._sort
_polygon
_2d
(polyhedron
.vertices())
540 xys
= [tuple(vertex
.values()) for vertex
in vertices
]
542 xmin
, xmax
= min(xmin
, float(min(xs
))), max(xmax
, float(max(xs
)))
543 ymin
, ymax
= min(ymin
, float(min(ys
))), max(ymax
, float(max(ys
)))
544 plot
.add_patch(Polygon(xys
, closed
=True, **kwargs
))
545 plot
.set_xlim(xmin
, xmax
)
546 plot
.set_ylim(ymin
, ymax
)
549 def _plot_3d(self
, plot
=None, **kwargs
):
550 import matplotlib
.pyplot
as plt
551 from mpl_toolkits
.mplot3d
import Axes3D
552 from mpl_toolkits
.mplot3d
.art3d
import Poly3DCollection
558 xmin
, xmax
= axes
.get_xlim()
559 ymin
, ymax
= axes
.get_ylim()
560 zmin
, zmax
= axes
.get_zlim()
562 for vertices
in self
.faces():
563 vertices
= self
._sort
_polygon
_3d
(vertices
)
564 vertices
.append(vertices
[0])
565 face_xyzs
= [tuple(vertex
.values()) for vertex
in vertices
]
566 xs
, ys
, zs
= zip(*face_xyzs
)
567 xmin
, xmax
= min(xmin
, float(min(xs
))), max(xmax
, float(max(xs
)))
568 ymin
, ymax
= min(ymin
, float(min(ys
))), max(ymax
, float(max(ys
)))
569 zmin
, zmax
= min(zmin
, float(min(zs
))), max(zmax
, float(max(zs
)))
570 poly_xyzs
.append(face_xyzs
)
571 collection
= Poly3DCollection(poly_xyzs
, **kwargs
)
572 axes
.add_collection3d(collection
)
573 axes
.set_xlim(xmin
, xmax
)
574 axes
.set_ylim(ymin
, ymax
)
575 axes
.set_zlim(zmin
, zmax
)
578 def plot(self
, plot
=None, **kwargs
):
580 Plot a 2D or 3D domain using matplotlib. Draw it to the current plot
581 object if present, otherwise create a new one. options are keyword
582 arguments passed to the matplotlib drawing functions, they can be used
583 to set the drawing color for example. Raise ValueError is the domain is
586 if not self
.isbounded():
587 raise ValueError('domain must be bounded')
588 elif self
.dimension
== 2:
589 return self
._plot
_2d
(plot
=plot
, **kwargs
)
590 elif self
.dimension
== 3:
591 return self
._plot
_3d
(plot
=plot
, **kwargs
)
593 raise ValueError('polyhedron must be 2 or 3-dimensional')
595 def subs(self
, symbol
, expression
=None):
597 Substitute the given symbol by an expression in the domain constraints.
598 To perform multiple substitutions at once, pass a sequence or a
599 dictionary of (old, new) pairs to subs. The syntax of this function is
600 similar to LinExpr.subs().
602 polyhedra
= [polyhedron
.subs(symbol
, expression
)
603 for polyhedron
in self
.polyhedra
]
604 return Domain(*polyhedra
)
607 def _fromislset(cls
, islset
, symbols
):
608 from .polyhedra
import Polyhedron
609 islset
= libisl
.isl_set_remove_divs(islset
)
610 islbsets
= islhelper
.isl_set_basic_sets(islset
)
611 libisl
.isl_set_free(islset
)
613 for islbset
in islbsets
:
614 polyhedron
= Polyhedron
._fromislbasicset
(islbset
, symbols
)
615 polyhedra
.append(polyhedron
)
616 if len(polyhedra
) == 0:
617 from .polyhedra
import Empty
619 elif len(polyhedra
) == 1:
622 self
= object().__new
__(Domain
)
623 self
._polyhedra
= tuple(polyhedra
)
624 self
._symbols
= cls
._xsymbols
(polyhedra
)
625 self
._dimension
= len(self
._symbols
)
629 def _toislset(cls
, polyhedra
, symbols
):
630 polyhedron
= polyhedra
[0]
631 islbset
= polyhedron
._toislbasicset
(polyhedron
.equalities
,
632 polyhedron
.inequalities
, symbols
)
633 islset1
= libisl
.isl_set_from_basic_set(islbset
)
634 for polyhedron
in polyhedra
[1:]:
635 islbset
= polyhedron
._toislbasicset
(polyhedron
.equalities
,
636 polyhedron
.inequalities
, symbols
)
637 islset2
= libisl
.isl_set_from_basic_set(islbset
)
638 islset1
= libisl
.isl_set_union(islset1
, islset2
)
642 def _fromast(cls
, node
):
643 from .polyhedra
import Polyhedron
644 if isinstance(node
, ast
.Module
) and len(node
.body
) == 1:
645 return cls
._fromast
(node
.body
[0])
646 elif isinstance(node
, ast
.Expr
):
647 return cls
._fromast
(node
.value
)
648 elif isinstance(node
, ast
.UnaryOp
):
649 domain
= cls
._fromast
(node
.operand
)
650 if isinstance(node
.operand
, ast
.invert
):
652 elif isinstance(node
, ast
.BinOp
):
653 domain1
= cls
._fromast
(node
.left
)
654 domain2
= cls
._fromast
(node
.right
)
655 if isinstance(node
.op
, ast
.BitAnd
):
656 return And(domain1
, domain2
)
657 elif isinstance(node
.op
, ast
.BitOr
):
658 return Or(domain1
, domain2
)
659 elif isinstance(node
, ast
.Compare
):
662 left
= LinExpr
._fromast
(node
.left
)
663 for i
in range(len(node
.ops
)):
665 right
= LinExpr
._fromast
(node
.comparators
[i
])
666 if isinstance(op
, ast
.Lt
):
667 inequalities
.append(right
- left
- 1)
668 elif isinstance(op
, ast
.LtE
):
669 inequalities
.append(right
- left
)
670 elif isinstance(op
, ast
.Eq
):
671 equalities
.append(left
- right
)
672 elif isinstance(op
, ast
.GtE
):
673 inequalities
.append(left
- right
)
674 elif isinstance(op
, ast
.Gt
):
675 inequalities
.append(left
- right
- 1)
680 return Polyhedron(equalities
, inequalities
)
681 raise SyntaxError('invalid syntax')
683 _RE_BRACES
= re
.compile(r
'^\{\s*|\s*\}$')
684 _RE_EQ
= re
.compile(r
'([^<=>])=([^<=>])')
685 _RE_AND
= re
.compile(r
'\band\b|,|&&|/\\|∧|∩')
686 _RE_OR
= re
.compile(r
'\bor\b|;|\|\||\\/|∨|∪')
687 _RE_NOT
= re
.compile(r
'\bnot\b|!|¬')
688 _RE_NUM_VAR
= LinExpr
._RE
_NUM
_VAR
689 _RE_OPERATORS
= re
.compile(r
'(&|\||~)')
692 def fromstring(cls
, string
):
694 Create a domain from a string. Raise SyntaxError if the string is not
697 # remove curly brackets
698 string
= cls
._RE
_BRACES
.sub(r
'', string
)
699 # replace '=' by '=='
700 string
= cls
._RE
_EQ
.sub(r
'\1==\2', string
)
701 # replace 'and', 'or', 'not'
702 string
= cls
._RE
_AND
.sub(r
' & ', string
)
703 string
= cls
._RE
_OR
.sub(r
' | ', string
)
704 string
= cls
._RE
_NOT
.sub(r
' ~', string
)
705 # add implicit multiplication operators, e.g. '5x' -> '5*x'
706 string
= cls
._RE
_NUM
_VAR
.sub(r
'\1*\2', string
)
707 # add parentheses to force precedence
708 tokens
= cls
._RE
_OPERATORS
.split(string
)
709 for i
, token
in enumerate(tokens
):
711 token
= '({})'.format(token
)
713 string
= ''.join(tokens
)
714 tree
= ast
.parse(string
, 'eval')
715 return cls
._fromast
(tree
)
718 assert len(self
.polyhedra
) >= 2
719 strings
= [repr(polyhedron
) for polyhedron
in self
.polyhedra
]
720 return 'Or({})'.format(', '.join(strings
))
722 def _repr_latex_(self
):
724 for polyhedron
in self
.polyhedra
:
725 strings
.append('({})'.format(polyhedron
._repr
_latex
_().strip('$')))
726 return '${}$'.format(' \\vee '.join(strings
))
729 def fromsympy(cls
, expr
):
731 Create a domain from a sympy expression.
734 from .polyhedra
import Lt
, Le
, Eq
, Ne
, Ge
, Gt
736 sympy
.And
: And
, sympy
.Or
: Or
, sympy
.Not
: Not
,
737 sympy
.Lt
: Lt
, sympy
.Le
: Le
,
738 sympy
.Eq
: Eq
, sympy
.Ne
: Ne
,
739 sympy
.Ge
: Ge
, sympy
.Gt
: Gt
,
741 if expr
.func
in funcmap
:
742 args
= [Domain
.fromsympy(arg
) for arg
in expr
.args
]
743 return funcmap
[expr
.func
](*args
)
744 elif isinstance(expr
, sympy
.Expr
):
745 return LinExpr
.fromsympy(expr
)
746 raise ValueError('non-domain expression: {!r}'.format(expr
))
750 Convert the domain to a sympy expression.
753 polyhedra
= [polyhedron
.tosympy() for polyhedron
in polyhedra
]
754 return sympy
.Or(*polyhedra
)
759 Create the intersection domain of the domains given in arguments.
761 if len(domains
) == 0:
762 from .polyhedra
import Universe
765 return domains
[0].intersection(*domains
[1:])
766 And
.__doc
__ = Domain
.intersection
.__doc
__
770 Create the union domain of the domains given in arguments.
772 if len(domains
) == 0:
773 from .polyhedra
import Empty
776 return domains
[0].union(*domains
[1:])
777 Or
.__doc
__ = Domain
.union
.__doc
__
781 Create the complementary domain of the domain given in argument.
784 Not
.__doc
__ = Domain
.complement
.__doc
__