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 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
283 for index
, symbol
in reversed(list(enumerate(self
.symbols
))):
284 if symbol
in symbols
:
287 islset
= libisl
.isl_set_project_out(islset
,
288 libisl
.isl_dim_set
, index
+ 1, n
)
291 islset
= libisl
.isl_set_project_out(islset
, libisl
.isl_dim_set
, 0, n
)
292 symbols
= [symbol
for symbol
in self
.symbols
if symbol
not in symbols
]
293 return Domain
._fromislset
(islset
, symbols
)
297 Return a sample of the domain, as an integer instance of Point. If the
298 domain is empty, a ValueError exception is raised.
300 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
301 islpoint
= libisl
.isl_set_sample_point(islset
)
302 if bool(libisl
.isl_point_is_void(islpoint
)):
303 libisl
.isl_point_free(islpoint
)
304 raise ValueError('domain must be non-empty')
306 for index
, symbol
in enumerate(self
.symbols
):
307 coordinate
= libisl
.isl_point_get_coordinate_val(islpoint
,
308 libisl
.isl_dim_set
, index
)
309 coordinate
= islhelper
.isl_val_to_int(coordinate
)
310 point
[symbol
] = coordinate
311 libisl
.isl_point_free(islpoint
)
314 def intersection(self
, *others
):
316 Return the intersection of two or more domains as a new domain. As an
317 alternative, function And() can be used.
321 symbols
= self
._xsymbols
((self
,) + others
)
322 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
324 islset2
= other
._toislset
(other
.polyhedra
, symbols
)
325 islset1
= libisl
.isl_set_intersect(islset1
, islset2
)
326 return self
._fromislset
(islset1
, symbols
)
328 def __and__(self
, other
):
329 return self
.intersection(other
)
330 __and__
.__doc
__ = intersection
.__doc
__
332 def union(self
, *others
):
334 Return the union of two or more domains as a new domain. As an
335 alternative, function Or() can be used.
339 symbols
= self
._xsymbols
((self
,) + others
)
340 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
342 islset2
= other
._toislset
(other
.polyhedra
, symbols
)
343 islset1
= libisl
.isl_set_union(islset1
, islset2
)
344 return self
._fromislset
(islset1
, symbols
)
346 def __or__(self
, other
):
347 return self
.union(other
)
348 __or__
.__doc
__ = union
.__doc
__
350 def __add__(self
, other
):
351 return self
.union(other
)
352 __add__
.__doc
__ = union
.__doc
__
354 def difference(self
, other
):
356 Return the difference of two domains as a new domain.
358 symbols
= self
._xsymbols
([self
, other
])
359 islset1
= self
._toislset
(self
.polyhedra
, symbols
)
360 islset2
= other
._toislset
(other
.polyhedra
, symbols
)
361 islset
= libisl
.isl_set_subtract(islset1
, islset2
)
362 return self
._fromislset
(islset
, symbols
)
364 def __sub__(self
, other
):
365 return self
.difference(other
)
366 __sub__
.__doc
__ = difference
.__doc
__
370 Return the lexicographic minimum of the elements in the domain.
372 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
373 islset
= libisl
.isl_set_lexmin(islset
)
374 return self
._fromislset
(islset
, self
.symbols
)
378 Return the lexicographic maximum of the elements in the domain.
380 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
381 islset
= libisl
.isl_set_lexmax(islset
)
382 return self
._fromislset
(islset
, self
.symbols
)
384 _RE_COORDINATE
= re
.compile(r
'\((?P<num>\-?\d+)\)(/(?P<den>\d+))?')
388 Return the vertices of the domain, as a list of rational instances of
391 from .polyhedra
import Polyhedron
392 if not self
.isbounded():
393 raise ValueError('domain must be bounded')
394 islbset
= self
._toislbasicset
(self
.equalities
, self
.inequalities
,
396 vertices
= libisl
.isl_basic_set_compute_vertices(islbset
);
397 vertices
= islhelper
.isl_vertices_vertices(vertices
)
399 for vertex
in vertices
:
400 expr
= libisl
.isl_vertex_get_expr(vertex
)
402 if islhelper
.isl_version
< '0.13':
403 constraints
= islhelper
.isl_basic_set_constraints(expr
)
404 for constraint
in constraints
:
405 constant
= libisl
.isl_constraint_get_constant_val(constraint
)
406 constant
= islhelper
.isl_val_to_int(constant
)
407 for index
, symbol
in enumerate(self
.symbols
):
408 coefficient
= libisl
.isl_constraint_get_coefficient_val(constraint
,
409 libisl
.isl_dim_set
, index
)
410 coefficient
= islhelper
.isl_val_to_int(coefficient
)
412 coordinate
= -Fraction(constant
, coefficient
)
413 coordinates
.append((symbol
, coordinate
))
415 string
= islhelper
.isl_multi_aff_to_str(expr
)
416 matches
= self
._RE
_COORDINATE
.finditer(string
)
417 for symbol
, match
in zip(self
.symbols
, matches
):
418 numerator
= int(match
.group('num'))
419 denominator
= match
.group('den')
420 denominator
= 1 if denominator
is None else int(denominator
)
421 coordinate
= Fraction(numerator
, denominator
)
422 coordinates
.append((symbol
, coordinate
))
423 points
.append(Point(coordinates
))
428 Return the integer points of a bounded domain, as a list of integer
429 instances of Point. If the domain is not bounded, a ValueError exception
432 if not self
.isbounded():
433 raise ValueError('domain must be bounded')
434 from .polyhedra
import Universe
, Eq
435 islset
= self
._toislset
(self
.polyhedra
, self
.symbols
)
436 islpoints
= islhelper
.isl_set_points(islset
)
438 for islpoint
in islpoints
:
440 for index
, symbol
in enumerate(self
.symbols
):
441 coordinate
= libisl
.isl_point_get_coordinate_val(islpoint
,
442 libisl
.isl_dim_set
, index
)
443 coordinate
= islhelper
.isl_val_to_int(coordinate
)
444 coordinates
[symbol
] = coordinate
445 points
.append(Point(coordinates
))
448 def __contains__(self
, point
):
450 Return True if the point if contained within the domain.
452 for polyhedron
in self
.polyhedra
:
453 if point
in polyhedron
:
458 def _polygon_inner_point(cls
, points
):
459 symbols
= points
[0].symbols
460 coordinates
= {symbol
: 0 for symbol
in symbols
}
462 for symbol
, coordinate
in point
.coordinates():
463 coordinates
[symbol
] += coordinate
464 for symbol
in symbols
:
465 coordinates
[symbol
] /= len(points
)
466 return Point(coordinates
)
469 def _sort_polygon_2d(cls
, points
):
472 o
= cls
._polygon
_inner
_point
(points
)
476 dx
, dy
= (coordinate
for symbol
, coordinate
in om
.coordinates())
477 angle
= math
.atan2(dy
, dx
)
479 return sorted(points
, key
=angles
.get
)
482 def _sort_polygon_3d(cls
, points
):
485 o
= cls
._polygon
_inner
_point
(points
)
496 raise ValueError('degenerate polygon')
500 normprod
= norm_oa
* om
.norm()
501 cosinus
= max(oa
.dot(om
) / normprod
, -1.)
502 sinus
= u
.dot(oa
.cross(om
)) / normprod
503 angle
= math
.acos(cosinus
)
504 angle
= math
.copysign(angle
, sinus
)
506 return sorted(points
, key
=angles
.get
)
510 Return the list of faces of a bounded domain. Each face is represented
511 by a list of vertices, in the form of rational instances of Point. If
512 the domain is not bounded, a ValueError exception is raised.
515 for polyhedron
in self
.polyhedra
:
516 vertices
= polyhedron
.vertices()
517 for constraint
in polyhedron
.constraints
:
519 for vertex
in vertices
:
520 if constraint
.subs(vertex
.coordinates()) == 0:
526 def _plot_2d(self
, plot
=None, **kwargs
):
527 import matplotlib
.pyplot
as plt
528 from matplotlib
.patches
import Polygon
531 plot
= fig
.add_subplot(1, 1, 1)
532 xmin
, xmax
= plot
.get_xlim()
533 ymin
, ymax
= plot
.get_ylim()
534 for polyhedron
in self
.polyhedra
:
535 vertices
= polyhedron
._sort
_polygon
_2d
(polyhedron
.vertices())
536 xys
= [tuple(vertex
.values()) for vertex
in vertices
]
538 xmin
, xmax
= min(xmin
, float(min(xs
))), max(xmax
, float(max(xs
)))
539 ymin
, ymax
= min(ymin
, float(min(ys
))), max(ymax
, float(max(ys
)))
540 plot
.add_patch(Polygon(xys
, closed
=True, **kwargs
))
541 plot
.set_xlim(xmin
, xmax
)
542 plot
.set_ylim(ymin
, ymax
)
545 def _plot_3d(self
, plot
=None, **kwargs
):
546 import matplotlib
.pyplot
as plt
547 from mpl_toolkits
.mplot3d
import Axes3D
548 from mpl_toolkits
.mplot3d
.art3d
import Poly3DCollection
554 xmin
, xmax
= axes
.get_xlim()
555 ymin
, ymax
= axes
.get_ylim()
556 zmin
, zmax
= axes
.get_zlim()
558 for vertices
in self
.faces():
559 vertices
= self
._sort
_polygon
_3d
(vertices
)
560 vertices
.append(vertices
[0])
561 face_xyzs
= [tuple(vertex
.values()) for vertex
in vertices
]
562 xs
, ys
, zs
= zip(*face_xyzs
)
563 xmin
, xmax
= min(xmin
, float(min(xs
))), max(xmax
, float(max(xs
)))
564 ymin
, ymax
= min(ymin
, float(min(ys
))), max(ymax
, float(max(ys
)))
565 zmin
, zmax
= min(zmin
, float(min(zs
))), max(zmax
, float(max(zs
)))
566 poly_xyzs
.append(face_xyzs
)
567 collection
= Poly3DCollection(poly_xyzs
, **kwargs
)
568 axes
.add_collection3d(collection
)
569 axes
.set_xlim(xmin
, xmax
)
570 axes
.set_ylim(ymin
, ymax
)
571 axes
.set_zlim(zmin
, zmax
)
574 def plot(self
, plot
=None, **kwargs
):
576 Plot a 2D or 3D domain using matplotlib. Draw it to the current plot
577 object if present, otherwise create a new one. options are keyword
578 arguments passed to the matplotlib drawing functions, they can be used
579 to set the drawing color for example. Raise ValueError is the domain is
582 if not self
.isbounded():
583 raise ValueError('domain must be bounded')
584 elif self
.dimension
== 2:
585 return self
._plot
_2d
(plot
=plot
, **kwargs
)
586 elif self
.dimension
== 3:
587 return self
._plot
_3d
(plot
=plot
, **kwargs
)
589 raise ValueError('polyhedron must be 2 or 3-dimensional')
591 def subs(self
, symbol
, expression
=None):
593 Substitute the given symbol by an expression in the domain constraints.
594 To perform multiple substitutions at once, pass a sequence or a
595 dictionary of (old, new) pairs to subs. The syntax of this function is
596 similar to LinExpr.subs().
598 polyhedra
= [polyhedron
.subs(symbol
, expression
)
599 for polyhedron
in self
.polyhedra
]
600 return Domain(*polyhedra
)
603 def _fromislset(cls
, islset
, symbols
):
604 from .polyhedra
import Polyhedron
605 islset
= libisl
.isl_set_remove_divs(islset
)
606 islbsets
= islhelper
.isl_set_basic_sets(islset
)
607 libisl
.isl_set_free(islset
)
609 for islbset
in islbsets
:
610 polyhedron
= Polyhedron
._fromislbasicset
(islbset
, symbols
)
611 polyhedra
.append(polyhedron
)
612 if len(polyhedra
) == 0:
613 from .polyhedra
import Empty
615 elif len(polyhedra
) == 1:
618 self
= object().__new
__(Domain
)
619 self
._polyhedra
= tuple(polyhedra
)
620 self
._symbols
= cls
._xsymbols
(polyhedra
)
621 self
._dimension
= len(self
._symbols
)
625 def _toislset(cls
, polyhedra
, symbols
):
626 polyhedron
= polyhedra
[0]
627 islbset
= polyhedron
._toislbasicset
(polyhedron
.equalities
,
628 polyhedron
.inequalities
, symbols
)
629 islset1
= libisl
.isl_set_from_basic_set(islbset
)
630 for polyhedron
in polyhedra
[1:]:
631 islbset
= polyhedron
._toislbasicset
(polyhedron
.equalities
,
632 polyhedron
.inequalities
, symbols
)
633 islset2
= libisl
.isl_set_from_basic_set(islbset
)
634 islset1
= libisl
.isl_set_union(islset1
, islset2
)
638 def _fromast(cls
, node
):
639 from .polyhedra
import Polyhedron
640 if isinstance(node
, ast
.Module
) and len(node
.body
) == 1:
641 return cls
._fromast
(node
.body
[0])
642 elif isinstance(node
, ast
.Expr
):
643 return cls
._fromast
(node
.value
)
644 elif isinstance(node
, ast
.UnaryOp
):
645 domain
= cls
._fromast
(node
.operand
)
646 if isinstance(node
.operand
, ast
.invert
):
648 elif isinstance(node
, ast
.BinOp
):
649 domain1
= cls
._fromast
(node
.left
)
650 domain2
= cls
._fromast
(node
.right
)
651 if isinstance(node
.op
, ast
.BitAnd
):
652 return And(domain1
, domain2
)
653 elif isinstance(node
.op
, ast
.BitOr
):
654 return Or(domain1
, domain2
)
655 elif isinstance(node
, ast
.Compare
):
658 left
= LinExpr
._fromast
(node
.left
)
659 for i
in range(len(node
.ops
)):
661 right
= LinExpr
._fromast
(node
.comparators
[i
])
662 if isinstance(op
, ast
.Lt
):
663 inequalities
.append(right
- left
- 1)
664 elif isinstance(op
, ast
.LtE
):
665 inequalities
.append(right
- left
)
666 elif isinstance(op
, ast
.Eq
):
667 equalities
.append(left
- right
)
668 elif isinstance(op
, ast
.GtE
):
669 inequalities
.append(left
- right
)
670 elif isinstance(op
, ast
.Gt
):
671 inequalities
.append(left
- right
- 1)
676 return Polyhedron(equalities
, inequalities
)
677 raise SyntaxError('invalid syntax')
679 _RE_BRACES
= re
.compile(r
'^\{\s*|\s*\}$')
680 _RE_EQ
= re
.compile(r
'([^<=>])=([^<=>])')
681 _RE_AND
= re
.compile(r
'\band\b|,|&&|/\\|∧|∩')
682 _RE_OR
= re
.compile(r
'\bor\b|;|\|\||\\/|∨|∪')
683 _RE_NOT
= re
.compile(r
'\bnot\b|!|¬')
684 _RE_NUM_VAR
= LinExpr
._RE
_NUM
_VAR
685 _RE_OPERATORS
= re
.compile(r
'(&|\||~)')
688 def fromstring(cls
, string
):
690 Create a domain from a string. Raise SyntaxError if the string is not
693 # remove curly brackets
694 string
= cls
._RE
_BRACES
.sub(r
'', string
)
695 # replace '=' by '=='
696 string
= cls
._RE
_EQ
.sub(r
'\1==\2', string
)
697 # replace 'and', 'or', 'not'
698 string
= cls
._RE
_AND
.sub(r
' & ', string
)
699 string
= cls
._RE
_OR
.sub(r
' | ', string
)
700 string
= cls
._RE
_NOT
.sub(r
' ~', string
)
701 # add implicit multiplication operators, e.g. '5x' -> '5*x'
702 string
= cls
._RE
_NUM
_VAR
.sub(r
'\1*\2', string
)
703 # add parentheses to force precedence
704 tokens
= cls
._RE
_OPERATORS
.split(string
)
705 for i
, token
in enumerate(tokens
):
707 token
= '({})'.format(token
)
709 string
= ''.join(tokens
)
710 tree
= ast
.parse(string
, 'eval')
711 return cls
._fromast
(tree
)
714 assert len(self
.polyhedra
) >= 2
715 strings
= [repr(polyhedron
) for polyhedron
in self
.polyhedra
]
716 return 'Or({})'.format(', '.join(strings
))
718 def _repr_latex_(self
):
720 for polyhedron
in self
.polyhedra
:
721 strings
.append('({})'.format(polyhedron
._repr
_latex
_().strip('$')))
722 return '${}$'.format(' \\vee '.join(strings
))
725 def fromsympy(cls
, expr
):
727 Create a domain from a sympy expression.
730 from .polyhedra
import Lt
, Le
, Eq
, Ne
, Ge
, Gt
732 sympy
.And
: And
, sympy
.Or
: Or
, sympy
.Not
: Not
,
733 sympy
.Lt
: Lt
, sympy
.Le
: Le
,
734 sympy
.Eq
: Eq
, sympy
.Ne
: Ne
,
735 sympy
.Ge
: Ge
, sympy
.Gt
: Gt
,
737 if expr
.func
in funcmap
:
738 args
= [Domain
.fromsympy(arg
) for arg
in expr
.args
]
739 return funcmap
[expr
.func
](*args
)
740 elif isinstance(expr
, sympy
.Expr
):
741 return LinExpr
.fromsympy(expr
)
742 raise ValueError('non-domain expression: {!r}'.format(expr
))
746 Convert the domain to a sympy expression.
749 polyhedra
= [polyhedron
.tosympy() for polyhedron
in polyhedra
]
750 return sympy
.Or(*polyhedra
)
755 Create the intersection domain of the domains given in arguments.
757 if len(domains
) == 0:
758 from .polyhedra
import Universe
761 return domains
[0].intersection(*domains
[1:])
762 And
.__doc
__ = Domain
.intersection
.__doc
__
766 Create the union domain of the domains given in arguments.
768 if len(domains
) == 0:
769 from .polyhedra
import Empty
772 return domains
[0].union(*domains
[1:])
773 Or
.__doc
__ = Domain
.union
.__doc
__
777 Create the complementary domain of the domain given in argument.
780 Not
.__doc
__ = Domain
.complement
.__doc
__