Unitary tests for Polyhedron, very incomplete
[linpy.git] / pypol / isl.py
index f1e63dc..0b9a485 100644 (file)
-"""
-note: for islpy
-isl format: basic set: ("{[x, y] : x >= 0 and x < 5 and y >= 0 and y < x+4 }")
-"""
-
 import ctypes, ctypes.util
-import math
-import numbers
-import operator
-import re
-import functools
+
+from . import _isl
 
 
-from decimal import Decimal
-from fractions import Fraction
-from functools import wraps
+__all__ = [
+    'Context',
+    'BasicSet',
+]
+
 
 libisl = ctypes.CDLL(ctypes.util.find_library('isl'))
 
 libisl.isl_printer_get_str.restype = ctypes.c_char_p
+libisl.isl_dim_set = _isl.dim_set
 
-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:
+class IslObject:
 
-    __slots__ = ('_ic')
+    __slots__ = (
+        '_ptr',
+    )
 
-    def __init__(self):
-        self._ic = libisl.isl_ctx_alloc()
+    def __init__(self, ptr):
+        self._ptr = ptr
 
     @property
     def _as_parameter_(self):
-        return self._ic
-    
-    #comment out so does not delete itself after being created 
+        return self._ptr
+
+
+class Context(IslObject):
+
+    def __init__(self):
+        ptr = libisl.isl_ctx_alloc()
+        super().__init__(ptr)
+
+    #comment out so does not delete itself after being created
     #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)
+        return self._ptr == other._ptr
 
-    _RE_FRACTION = re.compile(r'^(?P<num>[-+]?\d+)(/(?P<den>\d+))?$')
 
-    __slots__ = ('context', '_iv', '_numerator', '_denominator')
+class BasicSet(IslObject):
 
-    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 symbols(self):
-        s = set()
-        for constraint in self.constraints():
-            s.update(constraint.symbols)
-        yield from sorted(s)
+    def __str__(self):
+        ls = libisl.isl_basic_set_get_local_space(self)
+        ctx = libisl.isl_local_space_get_ctx(ls)
+        p = libisl.isl_printer_to_str(ctx)
+        p = libisl.isl_printer_print_basic_set(p, self)
+        string = libisl.isl_printer_get_str(p).decode()
+        return string
 
     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'
+        libisl.isl_basic_set_free(self)
 
-    def __repr__(self):
-        return '{}({!r})'.format(self.__class__.__name__, str(self))
+    def constraints(self):
+        return _isl.basic_set_constraints(self._ptr)