Implementation using iscc
authorVivien Maisonneuve <v.maisonneuve@gmail.com>
Sun, 8 Jun 2014 15:40:34 +0000 (17:40 +0200)
committerVivien Maisonneuve <v.maisonneuve@gmail.com>
Sun, 8 Jun 2014 15:44:37 +0000 (17:44 +0200)
Makefile
polyp.py [moved from pypol/linear.py with 58% similarity]
pypol/__init__.py [deleted file]
pypol/isl.py [deleted file]
setup.py
tests/__init__.py [deleted file]
tests/test_isl.py [deleted file]
tests/test_linear.py [deleted file]

index 4ab5d2a..d45d3b2 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -3,16 +3,11 @@ RM=rm -rf
 
 .PHONY: default
 default:
 
 .PHONY: default
 default:
-       @echo "pypol - A polyhedral library based on ISL"
+       @echo "polyp - A polyhedral library based on ISL"
        @echo
        @echo "Makefile usage:"
        @echo
        @echo "Makefile usage:"
-       @echo "  make test             run the test suite"
        @echo "  make clean            remove the generated files"
 
        @echo "  make clean            remove the generated files"
 
-.PHONY: test
-test:
-       $(PYTHON) -m unittest
-
 .PHONY: clean
 clean:
 .PHONY: clean
 clean:
-       $(RM) build dist MANIFEST pypol/__pycache__ tests/__pycache__
+       $(RM) build dist MANIFEST __pycache__
similarity index 58%
rename from pypol/linear.py
rename to polyp.py
index a5f55fa..b73814e 100644 (file)
+++ b/polyp.py
@@ -1,26 +1,43 @@
 
 import functools
 import numbers
 
 import functools
 import numbers
+import re
+import subprocess
 
 from fractions import Fraction, gcd
 
 
 __all__ = [
     'Expression',
 
 from fractions import Fraction, gcd
 
 
 __all__ = [
     'Expression',
-    'constant', 'symbol', 'symbols',
-    'eq', 'le', 'lt', 'ge', 'gt',
+    'Constant', 'Symbol', 'symbols',
+    'Eq', 'Le', 'Lt', 'Ge', 'Gt',
     'Polyhedron',
     'empty', 'universe'
 ]
 
 
     'Polyhedron',
     'empty', 'universe'
 ]
 
 
+_iscc_debug = False
+
+def _iscc(input):
+    if not input.endswith(';'):
+        input += ';'
+    proc = subprocess.Popen(['iscc'],
+            stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+            universal_newlines=True)
+    output, error = proc.communicate(input=input)
+    output = output.strip()
+    if _iscc_debug:
+        print('ISCC({!r}) = {!r}'.format(input, output))
+    return output
+
+
 def _polymorphic_method(func):
     @functools.wraps(func)
     def wrapper(a, b):
         if isinstance(b, Expression):
             return func(a, b)
         if isinstance(b, numbers.Rational):
 def _polymorphic_method(func):
     @functools.wraps(func)
     def wrapper(a, b):
         if isinstance(b, Expression):
             return func(a, b)
         if isinstance(b, numbers.Rational):
-            b = constant(b)
+            b = Constant(b)
             return func(a, b)
         return NotImplemented
     return wrapper
             return func(a, b)
         return NotImplemented
     return wrapper
@@ -31,7 +48,7 @@ def _polymorphic_operator(func):
     @functools.wraps(func)
     def wrapper(a, b):
         if isinstance(a, numbers.Rational):
     @functools.wraps(func)
     def wrapper(a, b):
         if isinstance(a, numbers.Rational):
-            a = constant(a)
+            a = Constant(a)
             return func(a, b)
         elif isinstance(a, Expression):
             return func(a, b)
             return func(a, b)
         elif isinstance(a, Expression):
             return func(a, b)
@@ -45,10 +62,6 @@ class Expression:
     """
 
     def __new__(cls, coefficients=None, constant=0):
     """
 
     def __new__(cls, coefficients=None, constant=0):
-        if isinstance(coefficients, str):
-            if constant:
-                raise TypeError('too many arguments')
-            return cls.fromstring(coefficients)
         self = super().__new__(cls)
         self._coefficients = {}
         if isinstance(coefficients, dict):
         self = super().__new__(cls)
         self._coefficients = {}
         if isinstance(coefficients, dict):
@@ -68,6 +81,16 @@ class Expression:
         self._constant = constant
         return self
 
         self._constant = constant
         return self
 
+    @classmethod
+    def _fromiscc(cls, symbols, string):
+        string = re.sub(r'(\d+)\s*([a-zA-Z_]\w*)',
+                lambda m: '{}*{}'.format(m.group(1), m.group(2)),
+                string)
+        context = {}
+        for symbol in symbols:
+            context[symbol] = Symbol(symbol)
+        return eval(string, context)
+
     def symbols(self):
         yield from sorted(self._coefficients)
 
     def symbols(self):
         yield from sorted(self._coefficients)
 
@@ -145,7 +168,8 @@ class Expression:
         constant = self.constant - other.constant
         return Expression(coefficients, constant)
 
         constant = self.constant - other.constant
         return Expression(coefficients, constant)
 
-    __rsub__ = __sub__
+    def __rsub__(self, other):
+        return -(self - other)
 
     @_polymorphic_method
     def __mul__(self, other):
 
     @_polymorphic_method
     def __mul__(self, other):
@@ -157,36 +181,12 @@ class Expression:
             return Expression(coefficients, constant)
         if isinstance(other, Expression) and not self.isconstant():
             raise ValueError('non-linear expression: '
             return Expression(coefficients, constant)
         if isinstance(other, Expression) and not self.isconstant():
             raise ValueError('non-linear expression: '
-                    '{} * {}'.format(self._parenstr(), other._parenstr()))
+                    '{} * {}'.format(self._prepr(), other._prepr()))
         return NotImplemented
 
     __rmul__ = __mul__
 
         return NotImplemented
 
     __rmul__ = __mul__
 
-    @_polymorphic_method
-    def __truediv__(self, other):
-        if other.isconstant():
-            coefficients = dict(self.coefficients())
-            for symbol in coefficients:
-                coefficients[symbol] = \
-                        Fraction(coefficients[symbol], other.constant)
-            constant = Fraction(self.constant, other.constant)
-            return Expression(coefficients, constant)
-        if isinstance(other, Expression):
-            raise ValueError('non-linear expression: '
-                '{} / {}'.format(self._parenstr(), other._parenstr()))
-        return NotImplemented
-
-    def __rtruediv__(self, other):
-        if isinstance(other, Rational):
-            if self.isconstant():
-                constant = Fraction(other, self.constant)
-                return Expression(constant=constant)
-            else:
-                raise ValueError('non-linear expression: '
-                        '{} / {}'.format(other._parenstr(), self._parenstr()))
-        return NotImplemented
-
-    def __str__(self):
+    def __repr__(self):
         string = ''
         symbols = sorted(self.symbols())
         i = 0
         string = ''
         symbols = sorted(self.symbols())
         i = 0
@@ -224,26 +224,13 @@ class Expression:
             string = '0'
         return string
 
             string = '0'
         return string
 
-    def _parenstr(self, always=False):
+    def _prepr(self, always=False):
         string = str(self)
         if not always and (self.isconstant() or self.issymbol()):
             return string
         else:
             return '({})'.format(string)
 
         string = str(self)
         if not always and (self.isconstant() or self.issymbol()):
             return string
         else:
             return '({})'.format(string)
 
-    def __repr__(self):
-        string = '{}({{'.format(self.__class__.__name__)
-        for i, (symbol, coefficient) in enumerate(self.coefficients()):
-            if i != 0:
-                string += ', '
-            string += '{!r}: {!r}'.format(symbol, coefficient)
-        string += '}}, {!r})'.format(self.constant)
-        return string
-
-    @classmethod
-    def fromstring(cls, string):
-        raise NotImplementedError
-
     @_polymorphic_method
     def __eq__(self, other):
         # "normal" equality
     @_polymorphic_method
     def __eq__(self, other):
         # "normal" equality
@@ -253,41 +240,36 @@ class Expression:
                 self.constant == other.constant
 
     def __hash__(self):
                 self.constant == other.constant
 
     def __hash__(self):
-        return hash((self._coefficients, self._constant))
-
-    def _canonify(self):
-        lcm = functools.reduce(lambda a, b: a*b // gcd(a, b),
-                [value.denominator for value in self.values()])
-        return self * lcm
+        return hash((tuple(self._coefficients), self._constant))
 
     @_polymorphic_method
     def _eq(self, other):
 
     @_polymorphic_method
     def _eq(self, other):
-        return Polyhedron(equalities=[(self - other)._canonify()])
+        return Polyhedron(equalities=[self - other])
 
     @_polymorphic_method
     def __le__(self, other):
 
     @_polymorphic_method
     def __le__(self, other):
-        return Polyhedron(inequalities=[(self - other)._canonify()])
+        return Polyhedron(inequalities=[self - other])
 
     @_polymorphic_method
     def __lt__(self, other):
 
     @_polymorphic_method
     def __lt__(self, other):
-        return Polyhedron(inequalities=[(self - other)._canonify() + 1])
+        return Polyhedron(inequalities=[self - other + 1])
 
     @_polymorphic_method
     def __ge__(self, other):
 
     @_polymorphic_method
     def __ge__(self, other):
-        return Polyhedron(inequalities=[(other - self)._canonify()])
+        return Polyhedron(inequalities=[other - self])
 
     @_polymorphic_method
     def __gt__(self, other):
 
     @_polymorphic_method
     def __gt__(self, other):
-        return Polyhedron(inequalities=[(other - self)._canonify() + 1])
+        return Polyhedron(inequalities=[other - self + 1])
 
 
 
 
-def constant(numerator=0, denominator=None):
+def Constant(numerator=0, denominator=None):
     if denominator is None and isinstance(numerator, numbers.Rational):
         return Expression(constant=numerator)
     else:
         return Expression(constant=Fraction(numerator, denominator))
 
     if denominator is None and isinstance(numerator, numbers.Rational):
         return Expression(constant=numerator)
     else:
         return Expression(constant=Fraction(numerator, denominator))
 
-def symbol(name):
+def Symbol(name):
     if not isinstance(name, str):
         raise TypeError('name must be a string')
     return Expression(coefficients={name: 1})
     if not isinstance(name, str):
         raise TypeError('name must be a string')
     return Expression(coefficients={name: 1})
@@ -295,27 +277,27 @@ def symbol(name):
 def symbols(names):
     if isinstance(names, str):
         names = names.replace(',', ' ').split()
 def symbols(names):
     if isinstance(names, str):
         names = names.replace(',', ' ').split()
-    return (symbol(name) for name in names)
+    return (Symbol(name) for name in names)
 
 
 @_polymorphic_operator
 
 
 @_polymorphic_operator
-def eq(a, b):
+def Eq(a, b):
     return a._eq(b)
 
 @_polymorphic_operator
     return a._eq(b)
 
 @_polymorphic_operator
-def le(a, b):
+def Le(a, b):
     return a <= b
 
 @_polymorphic_operator
     return a <= b
 
 @_polymorphic_operator
-def lt(a, b):
+def Lt(a, b):
     return a < b
 
 @_polymorphic_operator
     return a < b
 
 @_polymorphic_operator
-def ge(a, b):
+def Ge(a, b):
     return a >= b
 
 @_polymorphic_operator
     return a >= b
 
 @_polymorphic_operator
-def gt(a, b):
+def Gt(a, b):
     return a > b
 
 
     return a > b
 
 
@@ -325,27 +307,70 @@ class Polyhedron:
     """
 
     def __new__(cls, equalities=None, inequalities=None):
     """
 
     def __new__(cls, equalities=None, inequalities=None):
-        if isinstance(equalities, str):
-            if inequalities is not None:
-                raise TypeError('too many arguments')
-            return cls.fromstring(equalities)
+        if equalities is None:
+            equalities = []
+        if inequalities is None:
+            inequalities = []
+        symbols = set()
+        for equality in equalities:
+            symbols.update(equality.symbols())
+        for inequality in inequalities:
+            symbols.update(inequality.symbols())
+        symbols = sorted(symbols)
+        string = cls._isccpoly(symbols, equalities, inequalities)
+        string = _iscc(string)
+        return cls._fromiscc(symbols, string)
+
+    @classmethod
+    def _isccpoly(cls, symbols, equalities, inequalities):
+        strings = []
+        for equality in equalities:
+            strings.append('{} = 0'.format(equality))
+        for inequality in inequalities:
+            strings.append('{} <= 0'.format(inequality))
+        string = '{{ [{}] : {} }}'.format(', '.join(symbols), ' and '.join(strings))
+        return string
+
+    def _toiscc(self, symbols):
+        return self._isccpoly(symbols, self.equalities, self.inequalities)
+
+    @classmethod
+    def _fromiscc(cls, symbols, string):
+        if re.match(r'^\s*\{\s*\}\s*$', string):
+            return empty
         self = super().__new__(cls)
         self = super().__new__(cls)
+        self._symbols = symbols
         self._equalities = []
         self._equalities = []
-        if equalities is not None:
-            for constraint in equalities:
-                for value in constraint.values():
-                    if value.denominator != 1:
-                        raise TypeError('non-integer constraint: '
-                                '{} == 0'.format(constraint))
-                self._equalities.append(constraint)
         self._inequalities = []
         self._inequalities = []
-        if inequalities is not None:
-            for constraint in inequalities:
-                for value in constraint.values():
-                    if value.denominator != 1:
-                        raise TypeError('non-integer constraint: '
-                                '{} <= 0'.format(constraint))
-                self._inequalities.append(constraint)
+        string = re.sub(r'^\s*\{\s*(.*?)\s*\}\s*$', lambda m: m.group(1), string)
+        if ':' not in string:
+            string = string + ':'
+        vstr, cstr = re.split(r'\s*:\s*', string)
+        assert vstr != ''
+        vstr = re.sub(r'^\s*\[\s*(.*?)\s*\]\s*$', lambda m: m.group(1), vstr)
+        toks = list(filter(None, re.split(r'\s*,\s*', vstr)))
+        assert len(toks) == len(symbols)
+        for i in range(len(symbols)):
+            symbol = symbols[i]
+            if toks[i] != symbol:
+                expr = Expression._fromiscc(symbols, toks[i])
+                self._equalities.append(Symbol(symbol) - expr)
+        if cstr != '':
+            cstrs = re.split(r'\s*and\s*', cstr)
+            for cstr in cstrs:
+                lhs, op, rhs = re.split(r'\s*(<=|>=|<|>|=)\s*', cstr)
+                lhs = Expression._fromiscc(symbols, lhs)
+                rhs = Expression._fromiscc(symbols, rhs)
+                if op == '=':
+                    self._equalities.append(lhs - rhs)
+                elif op == '<=':
+                    self._inequalities.append(lhs - rhs)
+                elif op == '>=':
+                    self._inequalities.append(rhs - lhs)
+                elif op == '<':
+                    self._inequalities.append(lhs - rhs + 1)
+                elif op == '>':
+                    self._inequalities.append(rhs - lhs + 1)
         return self
 
     @property
         return self
 
     @property
@@ -361,10 +386,7 @@ class Polyhedron:
         yield from self.inequalities
 
     def symbols(self):
         yield from self.inequalities
 
     def symbols(self):
-        s = set()
-        for constraint in self.constraints():
-            s.update(constraint.symbols)
-        yield from sorted(s)
+        yield from self._symbols
 
     @property
     def dimension(self):
 
     @property
     def dimension(self):
@@ -372,14 +394,13 @@ class Polyhedron:
 
     def __bool__(self):
         # return false if the polyhedron is empty, true otherwise
 
     def __bool__(self):
         # return false if the polyhedron is empty, true otherwise
-        raise NotImplementedError
-
-    def __contains__(self, value):
-        # is the value in the polyhedron?
-        raise NotImplementedError
+        raise not self.isempty()
 
     def __eq__(self, other):
 
     def __eq__(self, other):
-        raise NotImplementedError
+        symbols = set(self.symbols()) | set(other.symbols())
+        string = '{} = {}'.format(self._toiscc(symbols), other._toiscc(symbols))
+        string = _iscc(string)
+        return string == 'True'
 
     def isempty(self):
         return self == empty
 
     def isempty(self):
         return self == empty
@@ -389,46 +410,67 @@ class Polyhedron:
 
     def isdisjoint(self, other):
         # return true if the polyhedron has no elements in common with other
 
     def isdisjoint(self, other):
         # return true if the polyhedron has no elements in common with other
-        raise NotImplementedError
+        return (self & other).isempty()
 
     def issubset(self, other):
 
     def issubset(self, other):
-        raise NotImplementedError
+        symbols = set(self.symbols()) | set(other.symbols())
+        string = '{} <= {}'.format(self._toiscc(symbols), other._toiscc(symbols))
+        string = _iscc(string)
+        return string == 'True'
 
     def __le__(self, other):
         return self.issubset(other)
 
     def __lt__(self, other):
 
     def __le__(self, other):
         return self.issubset(other)
 
     def __lt__(self, other):
-        raise NotImplementedError
+        symbols = set(self.symbols()) | set(other.symbols())
+        string = '{} < {}'.format(self._toiscc(symbols), other._toiscc(symbols))
+        string = _iscc(string)
+        return string == 'True'
 
     def issuperset(self, other):
         # test whether every element in other is in the polyhedron
 
     def issuperset(self, other):
         # test whether every element in other is in the polyhedron
-        raise NotImplementedError
+        symbols = set(self.symbols()) | set(other.symbols())
+        string = '{} >= {}'.format(self._toiscc(symbols), other._toiscc(symbols))
+        string = _iscc(string)
+        return string == 'True'
 
     def __ge__(self, other):
         return self.issuperset(other)
 
     def __gt__(self, other):
 
     def __ge__(self, other):
         return self.issuperset(other)
 
     def __gt__(self, other):
-        raise NotImplementedError
+        symbols = set(self.symbols() + other.symbols())
+        string = '{} > {}'.format(self._toiscc(symbols), other._toiscc(symbols))
+        string = _iscc(string)
+        return string == 'True'
 
     def union(self, *others):
         # return a new polyhedron with elements from the polyhedron and all
         # others (convex union)
 
     def union(self, *others):
         # return a new polyhedron with elements from the polyhedron and all
         # others (convex union)
-        raise NotImplementedError
+        symbols = set(self.symbols())
+        for other in others:
+            symbols.update(other.symbols())
+        symbols = sorted(symbols)
+        strings = [self._toiscc(symbols)]
+        for other in others:
+            strings.append(other._toiscc(symbols))
+        string = ' + '.join(strings)
+        string = _iscc('poly ({})'.format(string))
+        return Polyhedron._fromiscc(symbols, string)
 
     def __or__(self, other):
         return self.union(other)
 
     def intersection(self, *others):
 
     def __or__(self, other):
         return self.union(other)
 
     def intersection(self, *others):
-        # return a new polyhedron with elements common to the polyhedron and all
-        # others
-        # a poor man's implementation could be:
-        # equalities = list(self.equalities)
-        # inequalities = list(self.inequalities)
-        for other in others:
-        #     equalities.extend(other.equalities)
-        #     inequalities.extend(other.inequalities)
-        # return self.__class__(equalities, inequalities)
-        raise NotImplementedError
+        symbols = set(self.symbols())
+        for other in others:
+            symbols.update(other.symbols())
+        symbols = sorted(symbols)
+        strings = [self._toiscc(symbols)]
+        for other in others:
+            strings.append(other._toiscc(symbols))
+        string = ' * '.join(strings)
+        string = _iscc('poly ({})'.format(string))
+        return Polyhedron._fromiscc(symbols, string)
 
     def __and__(self, other):
         return self.intersection(other)
 
     def __and__(self, other):
         return self.intersection(other)
@@ -436,30 +478,35 @@ class Polyhedron:
     def difference(self, *others):
         # return a new polyhedron with elements in the polyhedron that are not
         # in the others
     def difference(self, *others):
         # return a new polyhedron with elements in the polyhedron that are not
         # in the others
-        raise NotImplementedError
+        symbols = set(self.symbols())
+        for other in others:
+            symbols.update(other.symbols())
+        symbols = sorted(symbols)
+        strings = [self._toiscc(symbols)]
+        for other in others:
+            strings.append(other._toiscc(symbols))
+        string = ' - '.join(strings)
+        string = _iscc('poly ({})'.format(string))
+        return Polyhedron._fromiscc(symbols, string)
 
     def __sub__(self, other):
         return self.difference(other)
 
 
     def __sub__(self, other):
         return self.difference(other)
 
-    def __str__(self):
+    def __repr__(self):
         constraints = []
         for constraint in self.equalities:
             constraints.append('{} == 0'.format(constraint))
         for constraint in self.inequalities:
             constraints.append('{} <= 0'.format(constraint))
         constraints = []
         for constraint in self.equalities:
             constraints.append('{} == 0'.format(constraint))
         for constraint in self.inequalities:
             constraints.append('{} <= 0'.format(constraint))
-        return '{{{}}}'.format(', '.join(constraints))
-
-    def __repr__(self):
-        equalities = list(self.equalities)
-        inequalities = list(self.inequalities)
-        return '{}(equalities={!r}, inequalities={!r})' \
-                ''.format(self.__class__.__name__, equalities, inequalities)
-
-    @classmethod
-    def fromstring(cls, string):
-        raise NotImplementedError
-
+        if len(constraints) == 0:
+            return 'universe'
+        elif len(constraints) == 1:
+            string = constraints[0]
+            return 'empty' if string == '1 == 0' else string
+        else:
+            strings = ['({})'.format(constraint) for constraint in constraints]
+            return ' & '.join(strings)
 
 
-empty = le(1, 0)
+empty = Eq(1, 0)
 
 universe = Polyhedron()
 
 universe = Polyhedron()
diff --git a/pypol/__init__.py b/pypol/__init__.py
deleted file mode 100644 (file)
index fde0347..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-
-"""
-A polyhedral library based on ISL.
-"""
-
-from .linear import constant, symbol, symbols
-from .linear import eq, le, lt, ge, gt
-from .linear import empty, universe
-
-
-__all__ = [
-    'constant', 'symbol', 'symbols',
-    'eq', 'le', 'lt', 'ge', 'gt',
-    'empty', 'universe'
-]
diff --git a/pypol/isl.py b/pypol/isl.py
deleted file mode 100644 (file)
index 74cc5d3..0000000
+++ /dev/null
@@ -1,267 +0,0 @@
-
-import ctypes, ctypes.util
-import math
-import numbers
-import operator
-import re
-import functools
-
-from decimal import Decimal
-from fractions import Fraction
-from functools import wraps
-
-
-libisl = ctypes.CDLL(ctypes.util.find_library('isl'))
-
-libisl.isl_printer_get_str.restype = ctypes.c_char_p
-
-def _polymorphic_method(func):
-    @functools.wraps(func)
-    def wrapper(self, other):
-        if isinstance(other, Value):
-            return func(self, other)
-        if isinstance(other, numbers.Rational):
-            other = Value(self.context, other)
-            return func(self, other)
-        raise TypeError('operand should be a Value or a Rational')
-    return wrapper
-
-class Context:
-
-    __slots__ = ('_ic')
-
-    def __init__(self):
-        self._ic = libisl.isl_ctx_alloc()
-
-    @property
-    def _as_parameter_(self):
-        return self._ic
-
-    def __del__(self):
-        libisl.isl_ctx_free(self)
-
-    def __eq__(self, other):
-        if not isinstance(other, Context):
-            return False
-        return self._ic == other._ic
-
-
-class Value:
-
-    class _ptr(int):
-        def __new__(cls, iv):
-            return super().__new__(cls, iv)
-        def __repr__(self):
-            return '{}({})'.format(self.__class__.__name__, self)
-
-    _RE_NONFINITE = re.compile(
-            r'^\s*(?P<sign>[-+])?((?P<inf>Inf(inity)?)|(?P<nan>NaN))\s*$',
-            re.IGNORECASE)
-
-    _RE_FRACTION = re.compile(r'^(?P<num>[-+]?\d+)(/(?P<den>\d+))?$')
-
-    __slots__ = ('context', '_iv', '_numerator', '_denominator')
-
-    def __new__(cls, context, numerator=0, denominator=None):
-        self = super().__new__(cls)
-        if not isinstance(context, Context):
-            raise TypeError('first argument should be a context')
-        self.context = context
-        if isinstance(numerator, cls._ptr):
-            assert denominator is None
-            self._iv = numerator
-            if libisl.isl_val_is_rat(self):
-                # retrieve numerator and denominator as strings to avoid integer
-                # overflows
-                ip = libisl.isl_printer_to_str(self.context)
-                ip = libisl.isl_printer_print_val(ip, self)
-                string = libisl.isl_printer_get_str(ip).decode()
-                libisl.isl_printer_free(ip)
-                m = self._RE_FRACTION.match(string)
-                assert m is not None
-                self._numerator = int(m.group('num'))
-                self._denominator = int(m.group('den')) if m.group('den') else 1
-            else:
-                self._numerator = None
-                self._denominator = None
-            return self
-        if isinstance(numerator, str) and denominator is None:
-            m = self._RE_NONFINITE.match(numerator)
-            if m is not None:
-                self._numerator = None
-                self._denominator = None
-                if m.group('inf'):
-                    if m.group('sign') == '-':
-                        self._iv = libisl.isl_val_neginfty(context)
-                    else:
-                        self._iv = libisl.isl_val_infty(context)
-                else:
-                    assert m.group('nan')
-                    self._iv = libisl.isl_val_nan(context)
-                return self
-        try:
-            frac = Fraction(numerator, denominator)
-        except ValueError:
-            raise ValueError('invalid literal for {}: {!r}'.format(
-                    cls.__name__, numerator))
-        self._numerator = frac.numerator
-        self._denominator = frac.denominator
-        # values passed as strings to avoid integer overflows
-        if frac.denominator == 1:
-            numerator = str(frac.numerator).encode()
-            self._iv = libisl.isl_val_read_from_str(context, numerator)
-        else:
-            numerator = str(frac.numerator).encode()
-            numerator = libisl.isl_val_read_from_str(context, numerator)
-            denominator = str(frac.denominator).encode()
-            denominator = libisl.isl_val_read_from_str(context, denominator)
-            self._iv = libisl.isl_val_div(numerator, denominator)
-        return self
-
-    @property
-    def _as_parameter_(self):
-        return self._iv
-
-    def __del__(self):
-        libisl.isl_val_free(self)
-        self.context # prevents context from being GC'ed before the value
-
-    @property
-    def numerator(self):
-        if self._numerator is None:
-            raise ValueError('not a rational number')
-        return self._numerator
-
-    @property
-    def denominator(self):
-        if self._denominator is None:
-            raise ValueError('not a rational number')
-        return self._denominator
-
-    def __bool__(self):
-        return not bool(libisl.isl_val_is_zero(self))
-
-    @_polymorphic_method
-    def __lt__(self, other):
-        return bool(libisl.isl_val_lt(self, other))
-
-    @_polymorphic_method
-    def __le__(self, other):
-        return bool(libisl.isl_val_le(self, other))
-
-    @_polymorphic_method
-    def __gt__(self, other):
-        return bool(libisl.isl_val_gt(self, other))
-
-    @_polymorphic_method
-    def __ge__(self, other):
-        return bool(libisl.isl_val_ge(self, other))
-
-    @_polymorphic_method
-    def __eq__(self, other):
-        return bool(libisl.isl_val_eq(self, other))
-
-    # __ne__ is not implemented, ISL semantics does not match Python's on
-    # nan != nan
-
-    def __abs__(self):
-        val = libisl.isl_val_copy(self)
-        val = libisl.isl_val_abs(val)
-        return self.__class__(self.context, self._ptr(val))
-
-    def __pos__(self):
-        return self
-
-    def __neg__(self):
-        val = libisl.isl_val_copy(self)
-        val = libisl.isl_val_neg(val)
-        return self.__class__(self.context, self._ptr(val))
-
-    def __floor__(self):
-        val = libisl.isl_val_copy(self)
-        val = libisl.isl_val_floor(val)
-        return self.__class__(self.context, self._ptr(val))
-
-    def __ceil__(self):
-        val = libisl.isl_val_copy(self)
-        val = libisl.isl_val_ceil(val)
-        return self.__class__(self.context, self._ptr(val))
-
-    def __trunc__(self):
-        val = libisl.isl_val_copy(self)
-        val = libisl.isl_val_trunc(val)
-        return self.__class__(self.context, self._ptr(val))
-
-    @_polymorphic_method
-    def __add__(self, other):
-        val1 = libisl.isl_val_copy(self)
-        val2 = libisl.isl_val_copy(other)
-        val = libisl.isl_val_add(val1, val2)
-        return self.__class__(self.context, self._ptr(val))
-
-    __radd__ = __add__
-
-    @_polymorphic_method
-    def __sub__(self, other):
-        val1 = libisl.isl_val_copy(self)
-        val2 = libisl.isl_val_copy(other)
-        val = libisl.isl_val_sub(val1, val2)
-        return self.__class__(self.context, self._ptr(val))
-
-    __rsub__ = __sub__
-
-    @_polymorphic_method
-    def __mul__(self, other):
-        val1 = libisl.isl_val_copy(self)
-        val2 = libisl.isl_val_copy(other)
-        val = libisl.isl_val_mul(val1, val2)
-        return self.__class__(self.context, self._ptr(val))
-
-    __rmul__ = __mul__
-
-    @_polymorphic_method
-    def __truediv__(self, other):
-        val1 = libisl.isl_val_copy(self)
-        val2 = libisl.isl_val_copy(other)
-        val = libisl.isl_val_div(val1, val2)
-        return self.__class__(self.context, self._ptr(val))
-
-    __rtruediv__ = __truediv__
-
-    def __float__(self):
-        if libisl.isl_val_is_rat(self):
-            return self.numerator / self.denominator
-        elif libisl.isl_val_is_infty(self):
-            return float('inf')
-        elif libisl.isl_val_is_neginfty(self):
-            return float('-inf')
-        else:
-            assert libisl.isl_val_is_nan(self)
-            return float('nan')
-
-    def is_finite(self):
-        return bool(libisl.isl_val_is_rat(self))
-
-    def is_infinite(self):
-        return bool(libisl.isl_val_is_infty(self) or
-                libisl.isl_val_is_neginfty(self))
-
-    def is_nan(self):
-        return bool(libisl.isl_val_is_nan(self))
-
-    def __str__(self):
-        if libisl.isl_val_is_rat(self):
-            if self.denominator == 1:
-                return '{}'.format(self.numerator)
-            else:
-                return '{}/{}'.format(self.numerator, self.denominator)
-        elif libisl.isl_val_is_infty(self):
-            return 'Infinity'
-        elif libisl.isl_val_is_neginfty(self):
-            return '-Infinity'
-        else:
-            assert libisl.isl_val_is_nan(self)
-            return 'NaN'
-
-    def __repr__(self):
-        return '{}({!r})'.format(self.__class__.__name__, str(self))
index a0dfb8a..10e32f5 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -3,8 +3,8 @@
 from distutils.core import setup
 
 setup(
 from distutils.core import setup
 
 setup(
-    name='pypol',
+    name='polyp',
     description='A polyhedral library based on ISL',
     author='MINES ParisTech',
     description='A polyhedral library based on ISL',
     author='MINES ParisTech',
-    packages=['pypol']
+    packages=['polyp']
 )
 )
diff --git a/tests/__init__.py b/tests/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/tests/test_isl.py b/tests/test_isl.py
deleted file mode 100644 (file)
index 7186dad..0000000
+++ /dev/null
@@ -1,199 +0,0 @@
-
-import unittest
-
-from math import floor, ceil, trunc
-
-from pypol.isl import *
-
-
-class TestContext(unittest.TestCase):
-
-    def test_eq(self):
-        ctx1, ctx2 = Context(), Context()
-        self.assertEqual(ctx1, ctx1)
-        self.assertNotEqual(ctx1, ctx2)
-
-
-class TestValue(unittest.TestCase):
-
-    def setUp(self):
-        self.context = Context()
-        self.zero = Value(self.context)
-        self.nan = Value(self.context, 'NaN')
-        self.inf = Value(self.context, 'Inf')
-        self.neginf = Value(self.context, '-Inf')
-        self.answer = Value(self.context, 42)
-        self.pi = Value(self.context, 22, 7)
-
-    def test_init(self):
-        self.assertEqual(Value(self.context, 42), self.answer)
-        self.assertEqual(Value(self.context, '42'), self.answer)
-        self.assertEqual(Value(self.context, 22, 7), self.pi)
-        self.assertEqual(Value(self.context, '-22/7'), -self.pi)
-        self.assertTrue(Value(self.context, 'nan').is_nan())
-        self.assertTrue(Value(self.context, '-nan').is_nan())
-        self.assertTrue(Value(self.context, 'NaN').is_nan())
-        self.assertEqual(Value(self.context, '-inf'), self.neginf)
-        self.assertEqual(Value(self.context, '-Infinity'), self.neginf)
-
-    def test_numerator(self):
-        self.assertEqual(self.zero.numerator, 0)
-        self.assertEqual(self.answer.numerator, 42)
-        self.assertEqual(self.pi.numerator, 22)
-        with self.assertRaises(ValueError):
-            self.nan.numerator
-        with self.assertRaises(ValueError):
-            self.inf.numerator
-
-    def test_denominator(self):
-        self.assertEqual(self.zero.denominator, 1)
-        self.assertEqual(self.answer.denominator, 1)
-        self.assertEqual(self.pi.denominator, 7)
-        with self.assertRaises(ValueError):
-            self.nan.denominator
-        with self.assertRaises(ValueError):
-            self.inf.denominator
-
-    def test_bool(self):
-        self.assertFalse(self.zero)
-        self.assertTrue(self.answer)
-        self.assertTrue(self.pi)
-        self.assertEqual(bool(self.nan), bool(float('nan')))
-        self.assertEqual(bool(self.inf), bool(float('inf')))
-
-    def test_lt(self):
-        self.assertTrue(self.neginf < self.zero)
-        self.assertTrue(self.zero < self.pi)
-        self.assertTrue(self.pi < self.answer)
-        self.assertTrue(self.answer < self.inf)
-        self.assertFalse(self.nan < self.answer)
-        self.assertFalse(self.nan < self.inf)
-        self.assertFalse(self.nan < self.neginf)
-        self.assertTrue(self.neginf < self.inf)
-
-    def test_le(self):
-        self.assertTrue(self.pi <= self.pi)
-        self.assertTrue(self.pi <= self.answer)
-        self.assertFalse(self.answer <= self.pi)
-
-    def test_gt(self):
-        self.assertFalse(self.pi > self.pi)
-        self.assertTrue(self.answer > self.pi)
-        self.assertFalse(self.pi > self.answer)
-
-    def test_ge(self):
-        self.assertTrue(self.pi >= self.pi)
-        self.assertTrue(self.answer >= self.pi)
-        self.assertFalse(self.pi >= self.answer)
-
-    def test_eq(self):
-        self.assertEqual(self.pi, self.pi)
-        self.assertEqual(self.inf, self.inf)
-        self.assertNotEqual(self.neginf, self.inf)
-        self.assertNotEqual(self.nan, self.nan)
-        self.assertEqual(self.zero, 0)
-        self.assertEqual(0, self.zero)
-        self.assertEqual(self.pi, Fraction(22, 7))
-        self.assertEqual(Fraction(22, 7), self.pi)
-        with self.assertRaises(TypeError):
-            self.zero == 0.
-
-    def test_ne(self):
-        self.assertTrue(self.pi != self.answer)
-        self.assertFalse(self.pi != self.pi)
-        self.assertTrue(self.neginf != self.inf)
-        self.assertTrue(self.nan != self.nan)
-
-    def test_abs(self):
-        self.assertEqual(abs(self.pi), self.pi)
-        self.assertEqual(abs(self.neginf), self.inf)
-        self.assertEqual(abs(-self.pi), self.pi)
-        self.assertTrue(abs(self.nan).is_nan())
-
-    def test_pos(self):
-        self.assertEqual(+self.pi, self.pi)
-
-    def test_neg(self):
-        self.assertEqual(-self.neginf, self.inf)
-        self.assertEqual(-(-self.pi), self.pi)
-
-    def test_floor(self):
-        self.assertEqual(floor(self.pi), Value(self.context, 3))
-        self.assertEqual(floor(-self.pi), Value(self.context, -4))
-        # not float behavior, but makes sense
-        self.assertEqual(floor(self.inf), self.inf)
-        self.assertTrue(floor(self.nan).is_nan())
-
-    def test_ceil(self):
-        self.assertEqual(ceil(self.pi), Value(self.context, 4))
-        self.assertRaises(ceil(-self.pi) == Value(self.context, -3))
-
-    def test_trunc(self):
-        self.assertEqual(trunc(self.pi), Value(self.context, 3))
-        self.assertEqual(trunc(-self.pi), Value(self.context, -3))
-
-    def test_add(self):
-        self.assertEqual(self.answer + self.answer, Value(self.context, 84))
-        self.assertEqual(self.answer + self.pi, Value(self.context, 316, 7))
-        self.assertEqual(self.pi + self.pi, Value(self.context, 44, 7))
-        self.assertEqual(self.pi + self.neginf, self.neginf)
-        self.assertEqual(self.pi + self.inf, self.inf)
-        self.assertTrue((self.pi + self.nan).is_nan())
-        self.assertTrue((self.inf + self.nan).is_nan())
-        self.assertTrue((self.inf + self.neginf).is_nan())
-        self.assertEqual(self.pi + 42, Value(self.context, 316, 7))
-        self.assertEqual(42 + self.pi, Value(self.context, 316, 7))
-        self.assertEqual(self.pi + Fraction(22, 7), Value(self.context, 44, 7))
-        with self.assertRaises(TypeError):
-            self.pi + float(42)
-
-    def test_sub(self):
-        self.assertEqual(self.answer - self.pi, Value(self.context, 272, 7))
-
-    def test_mul(self):
-        self.assertEqual(Value(self.context, 6) * Value(self.context, 7), self.answer)
-        self.assertNotEqual(Value(self.context, 6) * Value(self.context, 9), self.answer)
-        self.assertEqual(self.inf * Value(self.context, 2), self.inf)
-        self.assertEqual(self.inf * Value(self.context, -2), self.neginf)
-        self.assertTrue((self.nan * Value(self.context, 2)).is_nan())
-        self.assertTrue((self.nan * self.inf).is_nan())
-
-    def test_div(self):
-        self.assertEqual(Value(self.context, 22) / Value(self.context, 7), self.pi)
-        self.assertEqual(self.pi / self.pi, Value(self.context, 1))
-        # not float behavior, but makes sense
-        self.assertTrue((self.pi / self.zero).is_nan())
-
-    def test_float(self):
-        self.assertAlmostEqual(float(Value(self.context, 1, 2)), 0.5)
-        self.assertTrue(math.isnan(float(Value(self.context, 'NaN'))))
-        self.assertAlmostEqual(float(Value(self.context, 'Inf')), float('inf'))
-
-    def test_is_finite(self):
-        self.assertTrue(self.pi.is_finite())
-        self.assertFalse(self.inf.is_finite())
-        self.assertFalse(self.nan.is_finite())
-
-    def test_is_infinite(self):
-        self.assertFalse(self.pi.is_infinite())
-        self.assertTrue(self.inf.is_infinite())
-        self.assertFalse(self.nan.is_infinite())
-
-    def test_is_nan(self):
-        self.assertFalse(self.pi.is_nan())
-        self.assertFalse(self.inf.is_nan())
-        self.assertTrue(self.nan.is_nan())
-
-    def test_str(self):
-        self.assertEqual(str(self.answer), '42')
-        self.assertEqual(str(self.pi), '22/7')
-        self.assertEqual(str(self.nan), 'NaN')
-        self.assertEqual(str(self.inf), 'Infinity')
-        self.assertEqual(str(self.neginf), '-Infinity')
-
-    def test_repr(self):
-        self.assertEqual(repr(self.answer), "Value('42')")
-        self.assertEqual(repr(self.pi), "Value('22/7')")
-        self.assertEqual(repr(self.nan), "Value('NaN')")
-        self.assertEqual(repr(self.inf), "Value('Infinity')")
-        self.assertEqual(repr(self.neginf), "Value('-Infinity')")
diff --git a/tests/test_linear.py b/tests/test_linear.py
deleted file mode 100644 (file)
index 4636275..0000000
+++ /dev/null
@@ -1,177 +0,0 @@
-
-import unittest
-
-from fractions import Fraction
-
-from pypol.linear import *
-
-
-class TestExpression(unittest.TestCase):
-
-    def setUp(self):
-        self.x = symbol('x')
-        self.y = symbol('y')
-        self.z = symbol('z')
-        self.zero = constant(0)
-        self.pi = constant(Fraction(22, 7))
-        self.e = self.x - 2*self.y + 3
-
-    def test_new(self):
-        pass
-
-    def test_symbols(self):
-        self.assertCountEqual(self.x.symbols(), ['x'])
-        self.assertCountEqual(self.pi.symbols(), [])
-        self.assertCountEqual(self.e.symbols(), ['x', 'y'])
-
-    def test_dimension(self):
-        self.assertEqual(self.x.dimension, 1)
-        self.assertEqual(self.pi.dimension, 0)
-        self.assertEqual(self.e.dimension, 2)
-
-    def test_coefficient(self):
-        self.assertEqual(self.e.coefficient('x'), 1)
-        self.assertEqual(self.e.coefficient('y'), -2)
-        self.assertEqual(self.e.coefficient(self.y), -2)
-        self.assertEqual(self.e.coefficient('z'), 0)
-        with self.assertRaises(TypeError):
-            self.e.coefficient(0)
-        with self.assertRaises(TypeError):
-            self.e.coefficient(self.e)
-
-    def test_getitem(self):
-        self.assertEqual(self.e['x'], 1)
-        self.assertEqual(self.e['y'], -2)
-        self.assertEqual(self.e[self.y], -2)
-        self.assertEqual(self.e['z'], 0)
-        with self.assertRaises(TypeError):
-            self.e[0]
-        with self.assertRaises(TypeError):
-            self.e[self.e]
-
-    def test_coefficients(self):
-        self.assertCountEqual(self.e.coefficients(), [('x', 1), ('y', -2)])
-
-    def test_constant(self):
-        self.assertEqual(self.x.constant, 0)
-        self.assertEqual(self.pi.constant, Fraction(22, 7))
-        self.assertEqual(self.e.constant, 3)
-
-    def test_isconstant(self):
-        self.assertFalse(self.x.isconstant())
-        self.assertTrue(self.pi.isconstant())
-        self.assertFalse(self.e.isconstant())
-
-    def test_values(self):
-        self.assertCountEqual(self.e.values(), [1, -2, 3])
-
-    def test_symbol(self):
-        self.assertEqual(self.x.symbol(), 'x')
-        with self.assertRaises(ValueError):
-            self.pi.symbol()
-        with self.assertRaises(ValueError):
-            self.e.symbol()
-
-    def test_issymbol(self):
-        self.assertTrue(self.x.issymbol())
-        self.assertFalse(self.pi.issymbol())
-        self.assertFalse(self.e.issymbol())
-
-    def test_bool(self):
-        self.assertTrue(self.x)
-        self.assertFalse(self.zero)
-        self.assertTrue(self.pi)
-        self.assertTrue(self.e)
-
-    def test_pos(self):
-        self.assertEqual(+self.e, self.e)
-
-    def test_neg(self):
-        self.assertEqual(-self.e, -self.x + 2*self.y - 3)
-
-    def test_add(self):
-        self.assertEqual(self.x + Fraction(22, 7), self.x + self.pi)
-        self.assertEqual(Fraction(22, 7) + self.x, self.x + self.pi)
-        self.assertEqual(self.x + self.x, 2 * self.x)
-        self.assertEqual(self.e + 2*self.y, self.x + 3)
-
-    def test_sub(self):
-        self.assertEqual(self.x - self.x, 0)
-        self.assertEqual(self.e - 3, self.x - 2*self.y)
-
-    def test_mul(self):
-        self.assertEqual(self.pi * 7, 22)
-        self.assertEqual(self.e * 0, 0)
-        self.assertEqual(self.e * 2, 2*self.x - 4*self.y + 6)
-
-    def test_div(self):
-        with self.assertRaises(ZeroDivisionError):
-            self.e / 0
-        self.assertEqual(self.e / 2, self.x / 2 - self.y + Fraction(3, 2))
-
-    def test_str(self):
-        self.assertEqual(str(Expression()), '0')
-        self.assertEqual(str(self.x), 'x')
-        self.assertEqual(str(-self.x), '-x')
-        self.assertEqual(str(self.pi), '22/7')
-        self.assertEqual(str(self.e), 'x - 2*y + 3')
-
-    def test_repr(self):
-        self.assertEqual(repr(self.e), "Expression({'x': 1, 'y': -2}, 3)")
-
-    @unittest.expectedFailure
-    def test_fromstring(self):
-        self.assertEqual(Expression.fromstring('x'), self.x)
-        self.assertEqual(Expression.fromstring('-x'), -self.x)
-        self.assertEqual(Expression.fromstring('22/7'), self.pi)
-        self.assertEqual(Expression.fromstring('x - 2y + 3'), self.e)
-        self.assertEqual(Expression.fromstring('x - (3-1)y + 3'), self.e)
-        self.assertEqual(Expression.fromstring('x - 2*y + 3'), self.e)
-
-    def test_eq(self):
-        self.assertEqual(self.e, self.e)
-        self.assertNotEqual(self.x, self.y)
-        self.assertEqual(self.zero, 0)
-
-    def test_canonify(self):
-        self.assertEqual((self.x + self.y/2 + self.z/3)._canonify(),
-                6*self.x + 3*self.y + 2*self.z)
-
-
-class TestHelpers(unittest.TestCase):
-
-    def setUp(self):
-        self.x = symbol('x')
-        self.y = symbol('y')
-
-    def test_constant(self):
-        self.assertEqual(constant(3), 3)
-        self.assertEqual(constant('3'), 3)
-        self.assertEqual(constant(Fraction(3, 4)), Fraction(3, 4))
-        self.assertEqual(constant('3/4'), Fraction(3, 4))
-        with self.assertRaises(ValueError):
-            constant('a')
-        with self.assertRaises(TypeError):
-            constant([])
-
-    def test_symbol(self):
-        self.assertEqual(symbol('x'), self.x)
-        self.assertNotEqual(symbol('y'), self.x)
-        with self.assertRaises(TypeError):
-            symbol(0)
-
-    def test_symbols(self):
-        self.assertListEqual(list(symbols('x y')), [self.x, self.y])
-        self.assertListEqual(list(symbols('x,y')), [self.x, self.y])
-        self.assertListEqual(list(symbols(['x', 'y'])), [self.x, self.y])
-
-
-class TestOperators(unittest.TestCase):
-
-    pass
-
-
-class TestPolyhedron(unittest.TestCase):
-
-    pass
-