X-Git-Url: https://svn.cri.ensmp.fr/git/linpy.git/blobdiff_plain/1d494bb187b70135df721c13306d7f26fdf33f50..c3d305942999eeb429c6941184b47e7f1b55c499:/pypol/linexprs.py?ds=sidebyside diff --git a/pypol/linexprs.py b/pypol/linexprs.py index 0db7edd..9449219 100644 --- a/pypol/linexprs.py +++ b/pypol/linexprs.py @@ -3,6 +3,7 @@ import functools import numbers import re +from collections import OrderedDict from fractions import Fraction, gcd @@ -35,6 +36,7 @@ class Expression: '_constant', '_symbols', '_dimension', + '_hash', ) def __new__(cls, coefficients=None, constant=0): @@ -67,19 +69,21 @@ class Expression: raise TypeError('coefficients must be rational numbers ' 'or Constant instances') self._coefficients[symbol] = coefficient + self._coefficients = OrderedDict(sorted(self._coefficients.items())) if isinstance(constant, Constant): constant = constant.constant if not isinstance(constant, numbers.Rational): raise TypeError('constant must be a rational number ' 'or a Constant instance') self._constant = constant - self._symbols = tuple(sorted(self._coefficients)) + self._symbols = tuple(self._coefficients) self._dimension = len(self._symbols) + self._hash = hash((tuple(self._coefficients.items()), self._constant)) return self def coefficient(self, symbol): if isinstance(symbol, Symbol): - symbol = str(symbol) + symbol = symbol.name elif not isinstance(symbol, str): raise TypeError('symbol must be a string or a Symbol instance') try: @@ -90,8 +94,7 @@ class Expression: __getitem__ = coefficient def coefficients(self): - for symbol in self.symbols: - yield symbol, self.coefficient(symbol) + yield from self._coefficients.items() @property def constant(self): @@ -105,6 +108,9 @@ class Expression: def dimension(self): return self._dimension + def __hash__(self): + return self._hash + def isconstant(self): return False @@ -219,9 +225,6 @@ class Expression: from .polyhedra import Gt return Gt(self, other) - def __hash__(self): - return hash((tuple(sorted(self._coefficients.items())), self._constant)) - def _toint(self): lcm = functools.reduce(lambda a, b: a*b // gcd(a, b), [value.denominator for value in self.values()]) @@ -252,9 +255,12 @@ class Expression: return left / right raise SyntaxError('invalid syntax') + _RE_NUM_VAR = re.compile(r'(\d+|\))\s*([^\W\d_]\w*|\()') + @classmethod def fromstring(cls, string): - string = re.sub(r'(\d+|\))\s*([^\W\d_]\w*|\()', r'\1*\2', string) + # add implicit multiplication operators, e.g. '5x' -> '5*x' + string = cls._RE_NUM_VAR.sub(r'\1*\2', string) tree = ast.parse(string, 'eval') return cls._fromast(tree) @@ -333,8 +339,9 @@ class Expression: class Symbol(Expression): - __slots__ = Expression.__slots__ + ( + __slots__ = ( '_name', + '_hash', ) def __new__(cls, name): @@ -344,20 +351,48 @@ class Symbol(Expression): raise TypeError('name must be a string or a Symbol instance') name = name.strip() self = object().__new__(cls) - self._coefficients = {name: 1} - self._constant = 0 - self._symbols = tuple(name) self._name = name - self._dimension = 1 + self._hash = hash(self._name) return self @property def name(self): return self._name + def __hash__(self): + return self._hash + + def coefficient(self, symbol): + if isinstance(symbol, Symbol): + symbol = symbol.name + elif not isinstance(symbol, str): + raise TypeError('symbol must be a string or a Symbol instance') + if symbol == self.name: + return 1 + else: + return 0 + + def coefficients(self): + yield self.name, 1 + + @property + def constant(self): + return 0 + + @property + def symbols(self): + return self.name, + + @property + def dimension(self): + return 1 + def issymbol(self): return True + def __eq__(self, other): + return isinstance(other, Symbol) and self.name == other.name + @classmethod def _fromast(cls, node): if isinstance(node, ast.Module) and len(node.body) == 1: @@ -388,20 +423,48 @@ def symbols(names): class Constant(Expression): + __slots__ = ( + '_constant', + '_hash', + ) + def __new__(cls, numerator=0, denominator=None): self = object().__new__(cls) if denominator is None and isinstance(numerator, Constant): self._constant = numerator.constant else: self._constant = Fraction(numerator, denominator) - self._coefficients = {} - self._symbols = () - self._dimension = 0 + self._hash = hash(self._constant) return self + def __hash__(self): + return self._hash + + def coefficient(self, symbol): + if isinstance(symbol, Symbol): + symbol = symbol.name + elif not isinstance(symbol, str): + raise TypeError('symbol must be a string or a Symbol instance') + return 0 + + def coefficients(self): + yield from [] + + @property + def symbols(self): + return () + + @property + def dimension(self): + return 0 + def isconstant(self): return True + @_polymorphic + def __eq__(self, other): + return isinstance(other, Constant) and self.constant == other.constant + def __bool__(self): return self.constant != 0