diff --git a/pystencils/data_types.py b/pystencils/data_types.py
index 5d553006bcca4c1cccbddb681d8710a92b75c437..37b4f9479635f7661528c15275c59a4b81e89310 100644
--- a/pystencils/data_types.py
+++ b/pystencils/data_types.py
@@ -7,6 +7,7 @@ import numpy as np
 import sympy as sp
 import sympy.codegen.ast
 from sympy.core.cache import cacheit
+from sympy.core.logic import fuzzy_not, fuzzy_or
 from sympy.logic.boolalg import Boolean, BooleanFunction
 
 import pystencils
@@ -20,6 +21,105 @@ except ImportError as e:
     _ir_importerror = e
 
 
+class Type(sp.Basic):
+    is_Atom = True
+
+    def __new__(cls, *args, **kwargs):
+        return sp.Basic.__new__(cls)
+
+    def _sympystr(self, *args, **kwargs):
+        return str(self)
+
+
+class BasicType(Type):
+    @staticmethod
+    def numpy_name_to_c(name):
+        if name == 'float64':
+            return 'double'
+        elif name == 'float32':
+            return 'float'
+        elif name == 'complex64':
+            return 'ComplexFloat'
+        elif name == 'complex128':
+            return 'ComplexDouble'
+        elif name.startswith('int'):
+            width = int(name[len("int"):])
+            return "int%d_t" % (width,)
+        elif name.startswith('uint'):
+            width = int(name[len("uint"):])
+            return "uint%d_t" % (width,)
+        elif name == 'bool':
+            return 'bool'
+        else:
+            raise NotImplementedError("Can map numpy to C name for %s" % (name,))
+
+    def __init__(self, dtype, const=False):
+        self.const = const
+        if isinstance(dtype, Type):
+            self._dtype = dtype.numpy_dtype
+        else:
+            self._dtype = np.dtype(dtype)
+        assert self._dtype.fields is None, "Tried to initialize NativeType with a structured type"
+        assert self._dtype.hasobject is False
+        assert self._dtype.subdtype is None
+
+    def __getnewargs__(self):
+        return self.numpy_dtype, self.const
+
+    @property
+    def base_type(self):
+        return None
+
+    @property
+    def numpy_dtype(self):
+        return self._dtype
+
+    @property
+    def sympy_dtype(self):
+        return getattr(sympy.codegen.ast, str(self.numpy_dtype))
+
+    @property
+    def item_size(self):
+        return 1
+
+    def is_int(self):
+        return self.numpy_dtype in np.sctypes['int'] or self.numpy_dtype in np.sctypes['uint']
+
+    def is_float(self):
+        return self.numpy_dtype in np.sctypes['float']
+
+    def is_uint(self):
+        return self.numpy_dtype in np.sctypes['uint']
+
+    def is_complex(self):
+        return self.numpy_dtype in np.sctypes['complex']
+
+    def is_other(self):
+        return self.numpy_dtype in np.sctypes['others']
+
+    @property
+    def base_name(self):
+        return BasicType.numpy_name_to_c(str(self._dtype))
+
+    def __str__(self):
+        result = BasicType.numpy_name_to_c(str(self._dtype))
+        if self.const:
+            result += " const"
+        return result
+
+    def __repr__(self):
+        return str(self)
+
+    def __eq__(self, other):
+        if not isinstance(other, BasicType):
+            return False
+        else:
+            return (self.numpy_dtype, self.const) == (other.numpy_dtype, other.const)
+
+    def __hash__(self):
+        return hash(str(self))
+
+
 def typed_symbols(names, dtype, *args):
     symbols = sp.symbols(names, *args)
     if isinstance(symbols, Tuple):
@@ -144,48 +244,48 @@ class cast_func(sp.Function):
 
     @property
     def is_integer(self):
-        """
-        Uses Numpy type hierarchy to determine :func:`sympy.Expr.is_integer` predicate
-
-        For reference: Numpy type hierarchy https://docs.scipy.org/doc/numpy-1.13.0/reference/arrays.scalars.html
-        """
-        if hasattr(self.dtype, 'numpy_dtype'):
-            return np.issubdtype(self.dtype.numpy_dtype, np.integer) or super().is_integer
-        else:
-            return super().is_integer
+        try:
+            dtype_assumptions = assumptions_from_dtype(self.args[1].numpy_dtype)
+        except Exception:
+            dtype_assumptions = {}
+        # we do not want a float to be integer even if it numerically is
+        return dtype_assumptions.get('integer', None)
 
     @property
     def is_negative(self):
-        """
-        See :func:`.TypedSymbol.is_integer`
-        """
-        if hasattr(self.dtype, 'numpy_dtype'):
-            if np.issubdtype(self.dtype.numpy_dtype, np.unsignedinteger):
-                return False
-
-        return super().is_negative
+        try:
+            dtype_assumptions = assumptions_from_dtype(self.args[1].numpy_dtype)
+        except Exception:
+            dtype_assumptions = {}
+        return fuzzy_or((self.args[0].is_negative, dtype_assumptions.get('negative', None)))
 
     @property
     def is_nonnegative(self):
-        """
-        See :func:`.TypedSymbol.is_integer`
-        """
-        if self.is_negative is False:
-            return True
-        else:
-            return super().is_nonnegative
+        try:
+            dtype_assumptions = assumptions_from_dtype(self.args[1].numpy_dtype)
+        except Exception:
+            dtype_assumptions = {}
+        return fuzzy_or((self.args[0].is_nonnegative, fuzzy_not(dtype_assumptions.get('negative', None))))
 
     @property
     def is_real(self):
-        """
-        See :func:`.TypedSymbol.is_integer`
-        """
-        if hasattr(self.dtype, 'numpy_dtype'):
-            return np.issubdtype(self.dtype.numpy_dtype, np.integer) or \
-                np.issubdtype(self.dtype.numpy_dtype, np.floating) or \
-                super().is_real
-        else:
-            return super().is_real
+        try:
+            dtype_assumptions = assumptions_from_dtype(self.args[1].numpy_dtype)
+        except Exception:
+            dtype_assumptions = {}
+        return fuzzy_or((self.args[0].is_real, dtype_assumptions.get('real', None)))
+
+    @property
+    def is_extended_real(self):
+        try:
+            dtype_assumptions = assumptions_from_dtype(self.args[1].numpy_dtype)
+        except Exception:
+            dtype_assumptions = {}
+        return fuzzy_or((self.args[0].is_extended_real, dtype_assumptions.get('real', None)))
+
+    @property
+    def is_imaginary(self):
+        return self.args[0].is_imaginary
 
 
 # noinspection PyPep8Naming
@@ -214,7 +314,27 @@ class pointer_arithmetic_func(sp.Function, Boolean):
             raise NotImplementedError()
 
 
+def create_type(specification):
+    """Creates a subclass of Type according to a string or an object of subclass Type.
+
+    Args:
+        specification: Type object, or a string
+
+    Returns:
+        Type object, or a new Type object parsed from the string
+    """
+    if isinstance(specification, Type):
+        return specification
+    else:
+        numpy_dtype = np.dtype(specification)
+        if numpy_dtype.fields is None:
+            return BasicType(numpy_dtype, const=False)
+        else:
+            return StructType(numpy_dtype, const=False)
+
+
 class TypedSymbol(sp.Symbol):
+
     def __new__(cls, *args, **kwds):
         obj = TypedSymbol.__xnew_cached_(cls, *args, **kwds)
         return obj
@@ -267,25 +387,6 @@ class TypedSymbol(sp.Symbol):
         return headers
 
 
-def create_type(specification):
-    """Creates a subclass of Type according to a string or an object of subclass Type.
-
-    Args:
-        specification: Type object, or a string
-
-    Returns:
-        Type object, or a new Type object parsed from the string
-    """
-    if isinstance(specification, Type):
-        return specification
-    else:
-        numpy_dtype = np.dtype(specification)
-        if numpy_dtype.fields is None:
-            return BasicType(numpy_dtype, const=False)
-        else:
-            return StructType(numpy_dtype, const=False)
-
-
 @memorycache(maxsize=64)
 def create_composite_type_from_string(specification):
     """Creates a new Type object from a c-like string specification.
@@ -575,105 +676,6 @@ def get_type_of_expression(expr,
     raise NotImplementedError("Could not determine type for", expr, type(expr))
 
 
-class Type(sp.Basic):
-    is_Atom = True
-
-    def __new__(cls, *args, **kwargs):
-        return sp.Basic.__new__(cls)
-
-    def _sympystr(self, *args, **kwargs):
-        return str(self)
-
-
-class BasicType(Type):
-    @staticmethod
-    def numpy_name_to_c(name):
-        if name == 'float64':
-            return 'double'
-        elif name == 'float32':
-            return 'float'
-        elif name == 'complex64':
-            return 'ComplexFloat'
-        elif name == 'complex128':
-            return 'ComplexDouble'
-        elif name.startswith('int'):
-            width = int(name[len("int"):])
-            return "int%d_t" % (width,)
-        elif name.startswith('uint'):
-            width = int(name[len("uint"):])
-            return "uint%d_t" % (width,)
-        elif name == 'bool':
-            return 'bool'
-        else:
-            raise NotImplementedError("Can map numpy to C name for %s" % (name,))
-
-    def __init__(self, dtype, const=False):
-        self.const = const
-        if isinstance(dtype, Type):
-            self._dtype = dtype.numpy_dtype
-        else:
-            self._dtype = np.dtype(dtype)
-        assert self._dtype.fields is None, "Tried to initialize NativeType with a structured type"
-        assert self._dtype.hasobject is False
-        assert self._dtype.subdtype is None
-
-    def __getnewargs__(self):
-        return self.numpy_dtype, self.const
-
-    @property
-    def base_type(self):
-        return None
-
-    @property
-    def numpy_dtype(self):
-        return self._dtype
-
-    @property
-    def sympy_dtype(self):
-        return getattr(sympy.codegen.ast, str(self.numpy_dtype))
-
-    @property
-    def item_size(self):
-        return 1
-
-    def is_int(self):
-        return self.numpy_dtype in np.sctypes['int'] or self.numpy_dtype in np.sctypes['uint']
-
-    def is_float(self):
-        return self.numpy_dtype in np.sctypes['float']
-
-    def is_uint(self):
-        return self.numpy_dtype in np.sctypes['uint']
-
-    def is_complex(self):
-        return self.numpy_dtype in np.sctypes['complex']
-
-    def is_other(self):
-        return self.numpy_dtype in np.sctypes['others']
-
-    @property
-    def base_name(self):
-        return BasicType.numpy_name_to_c(str(self._dtype))
-
-    def __str__(self):
-        result = BasicType.numpy_name_to_c(str(self._dtype))
-        if self.const:
-            result += " const"
-        return result
-
-    def __repr__(self):
-        return str(self)
-
-    def __eq__(self, other):
-        if not isinstance(other, BasicType):
-            return False
-        else:
-            return (self.numpy_dtype, self.const) == (other.numpy_dtype, other.const)
-
-    def __hash__(self):
-        return hash(str(self))
-
-
 class VectorType(Type):
     instruction_set = None