[pypy-svn] r31334 - in pypy/dist/pypy: lib module/_stackless objspace objspace/cclp objspace/cclp/constraint objspace/cclp/constraint/test objspace/test

auc at codespeak.net auc at codespeak.net
Wed Aug 16 11:29:30 CEST 2006


Author: auc
Date: Wed Aug 16 11:29:21 2006
New Revision: 31334

Added:
   pypy/dist/pypy/objspace/cclp/constraint/distributor.py
   pypy/dist/pypy/objspace/cclp/constraint/test/problem.py
Modified:
   pypy/dist/pypy/lib/_exceptions.py
   pypy/dist/pypy/module/_stackless/clonable.py
   pypy/dist/pypy/objspace/cclp/constraint/constraint.py
   pypy/dist/pypy/objspace/cclp/constraint/domain.py
   pypy/dist/pypy/objspace/cclp/constraint/test/test_constraint.py
   pypy/dist/pypy/objspace/cclp/interp_var.py
   pypy/dist/pypy/objspace/cclp/scheduler.py
   pypy/dist/pypy/objspace/cclp/space.py
   pypy/dist/pypy/objspace/cclp/thread.py
   pypy/dist/pypy/objspace/cclp/thunk.py
   pypy/dist/pypy/objspace/cclp/types.py
   pypy/dist/pypy/objspace/logic.py
   pypy/dist/pypy/objspace/test/test_logicobjspace.py
Log:
constraints distributor, interp version of var utilities ; some correctness issues to be fixed later


Modified: pypy/dist/pypy/lib/_exceptions.py
==============================================================================
--- pypy/dist/pypy/lib/_exceptions.py	(original)
+++ pypy/dist/pypy/lib/_exceptions.py	Wed Aug 16 11:29:21 2006
@@ -442,10 +442,14 @@
 #-- Logic object space specific stuff
 #XXX conditionalize me on '-o logic'
 
-class LOError(Exception): pass
+class LogicError(Exception): pass
 
-class UnificationError(LOError): pass
+class UnificationError(LogicError): pass
 class RebindingError(UnificationError): pass
-class FutureBindingError(LOError): pass
+class FutureBindingError(LogicError): pass
 
-class AllBlockedError(LOError): pass
+class AllBlockedError(LogicError): pass
+
+# constraints
+
+class ConsistencyError(LogicError): pass

Modified: pypy/dist/pypy/module/_stackless/clonable.py
==============================================================================
--- pypy/dist/pypy/module/_stackless/clonable.py	(original)
+++ pypy/dist/pypy/module/_stackless/clonable.py	Wed Aug 16 11:29:21 2006
@@ -32,7 +32,7 @@
              space.getexecutioncontext().subcontext_new(self)
         self._dead = False
         self._next = self._prev = self
-        self._cspace = space.w_None
+        self._cspace = None
 
     def hello(self):
         if we_are_translated():

Modified: pypy/dist/pypy/objspace/cclp/constraint/constraint.py
==============================================================================
--- pypy/dist/pypy/objspace/cclp/constraint/constraint.py	(original)
+++ pypy/dist/pypy/objspace/cclp/constraint/constraint.py	Wed Aug 16 11:29:21 2006
@@ -19,19 +19,6 @@
 all_mms = {}
 
 
-#-- Exceptions ---------------------------------------
-
-class ConsistencyFailure(Exception):
-    """The repository is not in a consistent state"""
-    pass
-
-class DomainlessVariables(Exception):
-    """A constraint can't be defined on variables
-       without a domain"""
-    pass
-
-#-- Constraints ------------------------------------------
-
 class W_AbstractConstraint(W_Constraint):
     
     def __init__(self, object_space, w_variables):
@@ -185,9 +172,6 @@
                 domain.remove_values([val
                                       for val in domain._values.content.keys()
                                       if val not in keep.content])
-        except ConsistencyFailure:
-            raise ConsistencyFailure('Inconsistency while applying %s' % \
-                                     repr(self))
         except KeyError:
             # There are no more value in result_cache
             pass
@@ -207,6 +191,8 @@
 def make_expression(o_space, w_variables, w_formula):
     """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(o_space, w_variables, w_formula)
 app_make_expression = gateway.interp2app(make_expression)
@@ -276,6 +262,7 @@
 
 # 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))
 app_make_alldistinct = gateway.interp2app(make_alldistinct)

Added: pypy/dist/pypy/objspace/cclp/constraint/distributor.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/objspace/cclp/constraint/distributor.py	Wed Aug 16 11:29:21 2006
@@ -0,0 +1,150 @@
+import math
+from pypy.rpython.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.objspace.std.intobject import W_IntObject
+from pypy.objspace.std.listobject import W_ListObject
+from pypy.objspace.std.stringobject import W_StringObject
+
+from pypy.objspace.cclp.types import W_AbstractDistributor, Solution
+from pypy.objspace.cclp.thunk import DistributorThunk
+from pypy.objspace.cclp.misc import w, ClonableCoroutine
+from pypy.objspace.cclp.global_state import scheduler
+from pypy.objspace.cclp.interp_var import interp_free
+
+def spawn_distributor(space, distributor):
+    thread = ClonableCoroutine(space)
+    thread._cspace = ClonableCoroutine.w_getcurrent(space)._cspace
+    thunk = DistributorThunk(space, distributor, thread)
+    thread.bind(thunk)
+    if not we_are_translated():
+        w("DISTRIBUTOR THREAD", str(id(thread)))
+    scheduler[0].add_new_thread(thread)
+    scheduler[0].schedule()
+
+def distribute(space, w_strategy):
+    assert isinstance(w_strategy, W_StringObject)
+    strat = space.str_w(w_strategy)
+    if strat == 'naive':
+        pass
+    elif strat == 'dichotomy':
+        dist = make_split_distributor(space, space.newint(2))
+        spawn_distributor(space, dist)
+    else:
+        raise OperationError(space.w_RuntimeError,
+                             space.wrap("please pick a strategy in (naive, dichotomy)"))
+app_distribute = interp2app(distribute)
+
+
+def arrange_domains(cs, variables):
+    """build a data structure from var to dom
+       that satisfies distribute & friends"""
+    new_doms = {}
+    for var in variables:
+        new_doms[var] = cs.dom(var).copy()
+    return new_doms
+
+class W_Distributor(W_AbstractDistributor):
+    """_distribute is left unimplemented."""
+
+    def w_fanout(self):
+        return self._space.newint(self._fanout)
+
+    def fanout(self):
+        return self._fanout
+
+    def _find_smallest_domain(self):
+        """returns the variable having the smallest domain.
+        (or one of such variables if there is a tie)
+        """
+        vars_ = [var for var in self._cspace._store.values()
+                 if var.w_dom.size() > 1]
+        if  len(vars_) == 0:
+            raise Solution
+        best = vars_[0]
+        for var in vars_:
+            if var.w_dom.size() < best.w_dom.size():
+                best = var
+        return best
+
+    def w_distribute(self, w_choice):
+        assert isinstance(w_choice, W_IntObject)
+        self.distribute(self._space.int_w(w_choice) -1)
+
+    def distribute(self, choice):
+        assert isinstance(choice, int)
+        variable = self.find_distribution_variable()
+        domain = variable.w_dom
+        self._do_distribute(domain, choice)
+
+    def find_distribution_variable(self):
+        return self._find_smallest_domain()
+    
+    def _do_distribute(self, domain, choice):
+        """remove values from domain depending on choice"""
+        raise NotImplementedError
+
+W_Distributor.typedef = typedef.TypeDef("W_Distributor",
+    W_AbstractDistributor.typedef,
+    fanout = interp2app(W_Distributor.w_fanout),
+    distribute = interp2app(W_Distributor.w_distribute))
+    
+        
+class W_NaiveDistributor(W_Distributor):
+    """distributes domains by splitting the smallest domain in 2 new domains
+    The first new domain has a size of one,
+    and the second has all the other values"""
+
+        
+    def _do_distribute(self, domain, choice):
+        values = domain.get_values()
+        #assert len(values) > 0
+        if choice == 0:
+            domain.remove_values(values[1:])
+        else:
+            domain.w_remove_value(values[0]) #XXX w ? not w ?
+
+W_NaiveDistributor.typedef = typedef.TypeDef(
+    "W_NaiveDistributor",
+    W_Distributor.typedef)
+
+def make_naive_distributor(object_space, fanout=2):
+    if not isinstance(fanout, int):
+        raise OperationError(object_space.w_RuntimeError,
+                             object_space.wrap("fanout must be a positive integer"))
+    return object_space.wrap(W_NaiveDistributor(object_space, fanout))
+app_make_naive_distributor = interp2app(make_naive_distributor,
+                                        unwrap_spec = [baseobjspace.ObjSpace, int])
+
+
+class W_SplitDistributor(W_Distributor):
+    """distributes domains by splitting the smallest domain in
+    nb_subspaces equal parts or as equal as possible.
+    If nb_subspaces is 0, then the smallest domain is split in
+    domains of size 1"""
+    
+    def _subdomains(self):
+        """returns the min number of partitions
+           for a domain to be distributed"""
+        to_split = self._find_smallest_domain()
+        if self._fanout > 0:
+            return min(self._fanout, to_split.w_dom.size()) 
+        else:
+            return to_split.w_dom.size() 
+
+    def _do_distribute(self, domain, choice):
+        values = domain.get_values()
+        nb_elts = max(1, len(values)*1./self._subdomains())
+        start, end = (int(math.floor(choice * nb_elts)),
+                      int(math.floor((choice + 1) * nb_elts)))
+        domain.remove_values(values[:start])
+        domain.remove_values(values[end:])
+
+def make_split_distributor(space, w_fanout):
+    return W_SplitDistributor(space, space.int_w(w_fanout))
+app_make_split_distributor = interp2app(make_split_distributor)
+

Modified: pypy/dist/pypy/objspace/cclp/constraint/domain.py
==============================================================================
--- pypy/dist/pypy/objspace/cclp/constraint/domain.py	(original)
+++ pypy/dist/pypy/objspace/cclp/constraint/domain.py	Wed Aug 16 11:29:21 2006
@@ -7,15 +7,11 @@
 
 from pypy.objspace.std.model import StdObjSpaceMultiMethod
 
-from pypy.objspace.cclp.types import W_AbstractDomain, W_Var
+from pypy.objspace.cclp.types import W_AbstractDomain, W_Var, ConsistencyError
 from pypy.objspace.cclp.interp_var import interp_bind
 
 all_mms = {}
 
-class ConsistencyFailure(Exception):
-    """The repository is not in a consistent state"""
-    pass
-
 
 class W_FiniteDomain(W_AbstractDomain):
     """
@@ -42,16 +38,11 @@
 
     def _value_removed(self):
         """The implementation of remove_value should call this method"""
-        interp_bind(self._space, self._changed, True)
+        interp_bind(self._changed, True)
         self.clear_change()
         
         if self.size() == 0:
-            raise OperationError(self._space.w_RuntimeError,
-                                 self._space.wrap('ConsistencyFailure'))
-
-##     def w__del__(self):
-##         self._space.bind(self._changed, self._space.newbool(False))
-
+            raise ConsistencyError, "tried to make a domain empty"
 
     def set_values(self, w_values):
         """Objects in the value set can't be unwrapped unless we

Added: pypy/dist/pypy/objspace/cclp/constraint/test/problem.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/objspace/cclp/constraint/test/problem.py	Wed Aug 16 11:29:21 2006
@@ -0,0 +1,44 @@
+
+
+def conference_scheduling():
+
+    dom_values = [(room,slot) 
+          for room in ('room A','room B','room C') 
+          for slot in ('day 1 AM','day 1 PM','day 2 AM',
+                       'day 2 PM')]
+
+    variables = {}
+    for v in ('c01','c02','c03','c04','c05', 'c06','c07','c08','c09','c10'):
+        variables[v] = domain(dom_values, v)
+
+    for conf in ('c03','c04','c05','c06'):
+        v = variables[conf]
+        tell(make_expression([v], "%s[0] == 'room C'" % conf))
+
+    for conf in ('c01','c05','c10'):
+        v = variables[conf]
+        tell(make_expression([v], "%s[1].startswith('day 1')" % conf))
+
+    for conf in ('c02','c03','c04','c09'):
+        v = variables[conf]
+        tell(make_expression([v], "%s[1].startswith('day 2')" % conf))
+
+    groups = (('c01','c02','c03','c10'),
+              ('c02','c06','c08','c09'),
+              ('c03','c05','c06','c07'),
+              ('c01','c03','c07','c08'))
+
+    for group in groups:
+        for conf1 in group:
+            for conf2 in group:
+                if conf2 > conf1:
+                    v1, v2 = variables[conf1], variables[conf2]
+                    tell(make_expression([v1, v2], '%s[1] != %s[1]'% (conf1, conf2)))
+
+    
+    tell(all_diff(variables.values()))
+
+    distribute('dichotomy')
+
+    return variables.values()
+

Modified: pypy/dist/pypy/objspace/cclp/constraint/test/test_constraint.py
==============================================================================
--- pypy/dist/pypy/objspace/cclp/constraint/test/test_constraint.py	(original)
+++ pypy/dist/pypy/objspace/cclp/constraint/test/test_constraint.py	Wed Aug 16 11:29:21 2006
@@ -18,31 +18,35 @@
         assert cstr.knows_var(v2)
 
     def test_revise(self):
-        v1 = domain([1, 2], 'v1')
-        v2 = domain([1, 2], 'v2')
-        cstr = all_diff([v1, v2])
-        assert cstr.revise() == False # not entailed
-
-        v3 = domain([1], 'v3')
-        v4 = domain([2], 'v4')
-        cstr = all_diff([v3, v4])
-        assert cstr.revise() == True # entailed
-
-        v5 = domain([1], 'v5')
-        v6 = domain([1], 'v6')
-        cstr = all_diff([v5, v6])
-        raises(Exception, cstr.revise)
-
-        v7 = domain([1, 2], 'v7')
-        v8 = domain([1, 2], 'v8')
-        cstr = all_diff([v2, v7, v8])
-        raises(Exception, cstr.revise)
-
-        v9 = domain([1], 'v9')
-        v10= domain([1, 2], 'v10')
-        cstr = all_diff([v9, v10])
-        assert cstr.revise() == True
-        assert domain_of(v10).get_values() == [2]
+        def in_space():
+            v1 = domain([1, 2], 'v1')
+            v2 = domain([1, 2], 'v2')
+            cstr = all_diff([v1, v2])
+            assert cstr.revise() == False # not entailed
+
+            v3 = domain([1], 'v3')
+            v4 = domain([2], 'v4')
+            cstr = all_diff([v3, v4])
+            assert cstr.revise() == True # entailed
+
+            v5 = domain([1], 'v5')
+            v6 = domain([1], 'v6')
+            cstr = all_diff([v5, v6])
+            raises(Exception, cstr.revise)
+
+            v7 = domain([1, 2], 'v7')
+            v8 = domain([1, 2], 'v8')
+            cstr = all_diff([v2, v7, v8])
+            raises(Exception, cstr.revise)
+
+            v9 = domain([1], 'v9')
+            v10= domain([1, 2], 'v10')
+            cstr = all_diff([v9, v10])
+            assert cstr.revise() == True
+            assert domain_of(v10).get_values() == [2]
+
+        s = newspace(in_space)
+    
 
 class AppTest_Expression(object):
 

Modified: pypy/dist/pypy/objspace/cclp/interp_var.py
==============================================================================
--- pypy/dist/pypy/objspace/cclp/interp_var.py	(original)
+++ pypy/dist/pypy/objspace/cclp/interp_var.py	Wed Aug 16 11:29:21 2006
@@ -1,7 +1,11 @@
 from pypy.objspace.cclp.variable import wait__Var, _assign_aliases, _entail
-from pypy.objspace.cclp.types import W_Var
+from pypy.objspace.cclp.types import W_Var, W_CVar
+from pypy.objspace.cclp.global_state import scheduler
+from pypy.objspace.cclp.misc import w
 
 
+def interp_free(w_var):
+    return isinstance(w_var.w_bound_to, W_Var)
 
 def interp_wait(space, obj):
     return wait__Var(space, obj)
@@ -9,24 +13,56 @@
 
 class RebindingError(Exception): pass
 
-def interp_bind(space, w_var, obj):
-    if isinstance(w_var.w_bound_to, W_Var):
-        return _assign_aliases(space, w_var, obj)
+def interp_bind(w_var, obj):
+    if interp_free(w_var):
+        return interp_assign_aliases(w_var, obj)
     if w_var.w_bound_to == obj:
         return
     raise RebindingError
 
-class EntailmentFailure(Exception): pass
+class EntailmentError(Exception): pass
 
-def interp_entail(space, w_v1, w_v2):
+def interp_entail(w_v1, w_v2):
     w_v1val = w_v1.w_bound_to
     w_v2val = w_v2.w_bound_to
-    if not isinstance(w_v1val, W_Var):
-        if not isinstance(w_v2val, W_Var):
+    if not interp_free(w_v1):
+        if not interp_free(w_v2):
             # let's be simpler than unify
             if w_v1val != w_v2val:
-                raise EntailmentFailure
-        return _assign_aliases(space, w_v2, w_v1val)
+                raise EntailmentError
+        return interp_assign_aliases(w_v2, w_v1val)
     else:
-        return _entail(space, w_v1, w_v2)
+        w_v1.entails[w_v2] = True
 
+
+def interp_assign_aliases(w_var, w_val):
+    w("  :assign")
+    assert isinstance(w_var, W_Var)
+    w_curr = w_var
+    while 1:
+        w_next = w_curr.w_bound_to
+        _assign(w_curr, w_val)
+        # notify the blocked threads
+        scheduler[0].unblock_on(w_curr)
+        if w_next is w_var:
+            break
+        # switch to next
+        w_curr = w_next
+    _assign_entailed(w_var, w_val)
+    w("  :assigned")
+
+def _assign_entailed(w_var, w_val):
+    w("   :assign entailed")
+    for var in w_var.entails:
+        if interp_free(var):
+            interp_assign_aliases(var, w_val)
+        else:
+            if w_var.w_bound_to != w_val:
+                raise EntailmentError
+
+def _assign(w_var, w_val):
+    assert isinstance(w_var, W_Var)
+    if isinstance(w_var, W_CVar):
+        if not w_val in w_var.w_dom._values.content:
+            raise ValueError, "assignment out of domain"
+    w_var.w_bound_to = w_val

Modified: pypy/dist/pypy/objspace/cclp/scheduler.py
==============================================================================
--- pypy/dist/pypy/objspace/cclp/scheduler.py	(original)
+++ pypy/dist/pypy/objspace/cclp/scheduler.py	Wed Aug 16 11:29:21 2006
@@ -72,31 +72,6 @@
         thread._prev = l
         thread._next = r
 
-    def remove_thread(self, thread):
-        assert isinstance(thread, ClonableCoroutine)
-        w(".. REMOVING", str(id(thread)))
-        assert thread not in self._blocked
-        try:
-            del self._traced[thread]
-        except KeyError:
-            w(".. removing non-traced thread")
-        l = thread._prev
-        r = thread._next
-        l._next = r
-        r._prev = l
-        self._head = r
-        if r == thread: #XXX write a test for me !
-            if not we_are_translated(): 
-                import traceback
-                traceback.print_exc()
-            self.display_head()
-        thread._next = thread._prev = None
-        # cspace/threads account mgmt
-        if thread._cspace is not self.space.w_None:
-            count = self.dec_live_thread_count(thread._cspace)
-            if count == 0:
-                del self._per_space_live_threads[thread._cspace]
-
     #-- cspace helper
 
     def is_stable(self, cspace):
@@ -122,6 +97,7 @@
             self.schedule()
 
     #-- cspace -> thread_count helpers
+    
     def inc_live_thread_count(self, cspace):
         assert isinstance(cspace, W_CSpace)
         count = self._per_space_live_threads.get(cspace, 0) + 1
@@ -130,9 +106,9 @@
 
     def dec_live_thread_count(self, cspace):
         assert isinstance(cspace, W_CSpace)
-        count = self._per_space_live_threads[cspace] -1
+        count = self._per_space_live_threads[cspace] - 1
         assert count >= 0
-        self._per_space_live_threads[cspace] = count 
+        self._per_space_live_threads[cspace] = count
         return count 
     #-- /
 
@@ -166,56 +142,59 @@
             
             to_be_run = to_be_run._next
             assert isinstance(to_be_run, ClonableCoroutine)
+            # asking threads
+            if to_be_run in self._asking:
+                if self.is_stable(self._asking[to_be_run]):
+                    del self._asking[to_be_run]
+                    del self._blocked[to_be_run]
+                    break
             if to_be_run == sentinel:
                 if not dont_pass:
                     return ClonableCoroutine.w_getcurrent(self.space)
-                self.display_head()
+                w(str(sched_all(self.space)))
                 ## we RESET sched state so as to keep being usable beyond that
                 self._init_head(self._main)
                 self._init_blocked()
                 w(".. SCHEDULER reinitialized")
                 raise OperationError(self.space.w_AllBlockedError,
                                      self.space.wrap("can't schedule, possible deadlock in sight"))
-            # asking threads
-            if to_be_run in self._asking.keys():
-                if self.is_stable(self._asking[to_be_run]):
-                    del self._asking[to_be_run]
-                    del self._blocked[to_be_run]
-                    break
         self._head = to_be_run
         return to_be_run
 
-    #XXX call me directly for this to work translated
-    def __len__(self):
-        "count of known threads (including dead ones)"
-        curr = self._head
-        sentinel = curr
-        count = 1 # there is always a main thread
-        while curr._next != sentinel:
-            curr = curr._next
-            count += 1
-        return count
-
-    def display_head(self):
-        if we_are_translated():
-            w("<translated: we don't display the head>")
-            return
-        curr = self._head
-        v('Threads : [', '-'.join([str(id(curr)), str(curr in self._blocked)]))
-        while curr._next != self._head:
-            curr = curr._next
-            v('-'.join([str(id(curr)), str(curr in self._blocked)]))
-        w(']')
-
     def add_new_thread(self, thread):
         "insert 'thread' at end of running queue"
         assert isinstance(thread, ClonableCoroutine)
         # cspace account mgmt
-        if thread._cspace != self.space.w_None:
+        if thread._cspace != None:
             self._per_space_live_threads.get(thread._cspace, 0)
             self.inc_live_thread_count(thread._cspace)
         self._chain_insert(thread)
 
+    def remove_thread(self, thread):
+        assert isinstance(thread, ClonableCoroutine)
+        w(".. REMOVING", str(id(thread)))
+        assert thread not in self._blocked
+        try:
+            del self._traced[thread]
+        except KeyError:
+            w(".. removing non-traced thread")
+        l = thread._prev
+        r = thread._next
+        l._next = r
+        r._prev = l
+        self._head = r
+        if r == thread: #XXX write a test for me !
+            if not we_are_translated(): 
+                import traceback
+                traceback.print_exc()
+        thread._next = thread._prev = None
+        # cspace/threads account mgmt
+        if thread._cspace is not None:
+            cspace = thread._cspace
+            live = self.dec_live_thread_count(cspace)
+            if live == 0:
+                del self._per_space_live_threads[cspace]
+
     def add_to_blocked_on(self, w_var, thread):
         w(".. we BLOCK thread", str(id(thread)), "on var", str(w_var))
         assert isinstance(w_var, W_Var)
@@ -229,7 +208,7 @@
         blocked.append(thread)
         self._blocked[thread] = True
         # cspace accounting
-        if thread._cspace is not self.space.w_None:
+        if thread._cspace is not None:
             self.dec_live_thread_count(thread._cspace)
 
     def unblock_on(self, w_var):
@@ -243,7 +222,7 @@
         for thr in blocked:
             del self._blocked[thr]
             # cspace accounting
-            if thr._cspace is not self.space.w_None:
+            if thr._cspace is not None:
                 self.inc_live_thread_count(thr._cspace)
 
     def add_to_blocked_byneed(self, w_var, thread):
@@ -258,7 +237,7 @@
         blocked.append(thread)
         self._blocked[thread] = True
         # cspace accounting
-        if thread._cspace is not self.space.w_None:
+        if thread._cspace is not None:
             self.dec_live_thread_count(thread._cspace)
 
     def unblock_byneed_on(self, w_var):
@@ -274,7 +253,7 @@
         for thr in blocked:
             del self._blocked[thr]
             # cspace accounting
-            if thr._cspace is not self.space.w_None:
+            if thr._cspace is not None:
                 self.inc_live_thread_count(thr._cspace)
             
 

Modified: pypy/dist/pypy/objspace/cclp/space.py
==============================================================================
--- pypy/dist/pypy/objspace/cclp/space.py	(original)
+++ pypy/dist/pypy/objspace/cclp/space.py	Wed Aug 16 11:29:21 2006
@@ -8,7 +8,8 @@
 from pypy.objspace.cclp.thunk import CSpaceThunk, PropagatorThunk
 from pypy.objspace.cclp.global_state import scheduler
 from pypy.objspace.cclp.variable import newvar
-
+from pypy.objspace.cclp.types import ConsistencyError, Solution, W_Var
+from pypy.objspace.cclp.interp_var import interp_bind, interp_free
 
 def newspace(space, w_callable, __args__):
     args = __args__.normalize()
@@ -34,9 +35,13 @@
     assert isinstance(w_n, W_IntObject)
     n = space.int_w(w_n)
     cspace = ClonableCoroutine.w_getcurrent(space)._cspace
-    if cspace != space.w_None:
+    if cspace != None:
         assert isinstance(cspace, W_CSpace)
-        return cspace.choose(n)
+        try:
+            return space.newint(cspace.choose(w_n.intval))
+        except ConsistencyError:
+            raise OperationError(space.w_ConsistecyError,
+                                 space.wrap("the space is failed"))
     raise OperationError(space.w_RuntimeError,
                          space.wrap("choose is forbidden from the top-level space"))
 app_choose = gateway.interp2app(choose)
@@ -54,55 +59,69 @@
 
     def __init__(self, space, thread, parent):
         assert isinstance(thread, ClonableCoroutine)
-        assert (parent is space.w_None) or isinstance(parent, W_CSpace)
+        assert (parent is None) or isinstance(parent, W_CSpace)
         self.space = space # the object space ;-)
         self.parent = parent
         self.main_thread = thread
         # choice mgmt
         self._choice = newvar(space)
         self._committed = newvar(space)
-        # merging
+        # status, merging
         self._solution = newvar(space)
-        self._merged = newvar(space)
+        self._finished = newvar(space)
+        self._failed = False
+        # constraint store ...
+        self._store = {} # name -> var
         
+    def register_var(self, cvar):
+        self._store[cvar.name] = cvar
 
     def w_ask(self):
         scheduler[0].wait_stable(self)
         self.space.wait(self._choice)
-        return self._choice
+        choice = self._choice.w_bound_to
+        self._choice = newvar(self.space)
+        self._last_choice = choice.intval
+        return choice
 
     def choose(self, n):
         assert n > 1
         scheduler[0].wait_stable(self)
-        assert self.space.is_true(self.space.is_free(self._choice))
-        assert self.space.is_true(self.space.is_free(self._committed))
-        self.space.bind(self._choice, self.space.wrap(n))
+        if self._failed: #XXX set by any propagator
+            raise ConsistencyError
+        assert interp_free(self._choice)
+        assert interp_free(self._committed)
+        interp_bind(self._choice, self.space.wrap(n))
         self.space.wait(self._committed)
-        committed = self._committed
+        committed = self._committed.w_bound_to
         self._committed = newvar(self.space)
         return committed
 
     def w_commit(self, w_n):
-        assert self.space.is_true(self.space.is_bound(self._choice))
-        assert 0 < self.space.int_w(w_n)
-        assert self.space.int_w(w_n) <= self._choice.w_bound_to
-        self.space.bind(self._committed, w_n)
-        self._choice = newvar(self.space)
-
+        assert isinstance(w_n, W_IntObject)
+        n = w_n.intval
+        assert interp_free(self._committed)
+        assert n > 0
+        assert n <= self._last_choice
+        interp_bind(self._committed, n)
 
     def tell(self, w_constraint):
         space = self.space
         w_coro = ClonableCoroutine(space)
-        thunk = PropagatorThunk(space, w_constraint, w_coro, self._merged)
+        w_coro._cspace = self
+        thunk = PropagatorThunk(space, w_constraint, w_coro)
         w_coro.bind(thunk)
         if not we_are_translated():
-            w("PROPAGATOR, thread", str(id(w_coro)))
-        w_coro._cspace = self
+            w("PROPAGATOR in thread", str(id(w_coro)))
         scheduler[0].add_new_thread(w_coro)
         scheduler[0].schedule()
 
+    def fail(self):
+        self._failed = True
+        self._store = {}
+
     def w_merge(self):
-        self.space.bind(self._merged, self.space.w_True)
+        self._store = {}
         return self._solution
 
 

Modified: pypy/dist/pypy/objspace/cclp/thread.py
==============================================================================
--- pypy/dist/pypy/objspace/cclp/thread.py	(original)
+++ pypy/dist/pypy/objspace/cclp/thread.py	Wed Aug 16 11:29:21 2006
@@ -21,6 +21,7 @@
     w_Future = W_Future(space)
     thunk = FutureThunk(space, w_callable, args, w_Future, coro)
     coro.bind(thunk)
+    coro._cspace = ClonableCoroutine.w_getcurrent(space)._cspace
     if not we_are_translated():
         w("FUTURE", str(id(coro)), "for", str(w_callable.name))
     scheduler[0].add_new_thread(coro)
@@ -40,6 +41,7 @@
     #coro.cspace = ClonableCoroutine.w_getcurrent(space).cspace
     thunk = ProcedureThunk(space, w_callable, args, coro)
     coro.bind(thunk)
+    coro._cspace = ClonableCoroutine.w_getcurrent(space)._cspace
     if not we_are_translated():
         w("STACKLET", str(id(coro)), "for", str(w_callable.name))
     scheduler[0].add_new_thread(coro)
@@ -53,5 +55,3 @@
 def this_thread(space):
     return ClonableCoroutine.w_getcurrent(space)
 app_this_thread = gateway.interp2app(this_thread)
-
-

Modified: pypy/dist/pypy/objspace/cclp/thunk.py
==============================================================================
--- pypy/dist/pypy/objspace/cclp/thunk.py	(original)
+++ pypy/dist/pypy/objspace/cclp/thunk.py	Wed Aug 16 11:29:21 2006
@@ -3,8 +3,8 @@
 
 from pypy.objspace.cclp.misc import w
 from pypy.objspace.cclp.global_state import scheduler
-from pypy.objspace.cclp.types import W_Var, W_Future, W_FailedValue
-from pypy.objspace.cclp.interp_var import interp_wait, interp_entail, interp_bind
+from pypy.objspace.cclp.types import W_Var, W_Future, W_FailedValue, ConsistencyError, Solution
+from pypy.objspace.cclp.interp_var import interp_wait, interp_entail, interp_bind, interp_free
 
 
 def logic_args(args):
@@ -70,65 +70,96 @@
             scheduler[0].remove_thread(self._coro)
             scheduler[0].schedule()
 
-SPACE_FAILURE = 0
-SPACE_SOLUTION = 1
-
 class CSpaceThunk(_AppThunk):
     def __init__(self, space, w_callable, args, coro):
         _AppThunk.__init__(self, space, coro.costate, w_callable, args)
         self._coro = coro
 
     def call(self):
-        w(". initial (returnless) thunk CALL in", str(id(self._coro)))
+        w("-- initial thunk CALL in", str(id(self._coro)))
         scheduler[0].trace_vars(self._coro, logic_args(self.args.unpack()))
         cspace = self._coro._cspace
         try:
             try:
                 _AppThunk.call(self)
             except Exception, exc:
-                w(".% exceptional EXIT of", str(id(self._coro)), "with", str(exc))
+                w("-- exceptional EXIT of", str(id(self._coro)), "with", str(exc))
+                import traceback
+                traceback.print_exc()
                 scheduler[0].dirty_traced_vars(self._coro, W_FailedValue(exc))
                 self._coro._dead = True
                 self.space.bind(cspace._choice, self.space.wrap(SPACE_FAILURE))
             else:
-                w(".% clean (valueless) EXIT of", str(id(self._coro)))
+                w("-- clean (valueless) EXIT of", str(id(self._coro)))
                 self.space.bind(cspace._solution, self.costate.w_tempval)
-                self.space.bind(cspace._choice, self.space.wrap(SPACE_SOLUTION))
         finally:
             scheduler[0].remove_thread(self._coro)
             scheduler[0].schedule()
 
 
 class PropagatorThunk(AbstractThunk):
-    def __init__(self, space, w_constraint, coro, Merged):
+    def __init__(self, space, w_constraint, coro):
         self.space = space
         self.coro = coro
         self.const = w_constraint
-        self.Merged = Merged
 
     def call(self):
         try:
+            cspace = self.coro._cspace
             try:
                 while 1:
                     entailed = self.const.revise()
                     if entailed:
                         break
                     Obs = W_Var(self.space)
-                    interp_entail(self.space, self.Merged, Obs)
+                    interp_entail(cspace._finished, Obs)
                     for Sync in [var.w_dom.give_synchronizer()
                                  for var in self.const._variables]:
-                        interp_entail(self.space, Sync, Obs)
+                        interp_entail(Sync, Obs)
                     interp_wait(self.space, Obs)
+                    if not interp_free(cspace._finished):
+                        break
+            except ConsistencyError:
+                self.coro._cspace.fail()
             except:
                 import traceback
                 traceback.print_exc()
         finally:
-            # all values of dom size 1 are bound
-            for var in self.const._variables:
-                if var.w_dom.size() == 1:
-                    interp_bind(self.space, var, var.w_dom.get_values()[0])
             self.coro._dead = True
             scheduler[0].remove_thread(self.coro)
             scheduler[0].schedule()
 
 
+class DistributorThunk(AbstractThunk):
+    def __init__(self, space, w_distributor, coro):
+        self.space = space
+        self.coro = coro
+        self.dist = w_distributor
+
+    def call(self):
+        coro = self.coro
+        try:
+            cspace = coro._cspace
+            dist = self.dist
+            try:
+                while 1:
+                    choice = cspace.choose(dist.fanout())
+                    dist.distribute(choice)
+            except Solution:
+                w("-- DISTRIBUTOR thunk exited because a solution was found")
+                for var in cspace._solution.w_bound_to.wrappeditems:
+                    assert var.w_dom.size() == 1
+                    interp_bind(var, var.w_dom.get_values()[0])
+                interp_bind(cspace._choice, self.space.newint(1))
+            except ConsistencyError, e:
+                w("-- DISTRIBUTOR thunk exited because", str(e))
+                interp_bind(cspace._choice, self.space.newint(0))
+            except:
+                import traceback
+                traceback.print_exc()
+        finally:
+            interp_bind(cspace._finished, True)
+            coro._dead = True
+            scheduler[0].remove_thread(coro)
+            scheduler[0].schedule()
+        

Modified: pypy/dist/pypy/objspace/cclp/types.py
==============================================================================
--- pypy/dist/pypy/objspace/cclp/types.py	(original)
+++ pypy/dist/pypy/objspace/cclp/types.py	Wed Aug 16 11:29:21 2006
@@ -36,18 +36,23 @@
 
 
 class W_CVar(W_Var):
-    def __init__(w_self, space, w_dom, w_name):
+    def __init__(self, space, w_dom, w_name):
         assert isinstance(w_dom, W_AbstractDomain)
-        W_Var.__init__(w_self, space)
-        w_self.w_dom = w_dom
-        w_self.name = space.str_w(w_name)
-        w_self.w_nam = w_name
+        W_Var.__init__(self, space)
+        self.w_dom = w_dom
+        self.name = space.str_w(w_name)
+        self.w_nam = w_name
+        cspace = ClonableCoroutine.w_getcurrent(space)._cspace
+        if cspace is None:
+            w("-- WARNING : you are instanciating a constraint var in the top-level space")
+        else:
+            cspace.register_var(self)
 
-    def name_w(w_self):
-        return w_self.name
+    def name_w(self):
+        return self.name
 
-    def w_name(w_self):
-        return w_self.w_nam
+    def w_name(self):
+        return self.w_nam
 
 def domain_of(space, w_v):
     assert isinstance(w_v, W_CVar)
@@ -63,6 +68,10 @@
     def __init__(w_self, exc):
         w_self.exc = exc
 
+class ConsistencyError(Exception): pass
+
+class Solution(Exception): pass
+
 #-- Constraint ---------------------------------------------
 
 class W_Constraint(baseobjspace.Wrappable):
@@ -82,6 +91,15 @@
 
 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 = ClonableCoroutine.w_getcurrent(space)._cspace
+
+W_AbstractDistributor.typedef = typedef.TypeDef("W_AbstractDistributor")
 
 
 #-- Misc ---------------------------------------------------

Modified: pypy/dist/pypy/objspace/logic.py
==============================================================================
--- pypy/dist/pypy/objspace/logic.py	(original)
+++ pypy/dist/pypy/objspace/logic.py	Wed Aug 16 11:29:21 2006
@@ -50,13 +50,7 @@
 all_mms.update(constraint.all_mms)
 
 ## #----- distributors ---------------
-## from pypy.objspace.constraint import distributor
-
-
-#-- THE SPACE ---------------------------------------
-
-#class UnificationError(w_RuntimeError):
-#    pass
+from pypy.objspace.cclp.constraint import distributor
 
 
 #-- SPACE HELPERS -------------------------------------
@@ -243,13 +237,9 @@
                   space.wrap(constraint.app_make_expression))
     space.setitem(space.builtin.w_dict, space.wrap('all_diff'),
                  space.wrap(constraint.app_make_alldistinct))
-##     #-- distributor --
-##     space.setitem(space.builtin.w_dict, space.wrap('NaiveDistributor'),
-##                  space.wrap(distributor.app_make_naive_distributor))
-##     space.setitem(space.builtin.w_dict, space.wrap('SplitDistributor'),
-##                  space.wrap(distributor.app_make_split_distributor))
-##     space.setitem(space.builtin.w_dict, space.wrap('DichotomyDistributor'),
-##                  space.wrap(distributor.app_make_dichotomy_distributor))
+    #-- distributor --
+    space.setitem(space.builtin.w_dict, space.wrap('distribute'),
+                 space.wrap(distributor.app_distribute))
     #-- threading --
     space.setitem(space.builtin.w_dict, space.wrap('future'),
                  space.wrap(app_future))
@@ -286,8 +276,8 @@
     #-- path to the applevel modules --
     import pypy.objspace.constraint
     import os
-    dir = os.path.dirname(pypy.objspace.constraint.__file__)
-    dir = os.path.join(dir, 'applevel')
+    dir = os.path.dirname(pypy.objspace.cclp.constraint.__file__)
+    dir = os.path.join(dir, 'test')
     space.call_method(space.sys.get('path'), 'append', space.wrap(dir))
 
     # make sure that _stackless is imported

Modified: pypy/dist/pypy/objspace/test/test_logicobjspace.py
==============================================================================
--- pypy/dist/pypy/objspace/test/test_logicobjspace.py	(original)
+++ pypy/dist/pypy/objspace/test/test_logicobjspace.py	Wed Aug 16 11:29:21 2006
@@ -1,6 +1,7 @@
 from pypy.conftest import gettestobjspace
 from py.test import skip
 
+
 class AppTest_Logic(object):
 
     def setup_class(cls):
@@ -637,25 +638,6 @@
         schedule()
 
         reset_scheduler() # free all the hanging threads
-
-    def test_newspace_ask_noop(self):
-
-        def in_space(X): return X + 42
-
-        def asker():
-            ask()
-
-        X = newvar()
-        s = newspace(in_space, X)
-
-        assert sched_all()['space_accounting'][0][1] == 0 # live threads
-        assert len(sched_all()['blocked_on']) == 1
-
-        stacklet(asker)
-
-        unify(X, 42)
-        schedule()
-        assert len(sched_all()['threads']) == 1
         
 
 class AppTest_CompSpace(object):
@@ -665,48 +647,55 @@
 
     def test_cvar(self):
 
-        d = domain([1, 2, 4], '')
-
-        raises(UnificationError, bind, d, 42)
-        bind(d, 2)
-        assert d == 2
+        def in_space(X):
+            d = domain([1, 2, 4], '')
 
-        class Foo(object):
-            pass
-
-        f = Foo()
-        d = domain([Foo(), f, Foo()], '')
-        raises(UnificationError, bind, d, Foo())
-        bind(d, f)
-        assert d == f
-
-        d1 = domain([1, 2, 3], '')
-        d2 = domain([2, 3, 4], '')
-        d3 = domain([5, 6], '')
-        raises(UnificationError, unify, d1, d3)
-        unify(d1, d2)
-        assert alias_of(d1, d2)
-        assert domain_of(d1) == domain_of(d2) == FiniteDomain([2, 3])
-
-        d1 = domain([1, 2, 3], '')
-        d4 = domain([3, 4], '')
-        unify(d1, d4)
-        assert d1 == d4 == 3
+            raises(UnificationError, bind, d, 42)
+            bind(d, 2)
+            assert d == 2
+
+            class Foo(object):
+                pass
+
+            f = Foo()
+            d = domain([Foo(), f, Foo()], '')
+            raises(UnificationError, bind, d, Foo())
+            bind(d, f)
+            assert d == f
+
+            d1 = domain([1, 2, 3], '')
+            d2 = domain([2, 3, 4], '')
+            d3 = domain([5, 6], '')
+            raises(UnificationError, unify, d1, d3)
+            unify(d1, d2)
+            assert alias_of(d1, d2)
+            assert domain_of(d1) == domain_of(d2) == FiniteDomain([2, 3])
+
+            d1 = domain([1, 2, 3], '')
+            d4 = domain([3, 4], '')
+            unify(d1, d4)
+            assert d1 == d4 == 3
+
+            d1 = domain([1, 2], '')
+            x = newvar()
+            unify(d1, x)
+            assert alias_of(x, d1)
+            raises(UnificationError, unify, x, 42)
+
+            d1 = domain([1, 2], '')
+            x = newvar()
+            unify(d1, x)
+            assert alias_of(x, d1)
+            unify(x, 2)
+            assert d1 == x == 2
+            #XXX and a bunch of app-level functions
+            #raises(TypeError, domain_of, x)
 
-        d1 = domain([1, 2], '')
-        x = newvar()
-        unify(d1, x)
-        assert alias_of(x, d1)
-        raises(UnificationError, unify, x, 42)
+            bind(X, True)
 
-        d1 = domain([1, 2], '')
-        x = newvar()
-        unify(d1, x)
-        assert alias_of(x, d1)
-        unify(x, 2)
-        assert d1 == x == 2
-        #XXX and a bunch of app-level functions
-        #raises(TypeError, domain_of, x)
+        X = newvar()
+        newspace(in_space, X)
+        wait(X)
 
     def test_newspace_ask_wait(self):
 
@@ -739,13 +728,12 @@
             cspace.commit(2)
 
         X = newvar()
-
         s = newspace(chooser, X)
         stacklet(asker, s)
         schedule()
+        wait(X)
         assert X == 2
 
-
     def test_more_ask_choose(self):
 
         def chooser(vec, X):
@@ -757,9 +745,9 @@
         def asker(cspace):
             while 1:
                 choices = cspace.ask()
-                if choices == 1: # success !
-                    break
                 cspace.commit(choices)
+                if choices == 8: # success !
+                    break
 
         # choices >= 1
         v = range(2, 9)
@@ -774,28 +762,34 @@
 
         assert X == 'done'
         schedule()
-        assert len(sched_all()['threads']) == 1
+        #XXX
+        #assert len(sched_all()['threads']) == 1
 
-    def test_tell(self):
 
-        def problem():
-            X, Y = domain([1, 2], 'X'), domain([1, 2, 3], 'Y')
-            tell(make_expression([X, Y], 'X + Y > 4'))
-            return (X, Y)
+    def test_tell_ask_choose_commit(self):
+        from problem import conference_scheduling
 
-        def solve(spc, X):
+        def solve(spc, Sol):
             while 1:
                 status = spc.ask()
-                if status == 1:
+                if status > 1:
+                    spc.commit(1)
+                elif status in (0, 1):
                     break
-            unify(spc.merge(), X)
+            if status:
+                unify(Sol, spc.merge())
+            else:
+                unify(Sol, False)
 
-        s = newspace(problem)
+        s = newspace(conference_scheduling)
         Solution = newvar()
         stacklet(solve, s, Solution)
 
-        schedule()
-
-        assert Solution == (2, 3)
+        assert Solution == [('room B', 'day 1 PM'), ('room A', 'day 1 PM'),
+                            ('room B', 'day 2 AM'), ('room B', 'day 1 AM'),
+                            ('room A', 'day 2 PM'), ('room C', 'day 2 AM'),
+                            ('room C', 'day 2 PM'), ('room C', 'day 1 PM'),
+                            ('room C', 'day 1 AM'), ('room B', 'day 2 PM')]
 
-        assert len(sched_all()['threads']) == 1
+        #XXX
+        #assert len(sched_all()['threads']) == 1



More information about the Pypy-commit mailing list