X-Git-Url: https://svn.cri.ensmp.fr/git/linpy.git/blobdiff_plain/ba15f3f33f837b1291f74bc94081e99b860d3228..198c368d0b24dc80b1139b2a7282cc50ef654e4d:/linpy/polyhedra.py?ds=inline diff --git a/linpy/polyhedra.py b/linpy/polyhedra.py index b486be1..a720b74 100644 --- a/linpy/polyhedra.py +++ b/linpy/polyhedra.py @@ -44,7 +44,6 @@ class Polyhedron(Domain): __slots__ = ( '_equalities', '_inequalities', - '_constraints', '_symbols', '_dimension', ) @@ -88,22 +87,20 @@ class Polyhedron(Domain): if inequalities is not None: raise TypeError('too many arguments') return equalities.aspolyhedron() - if equalities is None: - equalities = [] - else: - for i, equality in enumerate(equalities): + sc_equalities = [] + if equalities is not None: + for equality in equalities: if not isinstance(equality, LinExpr): raise TypeError('equalities must be linear expressions') - equalities[i] = equality.scaleint() - if inequalities is None: - inequalities = [] - else: - for i, inequality in enumerate(inequalities): + sc_equalities.append(equality.scaleint()) + sc_inequalities = [] + if inequalities is not None: + for inequality in inequalities: if not isinstance(inequality, LinExpr): raise TypeError('inequalities must be linear expressions') - inequalities[i] = inequality.scaleint() - symbols = cls._xsymbols(equalities + inequalities) - islbset = cls._toislbasicset(equalities, inequalities, symbols) + sc_inequalities.append(inequality.scaleint()) + symbols = cls._xsymbols(sc_equalities + sc_inequalities) + islbset = cls._toislbasicset(sc_equalities, sc_inequalities, symbols) return cls._fromislbasicset(islbset, symbols) @property @@ -128,7 +125,7 @@ class Polyhedron(Domain): The tuple of constraints, i.e., equalities and inequalities. This is semantically equivalent to: equalities + inequalities. """ - return self._constraints + return self._equalities + self._inequalities @property def polyhedra(self): @@ -147,6 +144,15 @@ class Polyhedron(Domain): def aspolyhedron(self): return self + def convex_union(self, *others): + """ + Return the convex union of two or more polyhedra. + """ + for other in others: + if not isinstance(other, Polyhedron): + raise TypeError('arguments must be Polyhedron instances') + return Polyhedron(self.union(*others)) + def __contains__(self, point): if not isinstance(point, Point): raise TypeError('point must be a Point instance') @@ -167,7 +173,11 @@ class Polyhedron(Domain): for inequality in self.inequalities] return Polyhedron(equalities, inequalities) - def _asinequalities(self): + def asinequalities(self): + """ + Express the polyhedron using inequalities, given as a list of + expressions greater or equal to 0. + """ inequalities = list(self.equalities) inequalities.extend([-expression for expression in self.equalities]) inequalities.extend(self.inequalities) @@ -176,11 +186,14 @@ class Polyhedron(Domain): def widen(self, other): """ Compute the standard widening of two polyhedra, à la Halbwachs. + + In its current implementation, this method is slow and should not be + used on large polyhedra. """ if not isinstance(other, Polyhedron): - raise ValueError('argument must be a Polyhedron instance') - inequalities1 = self._asinequalities() - inequalities2 = other._asinequalities() + raise TypeError('argument must be a Polyhedron instance') + inequalities1 = self.asinequalities() + inequalities2 = other.asinequalities() inequalities = [] for inequality1 in inequalities1: if other <= Polyhedron(inequalities=[inequality1]): @@ -219,8 +232,7 @@ class Polyhedron(Domain): self = object().__new__(Polyhedron) self._equalities = tuple(equalities) self._inequalities = tuple(inequalities) - self._constraints = tuple(equalities + inequalities) - self._symbols = cls._xsymbols(self._constraints) + self._symbols = cls._xsymbols(self.constraints) self._dimension = len(self._symbols) return self @@ -307,13 +319,10 @@ class EmptyType(Polyhedron): The empty polyhedron, whose set of constraints is not satisfiable. """ - __slots__ = Polyhedron.__slots__ - def __new__(cls): self = object().__new__(cls) self._equalities = (Rational(1),) self._inequalities = () - self._constraints = self._equalities self._symbols = () self._dimension = 0 return self @@ -338,13 +347,10 @@ class UniverseType(Polyhedron): i.e. is empty. """ - __slots__ = Polyhedron.__slots__ - def __new__(cls): self = object().__new__(cls) self._equalities = () self._inequalities = () - self._constraints = () self._symbols = () self._dimension = () return self @@ -358,63 +364,77 @@ class UniverseType(Polyhedron): Universe = UniverseType() -def _polymorphic(func): +def _pseudoconstructor(func): @functools.wraps(func) - def wrapper(left, right): - if not isinstance(left, LinExpr): - if isinstance(left, numbers.Rational): - left = Rational(left) - else: - raise TypeError('left must be a a rational number ' - 'or a linear expression') - if not isinstance(right, LinExpr): - if isinstance(right, numbers.Rational): - right = Rational(right) - else: - raise TypeError('right must be a a rational number ' - 'or a linear expression') - return func(left, right) + def wrapper(expr1, expr2, *exprs): + exprs = (expr1, expr2) + exprs + for expr in exprs: + if not isinstance(expr, LinExpr): + if isinstance(expr, numbers.Rational): + expr = Rational(expr) + else: + raise TypeError('arguments must be rational numbers ' + 'or linear expressions') + return func(*exprs) return wrapper -@_polymorphic -def Lt(left, right): +@_pseudoconstructor +def Lt(*exprs): """ Create the polyhedron with constraints expr1 < expr2 < expr3 ... """ - return Polyhedron([], [right - left - 1]) + inequalities = [] + for left, right in zip(exprs, exprs[1:]): + inequalities.append(right - left - 1) + return Polyhedron([], inequalities) -@_polymorphic -def Le(left, right): +@_pseudoconstructor +def Le(*exprs): """ Create the polyhedron with constraints expr1 <= expr2 <= expr3 ... """ - return Polyhedron([], [right - left]) + inequalities = [] + for left, right in zip(exprs, exprs[1:]): + inequalities.append(right - left) + return Polyhedron([], inequalities) -@_polymorphic -def Eq(left, right): +@_pseudoconstructor +def Eq(*exprs): """ Create the polyhedron with constraints expr1 == expr2 == expr3 ... """ - return Polyhedron([left - right], []) + equalities = [] + for left, right in zip(exprs, exprs[1:]): + equalities.append(left - right) + return Polyhedron(equalities, []) -@_polymorphic -def Ne(left, right): +@_pseudoconstructor +def Ne(*exprs): """ Create the domain such that expr1 != expr2 != expr3 ... The result is a - Domain, not a Polyhedron. + Domain object, not a Polyhedron. """ - return ~Eq(left, right) + domain = Universe + for left, right in zip(exprs, exprs[1:]): + domain &= ~Eq(left, right) + return domain -@_polymorphic -def Gt(left, right): +@_pseudoconstructor +def Ge(*exprs): """ - Create the polyhedron with constraints expr1 > expr2 > expr3 ... + Create the polyhedron with constraints expr1 >= expr2 >= expr3 ... """ - return Polyhedron([], [left - right - 1]) + inequalities = [] + for left, right in zip(exprs, exprs[1:]): + inequalities.append(left - right) + return Polyhedron([], inequalities) -@_polymorphic -def Ge(left, right): +@_pseudoconstructor +def Gt(*exprs): """ - Create the polyhedron with constraints expr1 >= expr2 >= expr3 ... + Create the polyhedron with constraints expr1 > expr2 > expr3 ... """ - return Polyhedron([], [left - right]) + inequalities = [] + for left, right in zip(exprs, exprs[1:]): + inequalities.append(left - right - 1) + return Polyhedron([], inequalities)