[pypy-svn] r40891 - in pypy/dist/pypy/module/cclp: . constraint

auc at codespeak.net auc at codespeak.net
Wed Mar 21 12:19:51 CET 2007


Author: auc
Date: Wed Mar 21 12:19:50 2007
New Revision: 40891

Added:
   pypy/dist/pypy/module/cclp/app.py   (contents, props changed)
Modified:
   pypy/dist/pypy/module/cclp/__init__.py
   pypy/dist/pypy/module/cclp/constraint/__init__.py
   pypy/dist/pypy/module/cclp/constraint/constraint.py
   pypy/dist/pypy/module/cclp/constraint/domain.py
   pypy/dist/pypy/module/cclp/constraint/variable.py
   pypy/dist/pypy/module/cclp/cspace.py
   pypy/dist/pypy/module/cclp/interp_var.py
   pypy/dist/pypy/module/cclp/scheduler.py
   pypy/dist/pypy/module/cclp/thunk.py
   pypy/dist/pypy/module/cclp/types.py
   pypy/dist/pypy/module/cclp/variable.py
Log:
big cleanup


Modified: pypy/dist/pypy/module/cclp/__init__.py
==============================================================================
--- pypy/dist/pypy/module/cclp/__init__.py	(original)
+++ pypy/dist/pypy/module/cclp/__init__.py	Wed Mar 21 12:19:50 2007
@@ -7,6 +7,7 @@
     """
 
     appleveldefs = {
+        'make_expression':'app.make_expression'
     }
 
     interpleveldefs = {
@@ -21,12 +22,13 @@
         'reset_scheduler':'scheduler.reset_scheduler',
 
         'newspace':'cspace.newspace',
+        'dorkspace':'cspace.dorkspace',
         'choose':'cspace.choose',
         'tell':'cspace.tell',
 
-        'distribute':'constraint.distributor.distribute',
+        'distribute':'cspace.distribute',
 
-        'make_expression':'constraint.constraint.make_expression',
+        '_make_expression':'constraint.constraint._make_expression',
         'all_diff': 'constraint.constraint.make_alldistinct'
         
     }

Added: pypy/dist/pypy/module/cclp/app.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/module/cclp/app.py	Wed Mar 21 12:19:50 2007
@@ -0,0 +1,7 @@
+from cclp import _make_expression
+
+def make_expression(variables, formula):
+    func = 'lambda %s:%s' % (','.join([name_of(var)
+                                       for var in variables]),
+                             formula)
+    return _make_expression(variables, formula, eval(func))

Modified: pypy/dist/pypy/module/cclp/constraint/__init__.py
==============================================================================
--- pypy/dist/pypy/module/cclp/constraint/__init__.py	(original)
+++ pypy/dist/pypy/module/cclp/constraint/__init__.py	Wed Mar 21 12:19:50 2007
@@ -1 +1 @@
-# pass
+#

Modified: pypy/dist/pypy/module/cclp/constraint/constraint.py
==============================================================================
--- pypy/dist/pypy/module/cclp/constraint/constraint.py	(original)
+++ pypy/dist/pypy/module/cclp/constraint/constraint.py	Wed Mar 21 12:19:50 2007
@@ -1,320 +1,55 @@
-from pypy.rlib.objectmodel import we_are_translated
-from pypy.interpreter.error import OperationError
-from pypy.interpreter.baseobjspace import Wrappable
-from pypy.interpreter import baseobjspace, typedef, gateway
-from pypy.interpreter.gateway import interp2app
+from pypy.interpreter import baseobjspace
 from pypy.interpreter.function import Function
 
+from pypy.interpreter.error import OperationError
+
 from pypy.objspace.std.listobject import W_ListObject
 from pypy.objspace.std.stringobject import W_StringObject
-from pypy.objspace.std.dictobject import W_DictObject
-
-from pypy.module.cclp.types import W_Constraint, W_AbstractDomain, W_Root, \
-     W_Var, W_CVar as W_Variable
-
-from pypy.objspace.std.model import StdObjSpaceMultiMethod
-
-from pypy.module.cclp.constraint.btree import BTree
-#from pypy.objspace.constraint.util import sort
-
-all_mms = {}
-
-
-class W_AbstractConstraint(W_Constraint):
-    
-    def __init__(self, object_space, w_variables):
-        """variables is a list of variables which appear in the formula"""
-        W_Constraint.__init__(self, object_space)
-        assert isinstance(w_variables, W_ListObject)
-        assert self._space.is_true(self._space.ge(self._space.len(w_variables),
-                                                  self._space.newint(1)))
-        self._names_to_vars = {}
-        for var in w_variables.wrappeditems:
-            assert isinstance(var, W_Var)
-            self._names_to_vars[var.name_w()] = var
-        self._variables = w_variables.wrappeditems #unwrap once ...
-
-    def w_affected_variables(self):
-        """ Return a list of all variables affected by this constraint """
-        return self._space.newlist(self._variables)
 
-    def affected_variables(self):
-        return self._variables
+from pypy.module.cclp.types import W_CVar
 
-    def w_revise(self):
-        return self._space.newbool(self.revise())
-                
-W_AbstractConstraint.typedef = typedef.TypeDef(
-    "W_AbstractConstraint",
-    W_Constraint.typedef,                                           
-    affected_variables = interp2app(W_AbstractConstraint.w_affected_variables),
-    revise = interp2app(W_AbstractConstraint.w_revise)) 
-
-
-
-from pypy.module.__builtin__.compiling import eval as ev
-def make_filter__List_String(object_space, w_variables, w_formula):
-    """NOT RPYTHON"""
+def check_variables(space, w_variables, min_nb):
+    if not isinstance(w_variables, W_ListObject):
+        raise OperationError(space.w_TypeError,
+                             space.wrap("variables must be in a list or tuple."))
     assert isinstance(w_variables, W_ListObject)
-    assert isinstance(w_formula, W_StringObject)
-    items = object_space.unpackiterable(w_variables)
-    lst = []
-    for it in items:
-        assert isinstance(it, W_Variable)
-        lst.append(it.name_w())
-    var_ids = ','.join(lst) #[var.name_w()
-                        # for var in items]) 
-    func_head = 'lambda ' + var_ids + ':'
-    expr = func_head + object_space.str_w(w_formula)
-    func_obj = ev(object_space, object_space.wrap(expr), object_space.newdict(),
-                                 object_space.newdict())
-    assert isinstance(func_obj, Function)
-    return func_obj
-
-make_filter_mm = StdObjSpaceMultiMethod('make_filter', 2)
-make_filter_mm.register(make_filter__List_String, W_ListObject, W_StringObject)
-all_mms['make_filter'] = make_filter_mm
-
-class Quadruple(W_Root):
-    def __init__(self, zero, one, two, three):
-        self.zero = zero
-        self.one = one
-        self.two = two
-        self.three = three
-
-class W_Expression(W_AbstractConstraint):
-    """A constraint represented as a python expression."""
-
-    def __init__(self, object_space, w_variables, w_formula):
-        """variables is a list of variables which appear in the formula
-        formula is a python expression that will be evaluated as a boolean"""
-        W_AbstractConstraint.__init__(self, object_space, w_variables)
-        self.formula = self._space.str_w(w_formula)
-        # self.filter_func is a function taking keyword arguments and returning a boolean
-        self.filter_func = self._space.make_filter(w_variables, w_formula)
-
-    def copy(self):
-        if we_are_translated():
-            raise NotImplementedError
-        else:
-            newvars = [var.copy(self._space) for var in self._variables]
-            const = W_Expression(self._space, self._space.newlist(newvars), self._space.wrap(self.formula))
-            return const
-
-    def _init_result_cache(self):
-        """key = (variable,value), value = [has_success,has_failure]"""
-        result_cache = self._space.newdict()
-        for var in self._variables:
-            assert isinstance(var, W_Variable)
-            self._space.setitem(result_cache, var.w_name(), self._space.newdict())
-        return result_cache
-
-    def _assign_values(self):
-        kwargs = self._space.newdict()
-        variables = BTree()
-        for variable in self._variables:
-            assert isinstance(variable, W_Variable)
-            domain = variable.w_dom
-            assert isinstance(domain, W_AbstractDomain)
-            values = domain.get_values()
-            assert isinstance(values, list)
-            ds = domain.size()
-            assert isinstance(ds, int)
-            w_name = variable.w_name()
-            lval = len(values)
-            variables.add(ds, Quadruple(w_name, values, 0, lval))
-            # was meant to be:
-            #variables.append((domain.size(),
-            #                  [w_name, values, 0, len(values)]))
-            first_value = values[0]
-            assert isinstance(first_value, W_Root)
-            kwargs.content[variable.w_name()] = first_value
-        # get sorted variables to instanciate those with fewer possible values first
-        variables = variables.values()
-        self._assign_values_state = variables
-        return kwargs 
-        
-    def _next_value(self, kwargs):
-
-        # try to instanciate the next variable
-        variables = self._assign_values_state
+    if len(w_variables.wrappeditems) < min_nb:
+        raise OperationError(space.w_RuntimeError,
+                             space.wrap("there must be at least %s variables." % min_nb))
+    return w_variables
+
+def cvars_to_names(cvars):
+    variables = []
+    for w_var in cvars:
+        assert isinstance(w_var, W_CVar)
+        variables.append(w_var.w_nam)
+    return variables
 
-        for curr in variables:
-            assert isinstance(curr, Quadruple)
-            w_name = curr.zero
-            dom_values = curr.one
-            dom_index = curr.two
-            dom_len = curr.three
-            assert isinstance(w_name, W_StringObject)
-            assert isinstance(dom_values, list)
-            assert isinstance(dom_index, int)
-            assert isinstance(dom_len, int)
-            if dom_index < dom_len:
-                kwargs.content[w_name] = dom_values[dom_index]
-                curr.two = dom_index + 1
-                break
-            else:
-                curr.two = 0
-                kwargs.content[w_name] = dom_values[0]
-        else:
-            # it's over
-            raise StopIteration
-        return kwargs
 
-    def revise(self):
-        """generic propagation algorithm for n-ary expressions"""
-        sp = self._space
-        maybe_entailed = True
-        ffunc = self.filter_func
-        result_cache = self._init_result_cache()
-        assert isinstance(result_cache, W_DictObject)
+from pypy.module._cslib.constraint import interp_make_expression, \
+     make_alldistinct as mkalldiff
 
-        kwargs = self._assign_values()
-        assert isinstance(kwargs, W_DictObject)
-        while 1:
-            try:
-                kwargs = self._next_value(kwargs)
-                assert isinstance(kwargs, W_DictObject)
-            except StopIteration:
-                break
-            if maybe_entailed:
-                for varname, val in kwargs.content.iteritems():
-                    val_dict = result_cache.content[varname]
-                    assert isinstance(val_dict, W_DictObject)
-                    if val not in val_dict.content:
-                        break
-                else:
-                    continue
-            if sp.is_true(sp.call(sp.wrap(ffunc),
-                                  sp.newlist([]), kwargs)):
-                for var, val in kwargs.content.items():
-                    var_dict = result_cache.content[var]
-                    assert isinstance(var_dict, W_DictObject)
-                    var_dict.content[val] = sp.w_True
-            else:
-                maybe_entailed = False
-
-        try:
-            for varname, keep in result_cache.content.items():
-                var = self._names_to_vars[sp.str_w(varname)]
-                assert isinstance(var, W_Variable)
-                assert isinstance(keep, W_DictObject)
-                domain = var.w_dom
-                assert isinstance(domain, W_AbstractDomain)
-                domain.remove_values([val
-                                      for val in domain._values.content.keys()
-                                      if val not in keep.content])
-        except KeyError:
-            # There are no more value in result_cache
-            pass
-
-        return maybe_entailed
-        
-
-    def __repr__(self):
-        return '<%s>' % self.formula
-
-W_Expression.typedef = typedef.TypeDef("W_Expression",
-    W_AbstractConstraint.typedef,
-    revise = interp2app(W_Expression.w_revise))
-    
-
-
-def make_expression(space, w_variables, w_formula):
+def _make_expression(space, w_variables, w_formula, w_filter_func):
     """create a new constraint of type Expression or BinaryExpression
     The chosen class depends on the number of variables in the constraint"""
-    assert isinstance(w_variables, W_ListObject)
-    assert isinstance(w_formula, W_StringObject)
-    assert len(w_variables.wrappeditems) > 0
-    return W_Expression(space, w_variables, w_formula)
-make_expression.unwrap_spec = [baseobjspace.ObjSpace,
-                               baseobjspace.W_Root,
-                               baseobjspace.W_Root]
-
-
-class W_AllDistinct(W_AbstractConstraint):
-    """Contraint: all values must be distinct"""
-
-    def __init__(self, object_space, w_variables):
-        W_AbstractConstraint.__init__(self, object_space, w_variables)
-        # worst case complexity
-        #self.__cost = len(w_variables.wrappeditems) * (len(w_variables.wrappeditems) - 1) / 2
-
-    def copy(self):
-        if we_are_translated():
-            raise NotImplementedError
-        else:
-            newvars = [var.copy(self._space) for var in self._variables]
-            const = W_AllDistinct(self._space, self._space.newlist(newvars))
-            return const
-
-    def revise(self):
-        _spc = self._space
-
-        ord_vars = BTree()
-        for var in self._variables:
-            assert isinstance(var, W_Variable)
-            dom = var.w_dom
-            assert isinstance(dom, W_AbstractDomain)
-            sz = dom.size()
-            ord_vars.add(sz, var)
-        variables = ord_vars.values()
-        
-        # if a domain has a size of 1,
-        # then the value must be removed from the other domains
-        for var in variables:
-            assert isinstance(var, W_Variable)
-            dom = var.w_dom
-            assert isinstance(dom, W_AbstractDomain)
-            if dom.size() == 1:
-                #print "AllDistinct removes values"
-                for _var in variables:
-                    assert isinstance(_var, W_Variable)
-                    _dom = _var.w_dom
-                    assert isinstance(_dom, W_AbstractDomain)
-                    if not _var._same_as(var):
-                        try:
-                            _dom.remove_value(dom.get_values()[0])
-                        except KeyError, e:
-                            # we ignore errors caused by the removal of
-                            # non existing values
-                            pass
-
-        # if there are less values than variables, the constraint fails
-        values = {}
-        for var in variables:
-            assert isinstance(var, W_Variable)
-            dom = var.w_dom
-            assert isinstance(dom, W_AbstractDomain)
-            for val in dom.w_get_values().wrappeditems:
-                values[val] = 0
-
-        if len(values) < len(variables):
-            #print "AllDistinct failed"
-            raise OperationError(_spc.w_RuntimeError,
-                                 _spc.wrap("ConsistencyFailure"))
-
-        # the constraint is entailed if all domains have a size of 1
-        for var in variables:
-            assert isinstance(var, W_Variable)
-            dom = var.w_dom
-            assert isinstance(dom, W_AbstractDomain)
-            if not dom.size() == 1:
-                return False
-
-        # Question : did we *really* completely check
-        # our own alldistinctness predicate ?
-        #print "All distinct entailed"
-        return True
-
-W_AllDistinct.typedef = typedef.TypeDef(
-    "W_AllDistinct", W_AbstractConstraint.typedef,
-    revise = interp2app(W_AllDistinct.w_revise))
+    w_variables = check_variables(space, w_variables, 1)
+    assert isinstance(w_filter_func, Function)
+    if not isinstance(w_formula, W_StringObject):
+            raise OperationError(space.w_TypeError,
+                                 space.wrap("formula must be a string."))
+    variables = cvars_to_names(w_variables.wrappeditems)
+    return interp_make_expression(space, space.newlist(variables),
+                                  w_formula, w_filter_func)
+    
+_make_expression.unwrap_spec = [baseobjspace.ObjSpace,
+                                baseobjspace.W_Root,
+                                baseobjspace.W_Root,
+                                baseobjspace.W_Root]
+
+def make_alldistinct(space, w_variables):
+    w_variables = check_variables(space, w_variables, 2)
+    variables = cvars_to_names(w_variables.wrappeditems)
+    return mkalldiff(space, space.newlist(variables))
 
-#function bolted into the space to serve as constructor
-def make_alldistinct(object_space, w_variables):
-    assert isinstance(w_variables, W_ListObject)
-    assert len(w_variables.wrappeditems) > 0
-    return object_space.wrap(W_AllDistinct(object_space, w_variables))
 make_alldistinct.unwrap_spec = [baseobjspace.ObjSpace,
-                               baseobjspace.W_Root]
+                                baseobjspace.W_Root]

Modified: pypy/dist/pypy/module/cclp/constraint/domain.py
==============================================================================
--- pypy/dist/pypy/module/cclp/constraint/domain.py	(original)
+++ pypy/dist/pypy/module/cclp/constraint/domain.py	Wed Mar 21 12:19:50 2007
@@ -1,165 +1,31 @@
-from pypy.interpreter.error import OperationError
-
-from pypy.interpreter import typedef, gateway, baseobjspace
-from pypy.interpreter.gateway import interp2app
-
-from pypy.objspace.std.listobject import W_ListObject, W_TupleObject
-from pypy.objspace.std.intobject import W_IntObject
-
-from pypy.objspace.std.model import StdObjSpaceMultiMethod
-
-from pypy.module.cclp.types import W_AbstractDomain, W_Var, W_Root, ConsistencyError
+from pypy.module.cclp.types import W_Var
 from pypy.module.cclp.interp_var import interp_bind, interp_free
 
-all_mms = {}
+from pypy.module._cslib import fd
 
 
-class W_FiniteDomain(W_AbstractDomain):
+class _DorkFiniteDomain(fd._FiniteDomain):
     """
-    Variable Domain with a finite set of possible values
+    this variant accomodates synchronization needs
+    of the dorkspace
     """
 
-    def __init__(self, space, w_values):
-        """values is a list of values in the domain
-        This class uses a dictionnary to make sure that there are
-        no duplicate values"""
-        W_AbstractDomain.__init__(self, space)
-        #XXX a pure dict used to work there (esp. in revise)
-        assert isinstance(w_values, W_ListObject)
-        self._values = space.newdict()
-        self.set_values(w_values)
-
-    def copy(self):
-        return W_FiniteDomain(self._space, self.w_get_values())
+    def __init__(self, space, w_values, values):
+        fd._FiniteDomain.__init__(self, w_values, values)
+        self.space = space
+        self._changevar = W_Var(space)
 
     def clear_change(self):
         "create a fresh change synchonizer"
-        assert not interp_free(self._changed)
-        self._changed = W_Var(self._space)
-
-    def give_synchronizer(self):
-        return self._changed
-
-
-    def contains(self, w_val):
-        sp = self._space
-        assert isinstance(w_val, W_Root)
-        return sp.is_true(sp.contains(self._values, w_val))
-    __contains__ = contains
+        assert not interp_free(self._changevar)
+        self._changevar = W_Var(self.space)
 
+    def one_shot_watcher(self):
+        return self._changevar
+    
     def _value_removed(self):
-        "The implementation of remove_value should call this method"
-        #atomic
-        interp_bind(self._changed, self._space.w_True)
+        fd._FiniteDomain._value_removed(self)
+        interp_bind(self._changevar, self.space.w_True)
         self.clear_change()
-        #/atomic
-        
-        if self.size() == 0:
-            raise ConsistencyError, "tried to make a domain empty"
-
-    def set_values(self, w_values):
-        """XXX Objects in the value set can't be unwrapped unless we
-        specialize on specific types - this will need specialization
-        of revise & friends
-        """
-        for w_v in w_values.wrappeditems:
-            self._space.setitem(self._values, w_v, self._space.w_True)
-
-    def w_remove_value(self, w_value):
-        try:
-            self.remove_value(w_value)
-        except ConsistencyError:
-            raise OperationError(self._space.w_ConsistencyError,
-                                 self._space.wrap("tried to empty a domain"))
-        
-    def remove_value(self, w_value):
-        """Remove value of domain and check for consistency"""
-        assert isinstance(w_value, baseobjspace.W_Root)
-        self._space.delitem(self._values, w_value)
-        self._value_removed()
-
-    def w_remove_values(self, w_values):
-        """Remove values of domain and check for consistency"""
-        assert isinstance(w_values, W_ListObject)
-        try:
-            self.remove_values(w_values.wrappeditems)
-        except KeyError:
-            raise OperationError(self._space.w_RuntimeError,
-                                 self._space.wrap("attempt to remove unkown value from domain"))
-
-    def remove_values(self, values):
-        assert isinstance(values, list)
-        if len(values) > 0:
-            for w_val in values:
-                self._space.delitem(self._values,w_val)
-            self._value_removed()
-
-    def w_size(self):
-        return self._space.newint(self.size())
-
-    def size(self):
-        """computes the size of a finite domain"""
-        l = self._space.len(self._values)
-        assert isinstance(l, W_IntObject)
-        return l.intval
-    __len__ = size
-    
-    def w_get_values(self):
-        """return all the values in the domain
-           in an indexable sequence"""
-        return self._space.newlist(self.get_values())
-
-    def get_values(self):
-        return [x for x in self._space.unpackiterable(self._values)]
-        
-    def __repr__(self):
-        return '<FD %s>' % str(self.w_get_values())
-
-    def __eq__(self, w_other):
-        if not isinstance(w_other, W_FiniteDomain):
-            return self._space.newbool(False)
-        return self._space.newbool(self._space.eq_w(self._values, w_other._values))
-            
-    def __ne__(self, w_other):
-        if not isinstance(w_other, W_FiniteDomain):
-            return self._space.newbool(True)
-        if self._space.eq_w(self._values, w_other._values):
-            return self._space.newbool(False)
-        return self._space.newbool(True)
-
-
-
 
-# function bolted into the space to serve as constructor
-def make_fd(space, w_values):
-    assert isinstance(w_values, W_ListObject)
-    return W_FiniteDomain(space, w_values)
-app_make_fd = gateway.interp2app(make_fd)
-
-
-def intersection(space, w_fd1, w_fd2):
-    return space.intersection(w_fd1, w_fd2)
-app_intersection = gateway.interp2app(intersection)
-
-
-def intersection__FiniteDomain_FiniteDomain(space, w_fd1, w_fd2):
-    w_v1 = space.unpackiterable(w_fd1._values)
-    res = [w_v for w_v in space.unpackiterable(w_fd2._values)
-             if w_v in w_v1]
-    return make_fd(space, space.newlist(res))
-
-intersection_mm = StdObjSpaceMultiMethod('intersection', 2)
-intersection_mm.register(intersection__FiniteDomain_FiniteDomain,
-                         W_FiniteDomain, W_FiniteDomain)
-all_mms['intersection'] = intersection_mm
-
-W_FiniteDomain.typedef = typedef.TypeDef(
-    "W_FiniteDomain",
-    W_AbstractDomain.typedef,
-    remove_value = interp2app(W_FiniteDomain.w_remove_value),
-    remove_values = interp2app(W_FiniteDomain.w_remove_values),
-    get_values = interp2app(W_FiniteDomain.w_get_values),
-    __eq__ = interp2app(W_FiniteDomain.__eq__),
-    __ne__ = interp2app(W_FiniteDomain.__ne__),
-    size = interp2app(W_FiniteDomain.w_size))
 

Modified: pypy/dist/pypy/module/cclp/constraint/variable.py
==============================================================================
--- pypy/dist/pypy/module/cclp/constraint/variable.py	(original)
+++ pypy/dist/pypy/module/cclp/constraint/variable.py	Wed Mar 21 12:19:50 2007
@@ -2,21 +2,22 @@
 from pypy.objspace.std.listobject import W_ListObject
 from pypy.objspace.std.stringobject import W_StringObject
 
-from pypy.module.cclp.constraint.domain import W_FiniteDomain
-
 from pypy.module.cclp.types import deref, W_Var, W_CVar
 from pypy.module.cclp.variable import bind_mm, raise_unification_failure, _alias, \
      _assign_aliases, bind__Var_Root, bind__Var_Var
 from pypy.module.cclp.misc import w
 
+from pypy.module._cslib import fd
+
+
 W_Root = baseobjspace.W_Root
 
 def domain(space, w_values, w_name):
     assert isinstance(w_values, W_ListObject)
     assert isinstance(w_name, W_StringObject)
-    w_dom = W_FiniteDomain(space, w_values)
+    w_dom = fd.W_FiniteDomain(w_values, None)
     w_var = W_CVar(space, w_dom, w_name)
-    #w("CVAR", str(w_var))
+    w("CVAR", str(w_var))
     return w_var
 app_domain = gateway.interp2app(domain)
 
@@ -24,20 +25,24 @@
 def bind__CVar_Root(space, w_cvar, w_obj):
     #XXX we should (want to) be able to test membership
     #    in a wrapped against wrappeds into a non-wrapped dict
-    if [True for elt in space.unpackiterable(w_cvar.w_dom._values)
+    if [True for elt in w_cvar.w_dom.domain.get_wvalues_in_rlist()
         if space.is_true(space.eq(w_obj, elt))]:
         return bind__Var_Root(space, w_cvar, w_obj)
     raise_unification_failure(space, "value not in variable domain")
 
 def bind__CVar_CVar(space, w_cvar1, w_cvar2):
-    w_inter_dom = space.intersection(w_cvar1.w_dom, w_cvar2.w_dom)
-    if w_inter_dom.__len__() > 0:
-        if w_inter_dom.__len__() == 1:
-            w_value = w_inter_dom.get_values()[0]
+    d1 = w_cvar1.w_dom.domain
+    d2 = w_cvar2.w_dom.domain
+    dinter = d1.intersect(d2)
+    if dinter.size() > 0:
+        if dinter.size() == 1:
+            w_value = dinter.get_wvalues_in_rlist()[0]
             _assign_aliases(space, w_cvar1, w_value)
             _assign_aliases(space, w_cvar2, w_value)
         else:
-            w_cvar1.w_dom = w_cvar2.w_dom = w_inter_dom
+            w_interdom = fd.W_FiniteDomain(space.newlist([]), None)
+            w_interdom.domain = dinter
+            w_cvar1.w_dom = w_cvar2.w_dom = w_interdom
             _alias(space, w_cvar1, w_cvar2)
     else:
         raise_unification_failure(space, "incompatible domains")

Modified: pypy/dist/pypy/module/cclp/cspace.py
==============================================================================
--- pypy/dist/pypy/module/cclp/cspace.py	(original)
+++ pypy/dist/pypy/module/cclp/cspace.py	Wed Mar 21 12:19:50 2007
@@ -1,51 +1,82 @@
 from pypy.rlib.objectmodel import we_are_translated
 from pypy.interpreter import baseobjspace, gateway, argument, typedef
-from pypy.interpreter.function import Function
-from pypy.interpreter.pycode import PyCode
 
 from pypy.interpreter.error import OperationError
 
 from pypy.objspace.std.intobject import W_IntObject
 from pypy.objspace.std.listobject import W_ListObject, W_TupleObject
+from pypy.objspace.std.stringobject import W_StringObject
 
-from pypy.module._stackless.interp_coroutine import AbstractThunk
-
-from pypy.module.cclp.misc import get_current_cspace, w
+from pypy.module.cclp.misc import get_current_cspace, w, v
 from pypy.module.cclp.thunk import CSpaceThunk, PropagatorThunk
 from pypy.module.cclp.global_state import sched
 from pypy.module.cclp.variable import newvar
-from pypy.module.cclp.types import ConsistencyError, Solution, W_Var, \
-     W_CVar, W_AbstractDomain, W_AbstractDistributor, SpaceCoroutine
+from pypy.module.cclp.types import FailedSpace, ConsistencyError, W_Var, W_CVar
 from pypy.module.cclp.interp_var import interp_bind, interp_free
-from pypy.module.cclp.constraint.distributor import distribute
 from pypy.module.cclp.scheduler import W_ThreadGroupScheduler
-from pypy.module._stackless.clonable import AppClonableCoroutine
-from pypy.module._stackless.coroutine import AppCoroutine
-from pypy.rlib.rgc import gc_clone
-
+from pypy.module._cslib import fd
+from pypy.rlib.cslib import rdistributor as rd
 
+from pypy.module._stackless.coroutine import AppCoroutine
 
+import pypy.rlib.rgc as rgc
 
+def gc_swap_pool(pool):
+    if we_are_translated():
+        return rgc.gc_swap_pool(pool)
+
+def gc_clone(data, pool):
+    if we_are_translated():
+        return rgc.gc_clone(data, pool)
+        
 def newspace(space, w_callable, __args__):
     "application level creation of a new computation space"
     args = __args__.normalize()
-    dist_thread = SpaceCoroutine(space)
+    # allocate in a new pool
+    saved_pool = gc_swap_pool(None)
+    dist_thread = AppCoroutine(space)
     thunk = CSpaceThunk(space, w_callable, args, dist_thread)
     dist_thread.bind(thunk)
-    dist_thread.hello_local_pool()
-    try:
-        w_space = W_CSpace(space, dist_thread)
-    finally:
-        dist_thread.goodbye_local_pool()
+    w_space = W_CSpace(space, dist_thread, saved_pool)
+    w_space.goodbye_local_pool()
+    # /allocate
     if not we_are_translated():
-        w("NEWSPACE, (distributor) thread",
-          str(id(dist_thread)), "for", str(w_callable.name))
+        w("NEWSPACE, (distributor) thread %d for %s" %
+          ( id(dist_thread), str(w_callable.name) ) )
     return w_space
+
 newspace.unwrap_spec=[baseobjspace.ObjSpace,
                       baseobjspace.W_Root,
                       argument.Arguments]
 
+
+def dorkspace(space, w_callable, __args__):
+    "application level creation of a new complicated computation space"
+    args = __args__.normalize()
+    dist_thread = AppCoroutine(space)
+    thunk = CSpaceThunk(space, w_callable, args, dist_thread)
+    dist_thread.bind(thunk)
+
+    saved_pool = gc_swap_pool(None)
+    try:
+        w_space = W_ComplicatedSpace(space, dist_thread, saved_pool)
+        w_space.goodbye_local_pool()
+    except:
+        gc_swap_pool(saved_pool)
+        raise OperationError(space.w_RuntimeError,
+                             space.wrap("Unknown error in newspace"))
+    if not we_are_translated():
+        w("NEWSPACE, (distributor) thread %d for %s" %
+          ( id(dist_thread), str(w_callable.name) ) )
+    return w_space
+
+dorkspace.unwrap_spec=[baseobjspace.ObjSpace,
+                       baseobjspace.W_Root,
+                       argument.Arguments]
+
+
 def choose(space, w_n):
+    "non deterministic choice from within a c.space"
     if not isinstance(w_n, W_IntObject):
         raise OperationError(space.w_TypeError,
                              space.wrap('choose only accepts an integer.'))
@@ -64,33 +95,94 @@
                              space.wrap("this space is finished"))
     try:
         return cspace.choose(n)
-    except ConsistencyError:
-        raise OperationError(space.w_ConsistencyError,
-                             space.wrap("this space is failed"))
+    except Exception, e:
+        if not we_are_translated():
+            import traceback
+            traceback.print_exc()
+        w('whack whack whack')
+        raise OperationError(space.w_RuntimeError,
+                             space.wrap("something wacky happened %s" % e))
 choose.unwrap_spec = [baseobjspace.ObjSpace,
                       baseobjspace.W_Root]
 
-from pypy.module.cclp.constraint import constraint
+from pypy.module._cslib.constraint import W_AbstractConstraint
 
 def tell(space, w_constraint):
-    if not isinstance(w_constraint, constraint.W_AbstractConstraint):
+    "adding a constraint to a c.space (from within)"
+    if not isinstance(w_constraint, W_AbstractConstraint):
         raise OperationError(space.w_TypeError,
                              space.wrap('Tell only accepts object of '
                                         '(sub-)types Constraint.'))
-    get_current_cspace(space).tell(w_constraint)
+    get_current_cspace(space).tell(w_constraint.constraint)
 tell.unwrap_spec = [baseobjspace.ObjSpace,
                     baseobjspace.W_Root]
 
 
+def distribute(space, w_strategy):
+    assert isinstance(w_strategy, W_StringObject)
+    strat = space.str_w(w_strategy)
+    cspace = get_current_cspace(space)
+    # constraint distributor thread main loop
+    cspace.distribute(strat)
+distribute.unwrap_spec = [baseobjspace.ObjSpace,
+                          baseobjspace.W_Root]
+
+
+# base space
+# non concurrent propagators
+# hence much less weird synchronization stuff
+# a specific pool object
+# XXX maybe use a descr_method__new__ to create the pool before allocation
 class W_CSpace(W_ThreadGroupScheduler):
+    local_pool = None
 
-    def __init__(self, space, dist_thread):
+    def dump(self):
+        w('-- DUMPing C.Space data --')
+        w(':local pool %s' % id(self.local_pool))
+        w(':saved pool %s' % id(self.saved_pool))
+        v(':threads :')
+        curr = stop = self._head
+        while 1:
+            v('%s ' % id(curr))
+            curr = curr._next
+            if curr == stop:
+                break
+        w('')
+        v(':blocked :')
+        for th in self._blocked.keys():
+            v('%s ' % id(th))
+        w('')
+        w(':blocked_on')
+        for var, thl in self._blocked_on.items():
+            v('  var %s : ' % id(var))
+            for th in thl:
+                v('%s ' % id(th))
+            w('')
+        w(':blocked_byneed')
+        for var, thl in self._blocked_byneed.items():
+            v('  var %s : ' % id(var))
+            for th in thl:
+                v('%s ' % id(th))
+            w('')
+        w(':traced vars')
+        for th, varl in self._traced.items():
+            v('  thread %s : ' % id(th))
+            for var in varl:
+                v('%s ' % id(var))
+            w('')
+        w('-- /DUMP --')
+    
+    def __init__(self, space, dist_thread, saved_pool):
         W_ThreadGroupScheduler.__init__(self, space)
+        # pool
+        self.local_pool = None
+        self.saved_pool = saved_pool
+        # /pool
+        # thread ring
         dist_thread._cspace = self
         self._init_head(dist_thread)
-        self.main_thread = dist_thread
+        # /ring
         sched.uler.add_new_group(self)
-        self.distributor = None # dist instance != thread
         # choice mgmt
         self._choices = newvar(space)
         self._committed = newvar(space)
@@ -100,89 +192,277 @@
         self._merged = False
         self._finished = newvar(space)
         # constraint store ...
-        self._store = {} # name -> var
-
-##     def hello(self):
-##         self.main_thread.hello_local_pool()
-
-##     def goodbye(self):
-##         self.main_thread.goodbye_local_pool()
-
-    def register_var(self, cvar):
-        self._store[cvar.name] = cvar
+        self._constraints = []
+        self._domains = {} # varname -> domain
+        self._variables = [] # varnames
+        self._varconst = {} # varname -> constraints
+        self._cqueue = [] # constraint queue to be processed 
+
+    #-- POOL & cloning stuff
+
+    def hello_local_pool(self):
+        if we_are_translated():
+            self.saved_pool = gc_swap_pool(self.local_pool)
+
+    def goodbye_local_pool(self):
+        if we_are_translated():
+            self.local_pool = gc_swap_pool(self.saved_pool)
+            self.saved_pool = None
 
     def w_clone(self):
-        cl_thread = self.main_thread._clone()
-        cl_thread._cspace.finalize_cloning( self, cl_thread )
-        cl_thread._cspace.main_thread = cl_thread
-        sched.uler.add_new_group(cl_thread._cspace)
-        return self.space.wrap(cl_thread._cspace)
+        # all this stuff is created in the local pool so that
+        # gc_clone can clone it. every object we want to clone
+        # must be reachable through objects allocated in this
+        # local pool via the data tuple.
+        self.report_bad_condition_to_applevel()
+        head = curr = self._head
+        new_shells = []
+        # within new POOL
+        self.hello_local_pool()
+        coroutines_to_clone = []
+        while 1:
+            coroutines_to_clone.append((curr, curr.frame, curr.subctx))
+            self.goodbye_local_pool()
+            # outside new POOL, build new fresh coro shells
+            new = AppCoroutine(self.space, state = curr.costate)
+            new.parent = curr.parent
+            new_shells.append(new)
+            self.hello_local_pool()
+            # /outside
+            curr = curr._next
+            if curr == head:
+                break
+        data = (self, coroutines_to_clone)
+        # /within new POOL
+        self.goodbye_local_pool()
+        
+        (copy, copied_coros), copypool = gc_clone(data, self.local_pool)
+        copy.local_pool = copypool
+        copy.finalize_cloning(copied_coros, new_shells)
+        sched.uler.add_new_group(copy)
+        self.dump()
+        copy.dump()
+        return self.space.wrap(copy)
 
-    def finalize_cloning(self, orig_cspace, cl_thread ):
+    def finalize_cloning(self, copied_coros, new_shells):
         # We need to walk all threads references from this cloned
         # space and replace
-        # 1. our cloned thread get's a new thread ID
-        orig_tid = orig_cspace.main_thread.tid
-        sched.uler.register_thread( cl_thread )
-        self.main_thread = cl_thread
-        # walk the thread ring buffer to replace the cloned threads
-        cl_thread._cspace._init_head( cl_thread ) # XXX enough for now with only one thread
-        self.replace_thread( orig_tid, cl_thread )
+        # 1. our cloned thread gets a new thread ID
+        w('finalize cloning in c.space %s' % id(self))
+        self._head = None
+        for i in range(len(copied_coros)):
+            coro, cloned_frame, cloned_subctx = copied_coros[i]
+            # bolt cloned stuff on new coro shells
+            cloned_coro = new_shells[i]
+            cloned_coro.frame = cloned_frame
+            cloned_coro.subctx = cloned_subctx
+            cloned_coro._cspace = self
+            cloned_coro.thunk = coro.thunk
+            self.replace_thread(coro, cloned_coro)
+
+    def replace_thread(self, old, new):
+        # walk the list of _blocked threads:
+        if old in self._blocked.keys():
+            w('blocked : %s replaced %s' % (id(new), id(old)))
+            del self._blocked[old]
+            self._blocked[new] = True
+
+        # walk the mappings var->threads
+        for w_var in self._blocked_on:
+            threads = self._blocked_on[w_var]
+            for k in range(len(threads)):
+                if threads[k] is old:
+                    w('blocked_on : %s replaced %s' % (id(new), id(old)))
+                    threads[k] = new
+                    
+        for w_var in self._blocked_byneed:
+            threads = self._blocked_byneed[w_var]
+            for k in range(len(threads)):
+                if threads[k] is old:
+                    w('blocked_byneed : %s replaced %s' % (id(new), id(old)))
+                    threads[k] = new
+
+        # handled traced thread
+        for th in self._traced.keys():
+            if th is old:
+                lvars = self._traced[th]
+                del self._traced[th]
+                self._traced[new] = lvars
+
+        # insert the thread in the linked list
+        if self._head is None:
+            w('head was initialized with %s' % id(new))
+            self._head = new._next = new._prev = new
+        else:
+            w('%s was inserted in the runqueue' % id(new))
+            r = self._head
+            l = r._prev
+            l._next = new
+            r._prev = new
+            new._prev = l
+            new._next = r
+            assert new._next is not new
+            assert new._prev is not new
+
+    def _newvar(self):
+        """
+        ensure a new space-local variable is allocated
+        in the right space/pool
+        """
+        self.hello_local_pool()
+        var = newvar(self.space)
+        self.goodbye_local_pool()
+        return var
 
-    def report_bad_state_to_applevel(self):
+    #-- / POOL 
+
+    def register_var(self, cvar):
+        name = cvar.name
+        dom = cvar.w_dom.domain
+        self._domains[name] = dom
+        self._varconst[name] = []
+
+    def tell(self, rconst):
+        w('telling %s' % rconst)
+        self._constraints.append(rconst)
+        for var in rconst._variables:
+            self._varconst[var].append(rconst)
+
+    def untell(self, constraint):
+        "entailed constraint are allowed to go away"
+        self._constraints.remove(constraint)
+        for var in constraint._variables:
+            self._varconst[var].remove(constraint)
+
+    def distributable(self):
+        for dom in self._domains.values():
+            if dom.size() > 1:
+                return True
+        return False        
+
+    def distribute(self, strat):
+        w('SP:start constraint propagation & distribution loop')
+        space = self.space
+        if strat == 'dichotomy':
+            dist = rd.DichotomyDistributor()
+        elif strat == 'allornothing':
+            dist = rd.AllOrNothingDistributor()
+        else:
+            raise OperationError(space.w_RuntimeError,
+                                 space.wrap("please pick a strategy in "
+                                            "(allornothing, dichotomy)."))
+        # initialize constraint queue
+        self._cqueue = [(constr.estimate_cost(self._domains), constr)
+                        for constr in self._constraints]
+        self.wait_stable() # hmm
+        self.propagate()
+        while self.distributable():
+            w('SP:distribute loop')
+            w_choice = self.choose(2) # yes, two, always, all the time
+            choice = space.int_w(w_choice)
+            small_dom_var = dist.find_smallest_domain(self._domains)
+            dom = self._domains[small_dom_var]
+            dist._distribute_on_choice(dom, choice)
+            for constraint in self._varconst[small_dom_var]:
+                self._cqueue.append((0, constraint)) # *uck the cost
+            dom._changed = False
+            self.propagate()
+
+    def propagate(self): # XXX pasted from rlib.cslib.rpropagation, mixin me
+        """Prunes the domains of the variables
+        This method calls constraint.narrow() and queues constraints
+        that are affected by recent changes in the domains.
+        Returns True if a solution was found"""
+
+        # XXX : _queue.sort()
+        w('SP:propagating')
+        _queue = self._cqueue
+        _affected_constraints = {}
+        while True:
+            if not _queue:
+                # refill the queue if some constraints have been affected
+                _queue = [(constr.estimate_cost(self._domains), constr)
+                          for constr in _affected_constraints]
+                if not _queue:
+                    break
+                # XXX _queue.sort()
+                _affected_constraints.clear()
+            cost, constraint = _queue.pop(0)
+            entailed = constraint.revise(self._domains)
+            for var in constraint._variables:
+                # affected constraints are listeners of
+                # affected variables of this constraint
+                dom = self._domains[var]
+                if not dom._changed: # XXX
+                    continue
+                for constr in self._varconst[var]:
+                    if constr is not constraint:
+                        _affected_constraints[constr] = True
+                dom._changed = False
+            if entailed:
+                self.untell(constraint)
+                if constraint in _affected_constraints:
+                    del _affected_constraints[constraint]
+                
+        for domain in self._domains.values():
+            if domain.size() != 1:
+                return 0
+        return 1
+
+    #-- Public ops
+
+    def report_bad_condition_to_applevel(self):
+        """
+        a couple of checks for methods on spaces
+        but forbidden within
+        """
+        currspace = get_current_cspace(self.space)
+        if currspace is self:
+            raise OperationError(self.space.w_RuntimeError,
+                                 self.space.wrap("you can't do this operation"
+                                                 "on the current computation space"))
         if not interp_free(self._finished):
             raise OperationError(self.space.w_RuntimeError,
                                  self.space.wrap("space is finished"))
 
+
     def w_ask(self):
         try:
-            self.report_bad_state_to_applevel()
+            self.report_bad_condition_to_applevel()
         except: # we're dead, let's avoid wait_stable ...
-            return self._choices
+            return self.space.wrap(self._last_choice)
         self.wait_stable()
         self.space.wait(self._choices)
         choices = self._choices.w_bound_to
-        self.main_thread.hello_local_pool()
-        self._choices = newvar(self.space)
-        self.main_thread.goodbye_local_pool()
+        self._choices = self._newvar()
         assert isinstance(choices, W_IntObject)
         self._last_choice = self.space.int_w(choices)
         return choices
 
     def choose(self, n):
-        # solver probably asks
-        self.wait_stable()
-        if self._failed: #XXX set by any propagator while we were not looking
-            raise ConsistencyError
-        assert interp_free(self._choices) 
+        assert interp_free(self._choices)
         assert interp_free(self._committed)
         # XXX we wrap it a bit prematurely, so as to satisfy
-        # type requirements (logic variables do not support rpython types)
+        # type requirements (logic variables only accept W_Roots)
         interp_bind(self._choices, self.space.wrap(n)) # unblock the solver
         # now we wait on a solver commit
-        self.space.wait(self._committed)
+        self.space.wait(self._committed) 
         committed = self._committed.w_bound_to
         self._committed = newvar(self.space)
         return committed
 
     def w_commit(self, w_n):
-        self.report_bad_state_to_applevel()
-        assert isinstance(w_n, W_IntObject)
-        n = w_n.intval
+        self.report_bad_condition_to_applevel()
+        if not isinstance(w_n, W_IntObject):
+            raise OperationError(self.space.w_TypeError,
+                                 self.space.wrap('commit accepts only ints.'))
+        n = self.space.int_w(w_n)
         assert interp_free(self._committed)
         if n < 1 or n > self._last_choice:
             raise OperationError(self.space.w_ValueError,
-                                 self.space.wrap("need 0<commit<=%d" % self._last_choice))
-        interp_bind(self._committed, w_n)
-
-    def tell(self, w_constraint):
-        space = self.space
-        w_coro = AppCoroutine(space)
-        w_coro._next = w_coro._prev = w_coro
-        w_coro._cspace = self
-        thunk = PropagatorThunk(space, w_constraint, w_coro)
-        w_coro.bind(thunk)
-        self.add_new_thread(w_coro)
+                                 self.space.wrap("need 0<commit<=%d" %
+                                                 self._last_choice))
+        interp_bind(self._committed, w_n) 
 
     def fail(self):
         self._failed = True
@@ -197,10 +477,9 @@
         return self._failed
 
     def w_merge(self):
-        # let's bind the solution variables
         if self._merged:
-            raise OperationError(self.space.w_RuntimeError,
-                                 self.space.wrap("space is already merged"))
+            return self._solution
+        # let's bind the solution variables
         self._merged = True
         sol = self._solution.w_bound_to
         if isinstance(sol, W_ListObject):
@@ -209,6 +488,8 @@
             self._bind_solution_variables(sol.wrappeditems)
         return self._solution
 
+    #-- / Public ops
+
     def __ne__(self, other):
         if other is self:
             return False
@@ -218,11 +499,10 @@
         if contains_cvar(solution): # was a constraint script
             for var in solution:
                 assert isinstance(var, W_CVar)
-                realvar = self._store[var.name]
-                dom = realvar.w_dom
-                assert isinstance(dom, W_AbstractDomain)
+                dom = self._domains[var.name]
+                assert isinstance(dom, fd._FiniteDomain)
                 assert dom.size() == 1
-                interp_bind(var, dom.get_values()[0])
+                interp_bind(var, dom.get_wvalues_in_rlist()[0])
 
 
 def contains_cvar(lst):
@@ -235,3 +515,91 @@
     clone = gateway.interp2app(W_CSpace.w_clone),
     merge = gateway.interp2app(W_CSpace.w_merge),
     fail = gateway.interp2app(W_CSpace.w_fail))
+
+
+import pypy.rlib.cslib.rdistributor as rd
+from pypy.module.cclp.constraint.domain import _DorkFiniteDomain
+
+
+class W_ComplicatedSpace(W_CSpace):
+    """
+    a space with concurrent propagators inside
+    it performs poorly, is needlessly complicated
+    the author should be shot
+    """
+
+    def __init__(self, space, dist_thread, saved_pool):
+        W_ThreadGroupScheduler.__init__(self, space)
+        # thread ring
+        dist_thread._cspace = self
+        self._init_head(dist_thread)
+        # /ring
+        # pool
+        self.local_pool = None
+        self.saved_pool = saved_pool
+        # /pool
+        sched.uler.add_new_group(self)
+        self.dist = None # dist instance != thread
+        # choice mgmt
+        self._choices = newvar(space)
+        self._committed = newvar(space)
+        # status, merging
+        self._solution = newvar(space)
+        self._failed = False
+        self._merged = False
+        self._finished = newvar(space)
+        # constraint store ...
+        self._store = {} # name -> var
+        self._domains = {} # varname -> domain
+
+
+    def register_var(self, cvar):
+        name = cvar.name
+        self._store[name] = cvar
+        # let's switch to dork-style finite domains
+        basic_dom = cvar.w_dom.domain
+        dom = _DorkFiniteDomain(self.space,
+                                basic_dom.vlist,
+                                basic_dom._values)
+        cvar.w_dom.domain = dom
+        self._domains[name] = dom
+        
+    def distribute(self, strat):
+        space = self.space
+        if strat == 'dichotomy':
+            dist = rd.DichotomyDistributor()
+        elif strat == 'allornothing':
+            dist = rd.AllOrNothingDistributor()
+        else:
+            raise OperationError(space.w_RuntimeError,
+                                 space.wrap("please pick a strategy in "
+                                            "(allornothing, dichotomy)."))
+        self.wait_stable()
+        while self.distributable():
+            w_choice = self.choose(2)
+            choice = space.int_w(w_choice)
+            small_dom_var = dist.find_smallest_domain(self._domains)
+            dom = self._domains[small_dom_var]
+            w('ABOUT TO DISTRIBUTE')
+            dist._distribute_on_choice(dom, choice)
+            self.wait_stable()
+
+    def tell(self, constraint):
+        space = self.space
+        w_coro = AppCoroutine(space)
+        w_coro._next = w_coro._prev = w_coro
+        w_coro._cspace = self
+        thunk = PropagatorThunk(space, constraint, w_coro)
+        w_coro.bind(thunk)
+        self.add_new_thread(w_coro)
+
+    
+    def _bind_solution_variables(self, solution):
+        if contains_cvar(solution): # was a constraint script
+            for var in solution:
+                assert isinstance(var, W_CVar)
+                realvar = self._store[var.name]
+                dom = realvar.w_dom.domain
+                assert isinstance(dom, fd._FiniteDomain)
+                assert dom.size() == 1
+                interp_bind(var, dom.get_wvalues_in_rlist()[0])

Modified: pypy/dist/pypy/module/cclp/interp_var.py
==============================================================================
--- pypy/dist/pypy/module/cclp/interp_var.py	(original)
+++ pypy/dist/pypy/module/cclp/interp_var.py	Wed Mar 21 12:19:50 2007
@@ -69,7 +69,7 @@
 def _assign(w_var, w_val):
     assert isinstance(w_var, W_Var)
     if isinstance(w_var, W_CVar):
-        if not w_var.w_dom.contains(w_val):
+        if not w_val in w_var.w_dom.domain.vlist:
             raise ValueError, "assignment out of domain"
     w_var.w_bound_to = w_val
 

Modified: pypy/dist/pypy/module/cclp/scheduler.py
==============================================================================
--- pypy/dist/pypy/module/cclp/scheduler.py	(original)
+++ pypy/dist/pypy/module/cclp/scheduler.py	Wed Mar 21 12:19:50 2007
@@ -9,7 +9,6 @@
 
 #-- Singleton scheduler ------------------------------------------------
 
-AppCoroutine.tid = 0
 AppCoroutine._cspace = None
 
 class TopLevelScheduler(object):
@@ -26,25 +25,6 @@
         # asking for stability
         self._asking = {} # cspace -> thread set
         self._asking[top_level_space] = {} # XXX
-        # variables suspension lists
-        self._tl_blocked = {} # set of spaces
-        self._tl_blocked_on = {} # var -> spaces
-        self._tl_blocked_byneed = {} # var -> spaces
-        self.next_tid = 1 # 0 is reserved for untracked threads
-        self.threads = {} # id->thread mapping
-
-    def get_new_tid(self):
-        # XXX buggy if it overflows
-        tid = self.next_tid
-        self.next_tid += 1
-        return tid
-
-    def register_thread(self, thread):
-        if thread.tid==0:
-            thread.tid = self.get_new_tid()
-            self.threads[thread.tid] = thread
-##         else:
-##             assert thread.tid in self.threads, "Thread already registered"
     
     def _chain_insert(self, group):
         assert isinstance(group, W_ThreadGroupScheduler), "type error"
@@ -61,14 +41,11 @@
     def schedule(self):
         running = self._head
         to_be_run = self._select_next()
-        if not isinstance(to_be_run, W_ThreadGroupScheduler):
-            w("Aaarg something wrong in top level schedule")
-            assert False, "type error"
         #w(".. SWITCHING (spaces)", str(id(get_current_cspace(self.space))), "=>", str(id(to_be_run)))
         self._switch_count += 1
         if to_be_run != running:
-            running.goodbye()
-            to_be_run.hello()
+            running.goodbye_local_pool()
+            to_be_run.hello_local_pool()
         to_be_run.schedule()
 
     def _select_next(self):
@@ -79,7 +56,6 @@
             if to_be_run.is_runnable():
                 break
             to_be_run = to_be_run._next
-            assert isinstance(to_be_run, W_ThreadGroupScheduler), "type error"
             if to_be_run == sentinel:
                 reset_scheduler(self.space)
                 w(".. SCHEDULER reinitialized")
@@ -245,12 +221,13 @@
 
     def __init__(self, space):
         self.space = space
-        self._pool = None
         self._switch_count = 0
         self._traced = {} # thread -> vars
         self.thread_count = 1
         self.blocked_count = 0
+        # head thread
         self._head = None
+        # thread group ring
         self._next = self
         self._prev = self
         # accounting for blocked stuff
@@ -260,40 +237,9 @@
 
     def _init_head(self, thread):
         "sets the initial ring head"
-        assert isinstance(thread, AppCoroutine), "type error"
         self._head = thread
         thread._next = thread._prev = thread
-        sched.uler.register_thread( thread )
-        w("HEAD (main) THREAD = ", str(id(self._head)))
-
-
-    def replace_thread( self, orig_tid, cl_thread ):
-        # walk the list of _blocked threads:
-        for th in self._blocked:
-            if th.tid == orig_tid:
-                del self._blocked[th]
-                self._blocked[cl_thread] = True
-
-        # walk the mappings var->threads
-        for w_var in self._blocked_on:
-            threads = self._blocked_on[w_var]
-            for k in range(len(threads)):
-                if threads[k].tid == orig_tid:
-                    threads[k] = cl_thread
-                    
-        for w_var in self._blocked_byneed:
-            threads = self._blocked_byneed[w_var]
-            for k in range(len(threads)):
-                if threads[k].tid == orig_tid:
-                    threads[k] = cl_thread
-
-        # handled traced thread
-        for th in self._traced.keys():
-            if th.tid == orig_tid:
-                lvars = self._traced[th]
-                del self._traced[th]
-                self._traced[cl_thread] = lvars
-            
+        w("HEAD (main) THREAD = ", str(id(self._head)))            
 
     def _chain_insert(self, thread):
         assert isinstance(thread, AppCoroutine), "type error"
@@ -305,12 +251,11 @@
         r._prev = thread
         thread._prev = l
         thread._next = r
-        sched.uler.register_thread( thread )
 
-    def hello(self):
+    def hello_local_pool(self):
         pass
 
-    def goodbye(self):
+    def goodbye_local_pool(self):
         pass
 
     def register_var(self, var):
@@ -352,9 +297,7 @@
             asking[self] = {curr:True}
         curr._cspace._blocked[curr] = True  #XXX semantics please ?
         curr._cspace.blocked_count += 1
-        w("About to reschedule")
         sched.uler.schedule()
-        w("Resuming %d" % id(self) )
 
     def schedule(self):
         to_be_run = self._select_next()
@@ -449,14 +392,15 @@
             for th in sched.uler._asking[home].keys():
                 # these asking threads must be unblocked, in their
                 # respective home spaces
-                # was: del sched.uler._blocked[th]
-                v(' ', str(id(th)))
+                v(str(id(th)))
                 del th._cspace._blocked[th]
                 th._cspace.blocked_count -= 1
             w('')
             sched.uler._asking[home] = {}
 
-
+    def distribute(self, dist):
+        raise OperationError(self.space.w_RuntimeError,
+                             self.space.wrap("You can't distribute a top-level space."))
 
     def add_new_thread(self, thread):
         "insert 'thread' at end of running queue"
@@ -493,7 +437,6 @@
         assert isinstance(thread, AppCoroutine), "type error"
         assert isinstance(lvars, list), "type error"
         #w(".. TRACING logic vars.", str(lvars), "for", str(id(thread)))
-        #assert not self._traced.has_key(thread) doesn't translate 
         self._traced[thread] = lvars
 
     def dirty_traced_vars(self, thread, failed_value):

Modified: pypy/dist/pypy/module/cclp/thunk.py
==============================================================================
--- pypy/dist/pypy/module/cclp/thunk.py	(original)
+++ pypy/dist/pypy/module/cclp/thunk.py	Wed Mar 21 12:19:50 2007
@@ -1,12 +1,11 @@
 from pypy.module._stackless.coroutine import _AppThunk, AppCoroutine
 from pypy.module._stackless.interp_coroutine import AbstractThunk
 
-from pypy.module.cclp.misc import w
+from pypy.module.cclp.misc import w, get_current_cspace
 from pypy.module.cclp.global_state import sched
 from pypy.module.cclp.types import W_Var, W_CVar, W_Future, W_FailedValue, \
-     ConsistencyError, Solution, W_AbstractDomain, SpaceCoroutine
-from pypy.module.cclp.interp_var import interp_wait, interp_entail, \
-     interp_bind, interp_free, interp_wait_or
+     ConsistencyError, FailedSpace, Solution
+from pypy.module.cclp.interp_var import interp_bind, interp_free, interp_wait_or
 
 from pypy.objspace.std.listobject import W_ListObject
 from pypy.objspace.std.listobject import W_TupleObject
@@ -40,6 +39,9 @@
                 _AppThunk.call(self)
             except Exception, exc:
                 w(".! exceptional EXIT of procedure", str(id(self._coro)), "with", str(exc))
+                if not we_are_translated():
+                    import traceback
+                    traceback.print_exc()
                 sched.uler.dirty_traced_vars(self._coro, W_FailedValue(exc))
             else:
                 w(".! clean EXIT of procedure", str(id(self._coro)))
@@ -83,7 +85,7 @@
     def call(self):
         space = self.space
         coro = AppCoroutine.w_getcurrent(space)
-        assert isinstance(coro, SpaceCoroutine)
+        assert isinstance(coro, AppCoroutine)
         cspace = coro._cspace
         w("-- initial DISTRIBUTOR thunk CALL in", str(id(coro)))
         sched.uler.trace_vars(coro, logic_args(self.args.unpack()))
@@ -93,15 +95,20 @@
                     _AppThunk.call(self)
                 finally:
                     coro = AppCoroutine.w_getcurrent(space)
-                    assert isinstance(coro, SpaceCoroutine)
+                    assert isinstance(coro, AppCoroutine)
                     cspace = coro._cspace
-            except ConsistencyError, exc:
-                w("-- EXIT of DISTRIBUTOR, space is FAILED", str(id(coro)), "with", str(exc))
+            except FailedSpace, exc:
+                w("-- EXIT of DISTRIBUTOR %s, space is FAILED with %s" % (id(coro),
+                                                                          str(exc)))
                 failed_value = W_FailedValue(exc)
                 interp_bind(cspace._solution, failed_value)
             except Exception, exc:
                 # maybe app_level let something buble up ...
-                w("-- exceptional EXIT of DISTRIBUTOR", str(id(coro)), "with", str(exc))
+                w("-- exceptional EXIT of DISTRIBUTOR %s with %s" % (id(coro),
+                                                                     str(exc)))
+                if not we_are_translated():
+                    import traceback
+                    traceback.print_exc()
                 failed_value = W_FailedValue(exc)
                 sched.uler.dirty_traced_vars(coro, failed_value)
                 interp_bind(cspace._solution, failed_value)
@@ -125,7 +132,6 @@
             sched.uler.remove_thread(coro)
             sched.uler.schedule()
 
-
 class PropagatorThunk(AbstractThunk):
     def __init__(self, space, w_constraint, coro):
         self.space = space
@@ -135,26 +141,27 @@
     def call(self):
         #coro = AppCoroutine.w_getcurrent(self.space)
         try:
+            space = self.space
             cspace = self.coro._cspace
+            const = self.const
             try:
                 while 1:
-                    entailed = self.const.revise()
+                    if not interp_free(cspace._finished):
+                        break
+                    entailed = const.revise(cspace._domains)
                     if entailed:
                         break
                     # we will block on domains being pruned
                     wait_list = []
-                    _vars = self.const._variables
-                    assert isinstance(_vars, list)
-                    for var in _vars:
-                        assert isinstance(var, W_CVar)
-                        dom = var.w_dom
-                        assert isinstance(dom, W_AbstractDomain)
-                        wait_list.append(dom.give_synchronizer())
+                    _doms = [cspace._domains[var]
+                             for var in const._variables]
+                    for dom in _doms:
+                        #assert isinstance(dom, W_AbstractDomain)
+                        wait_list.append(dom.one_shot_watcher())
                     #or the cspace being dead
                     wait_list.append(cspace._finished)
-                    interp_wait_or(self.space, wait_list)
-                    if not interp_free(cspace._finished):
-                        break
+                    interp_wait_or(space, wait_list)
+                    cspace = get_current_cspace(space) # might have been cloned
             except ConsistencyError:
                 cspace.fail()
             except Exception: # rpython doesn't like just except:\n ...

Modified: pypy/dist/pypy/module/cclp/types.py
==============================================================================
--- pypy/dist/pypy/module/cclp/types.py	(original)
+++ pypy/dist/pypy/module/cclp/types.py	Wed Mar 21 12:19:50 2007
@@ -40,9 +40,11 @@
         w("FUT", str(w_self))
 
 
+from pypy.module._cslib import fd
+
 class W_CVar(W_Var):
     def __init__(self, space, w_dom, w_name):
-        assert isinstance(w_dom, W_AbstractDomain)
+        assert isinstance(w_dom, fd.W_FiniteDomain)
         W_Var.__init__(self, space)
         self.w_dom = w_dom
         self.name = space.str_w(w_name)
@@ -58,6 +60,12 @@
     def w_name(self):
         return self.w_nam
 
+    def assign(self, w_var):
+        if not w_var.w_dom.contains(w_val):
+            raise ValueError, "assignment out of domain"
+        w_var.w_bound_to = w_val
+
+
 def domain_of(space, w_v):
     if not isinstance(w_v, W_CVar):
         raise OperationError(space.w_TypeError,
@@ -85,95 +93,42 @@
 
 class Solution(Exception): pass
 
-#-- Constraint ---------------------------------------------
+class FailedSpace(Exception): pass
 
-class W_Constraint(baseobjspace.Wrappable):
-    def __init__(self, object_space):
-        self._space = object_space
-
-W_Constraint.typedef = typedef.TypeDef("W_Constraint")
-
-class W_AbstractDomain(baseobjspace.Wrappable):
-    """Implements the functionnality related to the changed flag.
-    Can be used as a starting point for concrete domains"""
-
-    def __init__(self, space):
-        self._space = space
-        self._changed = W_Var(self._space)
-
-    def give_synchronizer(self):
-        pass
-
-    def get_values(self):
-        pass
-
-    def remove_values(self, values):
-        assert isinstance(values, list)
-        
-    def size(self):
-        pass
-
-W_AbstractDomain.typedef = typedef.TypeDef("W_AbstractDomain")
-
-class W_AbstractDistributor(baseobjspace.Wrappable):
-
-    def __init__(self, space, fanout):
-        assert isinstance(fanout, int)
-        self._space = space
-        self._fanout = fanout
-        self._cspace = get_current_cspace(space)
-
-W_AbstractDistributor.typedef = typedef.TypeDef("W_AbstractDistributor")
-
-#-- Space Coroutine ----------------------
-
-
-class SpaceCoroutine(AppClonableCoroutine):
-    def __init__(self, space, state=None):
-        AppClonableCoroutine.__init__(self, space, state)
-        self._cspace = None
-        self._next = self._prev = None
-
-    def _clone(self):
-        if not we_are_translated():
-            raise NotImplementedError
-
-        space = self.space
-        costate = self.costate
-        if costate.current is self:
-            raise OperationError(space.w_RuntimeError,
-                                 space.wrap("clone() cannot clone the "
-                                            "current coroutine"
-                                            "; use fork() instead"))
-        copy = SpaceCoroutine(space, state=costate)
-
-        # This part is a copy of InterpClonableMixin.clone_into
-        # we can't use it because extradata as a different signature
-        # see the comments there for explanations
-        # ----------------------------------------------------------
-        copy.parent = self.parent
-        self.hello_local_pool()
-        data = (self.frame, self.subctx, self._cspace)
-        self.goodbye_local_pool()
-        # clone!
-        data, copy.local_pool = gc_clone(data, self.local_pool)
-        copy.frame, copy.subctx, copy._cspace = data
-        return copy
-
-    #XXX idea for future :
-    #    only call AppCoroutine.hello() there
-    #    do main_thread.hello|goodbye_local_pool when switching spaces
-    #    we will need to clone the other coros stackframes and
-    #    restuff these into fresh AppCoroutine shells
-    #    (because AppCoros have finalizers, hence are not cloned)
-    def hello(self):
-        w('Hello coro %d' % id(self) )
-        AppClonableCoroutine.hello(self)
-
-    def goodbye(self):
-        w('Bye coro %d' % id(self))
-        AppClonableCoroutine.goodbye(self)
+#-- Ring (used by scheduling entities)
 
+class RingMixin(object):
+    _mixin_ = True
+    """
+    useless till we can give a type parameter
+    """
+    
+    def init_head(self, head):
+        self._head = head
+        head._next = head._prev = head
+        self._count = 1
+
+    def chain_insert(self, obj):
+        r = self._head
+        l = r._prev
+        l._next = obj
+        r._prev = obj
+        obj._prev = l
+        obj._next = r
+
+    def remove(self, obj):
+        l = obj._prev
+        r = obj._next
+        l._next = r
+        r._prev = l
+        if self._head == obj:
+            self._head = r
+        if r == obj:
+            # that means obj was the last one
+            # the group is about to die
+            self._head = None
+        obj._next = obj._prev = None
+          
 
 #-- Misc ---------------------------------------------------
 

Modified: pypy/dist/pypy/module/cclp/variable.py
==============================================================================
--- pypy/dist/pypy/module/cclp/variable.py	(original)
+++ pypy/dist/pypy/module/cclp/variable.py	Wed Mar 21 12:19:50 2007
@@ -289,7 +289,7 @@
 def _assign(space, w_var, w_val):
     assert isinstance(w_var, W_Var)
     if isinstance(w_var, W_CVar):
-        if not w_val in space.unpackiterable(w_var.w_dom._values):
+        if not w_val in w_var.w_dom.domain.get_wvalues_in_rlist():
             raise_unification_failure(space, "assignment out of domain")
     w_var.w_bound_to = w_val
 



More information about the Pypy-commit mailing list