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.
328 def __and__(self
, other
):
329 if isinstance(other
, Domain
):
330 symbols
= self
._xsymbols
([self
, other
])
331 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
332 islset2
= other
._toislset
(other
.polyhedra
, symbols
)
333 islset
= libisl
.isl_set_intersect(islset1
, islset2
)
334 return self
._fromislset
(islset
, symbols
)
335 return NotImplemented
336 __and__
.__doc
__ = intersection
.__doc
__
338 def union(self
, *others
):
340 Return the union of two or more domains as a new domain. As an
341 alternative, function Or() can be used.
345 symbols
= self
._xsymbols
((self
,) + others
)
346 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
348 islset2
= other
._toislset
(other
.polyhedra
, symbols
)
349 islset1
= libisl
.isl_set_union(islset1
, islset2
)
350 return self
._fromislset
(islset1
, symbols
)
352 def __or__(self
, other
):
353 return self
.union(other
)
354 __or__
.__doc
__ = union
.__doc
__
356 def __add__(self
, other
):
357 return self
.union(other
)
358 __add__
.__doc
__ = union
.__doc
__
360 def difference(self
, other
):
362 Return the difference of two domains as a new domain.
364 symbols
= self
._xsymbols
([self
, other
])
365 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
366 islset2
= other
._toislset
(other
.polyhedra
, symbols
)
367 islset
= libisl
.isl_set_subtract(islset1
, islset2
)
368 return self
._fromislset
(islset
, symbols
)
370 def __sub__(self
, other
):
371 return self
.difference(other
)
372 __sub__
.__doc
__ = difference
.__doc
__
376 Return the lexicographic minimum of the elements in the domain.
378 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
379 islset
= libisl
.isl_set_lexmin(islset
)
380 return self
._fromislset
(islset
, self
.symbols
)
384 Return the lexicographic maximum of the elements in the domain.
386 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
387 islset
= libisl
.isl_set_lexmax(islset
)
388 return self
._fromislset
(islset
, self
.symbols
)
390 _RE_COORDINATE
= re
.compile(r
'\((?P<num>\-?\d+)\)(/(?P<den>\d+))?')
394 Return the vertices of the domain, as a list of rational instances of
397 from .polyhedra
import Polyhedron
398 if not self
.isbounded():
399 raise ValueError('domain must be bounded')
400 islbset
= self
._toislbasicset
(self
.equalities
, self
.inequalities
,
402 vertices
= libisl
.isl_basic_set_compute_vertices(islbset
);
403 vertices
= islhelper
.isl_vertices_vertices(vertices
)
405 for vertex
in vertices
:
406 expr
= libisl
.isl_vertex_get_expr(vertex
)
408 if islhelper
.isl_version
< '0.13':
409 constraints
= islhelper
.isl_basic_set_constraints(expr
)
410 for constraint
in constraints
:
411 constant
= libisl
.isl_constraint_get_constant_val(constraint
)
412 constant
= islhelper
.isl_val_to_int(constant
)
413 for index
, symbol
in enumerate(self
.symbols
):
414 coefficient
= libisl
.isl_constraint_get_coefficient_val(constraint
,
415 libisl
.isl_dim_set
, index
)
416 coefficient
= islhelper
.isl_val_to_int(coefficient
)
418 coordinate
= -Fraction(constant
, coefficient
)
419 coordinates
.append((symbol
, coordinate
))
421 string
= islhelper
.isl_multi_aff_to_str(expr
)
422 matches
= self
._RE
_COORDINATE
.finditer(string
)
423 for symbol
, match
in zip(self
.symbols
, matches
):
424 numerator
= int(match
.group('num'))
425 denominator
= match
.group('den')
426 denominator
= 1 if denominator
is None else int(denominator
)
427 coordinate
= Fraction(numerator
, denominator
)
428 coordinates
.append((symbol
, coordinate
))
429 points
.append(Point(coordinates
))
434 Return the integer points of a bounded domain, as a list of integer
435 instances of Point. If the domain is not bounded, a ValueError exception
438 if not self
.isbounded():
439 raise ValueError('domain must be bounded')
440 from .polyhedra
import Universe
, Eq
441 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
442 islpoints
= islhelper
.isl_set_points(islset
)
444 for islpoint
in islpoints
:
446 for index
, symbol
in enumerate(self
.symbols
):
447 coordinate
= libisl
.isl_point_get_coordinate_val(islpoint
,
448 libisl
.isl_dim_set
, index
)
449 coordinate
= islhelper
.isl_val_to_int(coordinate
)
450 coordinates
[symbol
] = coordinate
451 points
.append(Point(coordinates
))
454 def __contains__(self
, point
):
456 Return True if the point if contained within the domain.
458 for polyhedron
in self
.polyhedra
:
459 if point
in polyhedron
:
464 def _polygon_inner_point(cls
, points
):
465 symbols
= points
[0].symbols
466 coordinates
= {symbol
: 0 for symbol
in symbols
}
468 for symbol
, coordinate
in point
.coordinates():
469 coordinates
[symbol
] += coordinate
470 for symbol
in symbols
:
471 coordinates
[symbol
] /= len(points
)
472 return Point(coordinates
)
475 def _sort_polygon_2d(cls
, points
):
478 o
= cls
._polygon
_inner
_point
(points
)
482 dx
, dy
= (coordinate
for symbol
, coordinate
in om
.coordinates())
483 angle
= math
.atan2(dy
, dx
)
485 return sorted(points
, key
=angles
.get
)
488 def _sort_polygon_3d(cls
, points
):
491 o
= cls
._polygon
_inner
_point
(points
)
502 raise ValueError('degenerate polygon')
506 normprod
= norm_oa
* om
.norm()
507 cosinus
= max(oa
.dot(om
) / normprod
, -1.)
508 sinus
= u
.dot(oa
.cross(om
)) / normprod
509 angle
= math
.acos(cosinus
)
510 angle
= math
.copysign(angle
, sinus
)
512 return sorted(points
, key
=angles
.get
)
516 Return the list of faces of a bounded domain. Each face is represented
517 by a list of vertices, in the form of rational instances of Point. If
518 the domain is not bounded, a ValueError exception is raised.
521 for polyhedron
in self
.polyhedra
:
522 vertices
= polyhedron
.vertices()
523 for constraint
in polyhedron
.constraints
:
525 for vertex
in vertices
:
526 if constraint
.subs(vertex
.coordinates()) == 0:
532 def _plot_2d(self
, plot
=None, **kwargs
):
533 import matplotlib
.pyplot
as plt
534 from matplotlib
.patches
import Polygon
537 plot
= fig
.add_subplot(1, 1, 1)
538 xmin
, xmax
= plot
.get_xlim()
539 ymin
, ymax
= plot
.get_ylim()
540 for polyhedron
in self
.polyhedra
:
541 vertices
= polyhedron
._sort
_polygon
_2d
(polyhedron
.vertices())
542 xys
= [tuple(vertex
.values()) for vertex
in vertices
]
544 xmin
, xmax
= min(xmin
, float(min(xs
))), max(xmax
, float(max(xs
)))
545 ymin
, ymax
= min(ymin
, float(min(ys
))), max(ymax
, float(max(ys
)))
546 plot
.add_patch(Polygon(xys
, closed
=True, **kwargs
))
547 plot
.set_xlim(xmin
, xmax
)
548 plot
.set_ylim(ymin
, ymax
)
551 def _plot_3d(self
, plot
=None, **kwargs
):
552 import matplotlib
.pyplot
as plt
553 from mpl_toolkits
.mplot3d
import Axes3D
554 from mpl_toolkits
.mplot3d
.art3d
import Poly3DCollection
560 xmin
, xmax
= axes
.get_xlim()
561 ymin
, ymax
= axes
.get_ylim()
562 zmin
, zmax
= axes
.get_zlim()
564 for vertices
in self
.faces():
565 vertices
= self
._sort
_polygon
_3d
(vertices
)
566 vertices
.append(vertices
[0])
567 face_xyzs
= [tuple(vertex
.values()) for vertex
in vertices
]
568 xs
, ys
, zs
= zip(*face_xyzs
)
569 xmin
, xmax
= min(xmin
, float(min(xs
))), max(xmax
, float(max(xs
)))
570 ymin
, ymax
= min(ymin
, float(min(ys
))), max(ymax
, float(max(ys
)))
571 zmin
, zmax
= min(zmin
, float(min(zs
))), max(zmax
, float(max(zs
)))
572 poly_xyzs
.append(face_xyzs
)
573 collection
= Poly3DCollection(poly_xyzs
, **kwargs
)
574 axes
.add_collection3d(collection
)
575 axes
.set_xlim(xmin
, xmax
)
576 axes
.set_ylim(ymin
, ymax
)
577 axes
.set_zlim(zmin
, zmax
)
580 def plot(self
, plot
=None, **kwargs
):
582 Plot a 2D or 3D domain using matplotlib. Draw it to the current plot
583 object if present, otherwise create a new one. options are keyword
584 arguments passed to the matplotlib drawing functions, they can be used
585 to set the drawing color for example. Raise ValueError is the domain is
588 if not self
.isbounded():
589 raise ValueError('domain must be bounded')
590 elif self
.dimension
== 2:
591 return self
._plot
_2d
(plot
=plot
, **kwargs
)
592 elif self
.dimension
== 3:
593 return self
._plot
_3d
(plot
=plot
, **kwargs
)
595 raise ValueError('polyhedron must be 2 or 3-dimensional')
597 def subs(self
, symbol
, expression
=None):
599 Substitute the given symbol by an expression in the domain constraints.
600 To perform multiple substitutions at once, pass a sequence or a
601 dictionary of (old, new) pairs to subs. The syntax of this function is
602 similar to LinExpr.subs().
604 polyhedra
= [polyhedron
.subs(symbol
, expression
)
605 for polyhedron
in self
.polyhedra
]
606 return Domain(*polyhedra
)
609 def _fromislset(cls
, islset
, symbols
):
610 from .polyhedra
import Polyhedron
611 islset
= libisl
.isl_set_remove_divs(islset
)
612 islbsets
= islhelper
.isl_set_basic_sets(islset
)
613 libisl
.isl_set_free(islset
)
615 for islbset
in islbsets
:
616 polyhedron
= Polyhedron
._fromislbasicset
(islbset
, symbols
)
617 polyhedra
.append(polyhedron
)
618 if len(polyhedra
) == 0:
619 from .polyhedra
import Empty
621 elif len(polyhedra
) == 1:
624 self
= object().__new
__(Domain
)
625 self
._polyhedra
= tuple(polyhedra
)
626 self
._symbols
= cls
._xsymbols
(polyhedra
)
627 self
._dimension
= len(self
._symbols
)
631 def _toislset(cls
, polyhedra
, symbols
):
632 polyhedron
= polyhedra
[0]
633 islbset
= polyhedron
._toislbasicset
(polyhedron
.equalities
,
634 polyhedron
.inequalities
, symbols
)
635 islset1
= libisl
.isl_set_from_basic_set(islbset
)
636 for polyhedron
in polyhedra
[1:]:
637 islbset
= polyhedron
._toislbasicset
(polyhedron
.equalities
,
638 polyhedron
.inequalities
, symbols
)
639 islset2
= libisl
.isl_set_from_basic_set(islbset
)
640 islset1
= libisl
.isl_set_union(islset1
, islset2
)
644 def _fromast(cls
, node
):
645 from .polyhedra
import Polyhedron
646 if isinstance(node
, ast
.Module
) and len(node
.body
) == 1:
647 return cls
._fromast
(node
.body
[0])
648 elif isinstance(node
, ast
.Expr
):
649 return cls
._fromast
(node
.value
)
650 elif isinstance(node
, ast
.UnaryOp
):
651 domain
= cls
._fromast
(node
.operand
)
652 if isinstance(node
.operand
, ast
.invert
):
654 elif isinstance(node
, ast
.BinOp
):
655 domain1
= cls
._fromast
(node
.left
)
656 domain2
= cls
._fromast
(node
.right
)
657 if isinstance(node
.op
, ast
.BitAnd
):
658 return And(domain1
, domain2
)
659 elif isinstance(node
.op
, ast
.BitOr
):
660 return Or(domain1
, domain2
)
661 elif isinstance(node
, ast
.Compare
):
664 left
= LinExpr
._fromast
(node
.left
)
665 for i
in range(len(node
.ops
)):
667 right
= LinExpr
._fromast
(node
.comparators
[i
])
668 if isinstance(op
, ast
.Lt
):
669 inequalities
.append(right
- left
- 1)
670 elif isinstance(op
, ast
.LtE
):
671 inequalities
.append(right
- left
)
672 elif isinstance(op
, ast
.Eq
):
673 equalities
.append(left
- right
)
674 elif isinstance(op
, ast
.GtE
):
675 inequalities
.append(left
- right
)
676 elif isinstance(op
, ast
.Gt
):
677 inequalities
.append(left
- right
- 1)
682 return Polyhedron(equalities
, inequalities
)
683 raise SyntaxError('invalid syntax')
685 _RE_BRACES
= re
.compile(r
'^\{\s*|\s*\}$')
686 _RE_EQ
= re
.compile(r
'([^<=>])=([^<=>])')
687 _RE_AND
= re
.compile(r
'\band\b|,|&&|/\\|∧|∩')
688 _RE_OR
= re
.compile(r
'\bor\b|;|\|\||\\/|∨|∪')
689 _RE_NOT
= re
.compile(r
'\bnot\b|!|¬')
690 _RE_NUM_VAR
= LinExpr
._RE
_NUM
_VAR
691 _RE_OPERATORS
= re
.compile(r
'(&|\||~)')
694 def fromstring(cls
, string
):
696 Create a domain from a string. Raise SyntaxError if the string is not
699 # remove curly brackets
700 string
= cls
._RE
_BRACES
.sub(r
'', string
)
701 # replace '=' by '=='
702 string
= cls
._RE
_EQ
.sub(r
'\1==\2', string
)
703 # replace 'and', 'or', 'not'
704 string
= cls
._RE
_AND
.sub(r
' & ', string
)
705 string
= cls
._RE
_OR
.sub(r
' | ', string
)
706 string
= cls
._RE
_NOT
.sub(r
' ~', string
)
707 # add implicit multiplication operators, e.g. '5x' -> '5*x'
708 string
= cls
._RE
_NUM
_VAR
.sub(r
'\1*\2', string
)
709 # add parentheses to force precedence
710 tokens
= cls
._RE
_OPERATORS
.split(string
)
711 for i
, token
in enumerate(tokens
):
713 token
= '({})'.format(token
)
715 string
= ''.join(tokens
)
716 tree
= ast
.parse(string
, 'eval')
717 return cls
._fromast
(tree
)
720 assert len(self
.polyhedra
) >= 2
721 strings
= [repr(polyhedron
) for polyhedron
in self
.polyhedra
]
722 return 'Or({})'.format(', '.join(strings
))
724 def _repr_latex_(self
):
726 for polyhedron
in self
.polyhedra
:
727 strings
.append('({})'.format(polyhedron
._repr
_latex
_().strip('$')))
728 return '${}$'.format(' \\vee '.join(strings
))
731 def fromsympy(cls
, expr
):
733 Create a domain from a sympy expression.
736 from .polyhedra
import Lt
, Le
, Eq
, Ne
, Ge
, Gt
738 sympy
.And
: And
, sympy
.Or
: Or
, sympy
.Not
: Not
,
739 sympy
.Lt
: Lt
, sympy
.Le
: Le
,
740 sympy
.Eq
: Eq
, sympy
.Ne
: Ne
,
741 sympy
.Ge
: Ge
, sympy
.Gt
: Gt
,
743 if expr
.func
in funcmap
:
744 args
= [Domain
.fromsympy(arg
) for arg
in expr
.args
]
745 return funcmap
[expr
.func
](*args
)
746 elif isinstance(expr
, sympy
.Expr
):
747 return LinExpr
.fromsympy(expr
)
748 raise ValueError('non-domain expression: {!r}'.format(expr
))
752 Convert the domain to a sympy expression.
755 polyhedra
= [polyhedron
.tosympy() for polyhedron
in polyhedra
]
756 return sympy
.Or(*polyhedra
)
761 Create the intersection domain of the domains given in arguments.
763 if len(domains
) == 0:
764 from .polyhedra
import Universe
767 return domains
[0].intersection(*domains
[1:])
768 And
.__doc
__ = Domain
.intersection
.__doc
__
772 Create the union domain of the domains given in arguments.
774 if len(domains
) == 0:
775 from .polyhedra
import Empty
778 return domains
[0].union(*domains
[1:])
779 Or
.__doc
__ = Domain
.union
.__doc
__
783 Create the complementary domain of the domain given in argument.
786 Not
.__doc
__ = Domain
.complement
.__doc
__