[pypy-svn] r22687 - pypy/dist/pypy/lib/logic/store_without_cs

auc at codespeak.net auc at codespeak.net
Thu Jan 26 15:48:39 CET 2006


Author: auc
Date: Thu Jan 26 15:48:35 2006
New Revision: 22687

Added:
   pypy/dist/pypy/lib/logic/store_without_cs/
   pypy/dist/pypy/lib/logic/store_without_cs/constraint.py
   pypy/dist/pypy/lib/logic/store_without_cs/test_unification.py
   pypy/dist/pypy/lib/logic/store_without_cs/test_variable.py
   pypy/dist/pypy/lib/logic/store_without_cs/unification.py
   pypy/dist/pypy/lib/logic/store_without_cs/variable.py
Log:
(ale, auc)
* create a 'branch' with current version of store/variables/constraints which are not (almost not) aware of computation spaces
* this will allow us to try a merge of computation spaces / store 


Added: pypy/dist/pypy/lib/logic/store_without_cs/constraint.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/lib/logic/store_without_cs/constraint.py	Thu Jan 26 15:48:35 2006
@@ -0,0 +1,389 @@
+# a) new requirement : be able to postpone asking fo the
+# values of the domain
+
+#-- 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
+
+#-- Domains --------------------------------------------
+
+class AbstractDomain(object):
+    """Implements the functionnality related to the changed flag.
+    Can be used as a starting point for concrete domains"""
+
+    #__implements__ = DomainInterface
+    def __init__(self):
+        self.__changed = 0
+
+    def reset_flags(self):
+        self.__changed = 0
+    
+    def has_changed(self):
+        return self.__changed
+
+    def _value_removed(self):
+        """The implementation of remove_value should call this method"""
+        self.__changed = 1
+        if self.size() == 0:
+            raise ConsistencyFailure()
+
+
+class FiniteDomain(AbstractDomain):
+    """
+    Variable Domain with a finite set of possible values
+    """
+
+    _copy_count = 0
+    _write_count = 0
+    
+    def __init__(self, values):
+        """values is a list of values in the domain
+        This class uses a dictionnary to make sure that there are
+        no duplicate values"""
+        AbstractDomain.__init__(self)
+        if isinstance(values,FiniteDomain):
+            # do a copy on write
+            self._cow = True
+            values._cow = True
+            FiniteDomain._copy_count += 1
+            self._values = values._values
+        else:
+            # don't check this there (a)
+            #assert len(values) > 0
+            self.set_values(values)
+            
+        ##self.getValues = self._values.keys
+
+    def set_values(self, values):
+        self._cow = False
+        FiniteDomain._write_count += 1
+        self._values = set(values)
+        
+    def remove_value(self, value):
+        """Remove value of domain and check for consistency"""
+##         print "removing", value, "from", self._values.keys()
+        if self._cow:
+            self.set_values(self._values)
+        del self._values[value]
+        self._value_removed()
+
+    def remove_values(self, values):
+        """Remove values of domain and check for consistency"""
+        if self._cow:
+            self.set_values(self._values)
+        if values:
+##             print "removing", values, "from", self._values.keys()
+            for val in values :
+                self._values.remove(val)
+            self._value_removed()
+    __delitem__ = remove_value
+    
+    def size(self):
+        """computes the size of a finite domain"""
+        return len(self._values)
+    __len__ = size
+    
+    def get_values(self):
+        """return all the values in the domain
+           in an indexable sequence"""
+        return list(self._values)
+
+    def __iter__(self):
+        return iter(self._values)
+    
+    def copy(self):
+        """clone the domain"""
+        return FiniteDomain(self)
+    
+    def __repr__(self):
+        return '<FiniteDomain %s>' % str(self.get_values())
+
+    def __eq__(self, other):
+        if other is None: return False
+        return self._values == other._values
+
+    def intersection(self, other):
+        if other is None: return self.get_values()
+        return self._values & other._values
+
+#-- Constraints ------------------------------------------
+
+class AbstractConstraint(object):
+    
+    def __init__(self, variables):
+        """variables is a list of variables which appear in the formula"""
+        self._names_to_vars = {}
+        for var in variables:
+            if var.dom is None:
+                raise DomainlessVariables
+            self._names_to_vars[var.name] = var
+            var.add_constraint(self)
+        self._variables = variables
+
+    def affectedVariables(self):
+        """ Return a list of all variables affected by this constraint """
+        return self._variables
+
+    def isVariableRelevant(self, variable):
+        return variable in self._variables
+
+    def estimateCost(self, domains):
+        """Return an estimate of the cost of the narrowing of the constraint"""
+        return reduce(operator.mul,
+                      [domains[var].size() for var in self._variables])
+
+
+class BasicConstraint(object):
+    """A BasicConstraint, which is never queued by the Repository
+    A BasicConstraint affects only one variable, and will be entailed
+    on the first call to narrow()"""
+    
+    def __init__(self, variable, reference, operator):
+        """variables is a list of variables on which
+        the constraint is applied"""
+        self._variable = variable
+        self._reference = reference
+        self._operator = operator
+
+    def __repr__(self):
+        return '<%s %s %s>'% (self.__class__, self._variable, self._reference)
+
+    def isVariableRelevant(self, variable):
+        return variable == self._variable
+
+    def estimateCost(self, domains):
+        return 0 # get in the first place in the queue
+    
+    def affectedVariables(self):
+        return [self._variable]
+    
+    def getVariable(self):
+        return self._variable
+        
+    def narrow(self, domains):
+        domain = domains[self._variable]
+        operator = self._operator
+        ref = self._reference
+        try:
+            for val in domain.get_values() :
+                if not operator(val, ref) :
+                    domain.remove_value(val)
+        except ConsistencyFailure:
+            raise ConsistencyFailure('inconsistency while applying %s' % \
+                                     repr(self))
+        return 1
+
+
+def make_lambda_head(vars):
+    var_ids = ','.join([var.name for var in vars])
+    return 'lambda ' + var_ids + ':'
+
+def expand_expr_template(expr, vars):
+    for var in vars:
+        expr.replace(var.name, var.name + '.val')
+    return expr
+
+class Expression(AbstractConstraint):
+    """A constraint represented as a python expression."""
+    _FILTER_CACHE = {}
+
+    def __init__(self, variables, formula, type='fd.Expression'):
+        """variables is a list of variables which appear in the formula
+        formula is a python expression that will be evaluated as a boolean"""
+        AbstractConstraint.__init__(self, variables)
+        self.formula = formula
+        self.type = type
+        try:
+            self.filterFunc = Expression._FILTER_CACHE[formula]
+        except KeyError:
+            self.filterFunc = eval(make_lambda_head(variables) \
+                                   + expand_expr_template(formula, variables), {}, {})
+            Expression._FILTER_CACHE[formula] = self.filterFunc
+
+    def _init_result_cache(self):
+        """key = (variable,value), value = [has_success,has_failure]"""
+        result_cache = {}
+        for var_name in self._variables:
+            result_cache[var_name.name] = {}
+        return result_cache
+
+
+    def _assign_values(self):
+        variables = []
+        kwargs = {}
+        for variable in self._variables:
+            domain = variable.dom
+            values = domain.get_values()
+            variables.append((domain.size(), [variable, values, 0, len(values)]))
+            kwargs[variable.name] = values[0]
+        # sort variables to instanciate those with fewer possible values first
+        variables.sort()
+
+        go_on = 1
+        while go_on:
+            yield kwargs
+            # try to instanciate the next variable
+            for size, curr in variables:
+                if (curr[2] + 1) < curr[-1]:
+                    curr[2] += 1
+                    kwargs[curr[0].name] = curr[1][curr[2]]
+                    break
+                else:
+                    curr[2] = 0
+                    kwargs[curr[0].name] = curr[1][0]
+            else:
+                # it's over
+                go_on = 0
+            
+        
+    def narrow(self):
+        # removed domain arg. (auc, ale)
+        """generic narrowing algorithm for n-ary expressions"""
+        maybe_entailed = 1
+        ffunc = self.filterFunc
+        result_cache = self._init_result_cache()
+        for kwargs in self._assign_values():
+            if maybe_entailed:
+                for var, val in kwargs.iteritems():
+                    if val not in result_cache[var]:
+                        break
+                else:
+                    continue
+            if ffunc(**kwargs):
+                for var, val in kwargs.items():
+                    result_cache[var][val] = 1
+            else:
+                maybe_entailed = 0
+
+        try:
+            for var, keep in result_cache.iteritems():
+                domain = self._names_to_vars[var].dom
+                domain.remove_values([val for val in domain if val not in keep])
+                
+        except ConsistencyFailure:
+            raise ConsistencyFailure('Inconsistency while applying %s' % \
+                                     repr(self))
+        except KeyError:
+            # There are no more value in result_cache
+            pass
+
+        return maybe_entailed
+
+    def __repr__(self):
+        return '<%s "%s">' % (self.type, self.formula)
+
+class BinaryExpression(Expression):
+    """A binary constraint represented as a python expression
+
+    This implementation uses a narrowing algorithm optimized for
+    binary constraints."""
+    
+    def __init__(self, variables, formula, type = 'fd.BinaryExpression'):
+        assert len(variables) == 2
+        Expression.__init__(self, variables, formula, type)
+
+    def narrow(self, domains):
+        """specialized narrowing algorithm for binary expressions
+        Runs much faster than the generic version"""
+        maybe_entailed = 1
+        var1 = self._variables[0]
+        dom1 = domains[var1]
+        values1 = dom1.get_values()
+        var2 = self._variables[1]
+        dom2 = domains[var2]
+        values2 = dom2.get_values()
+        ffunc = self.filterFunc
+        if dom2.size() < dom1.size():
+            var1, var2 = var2, var1
+            dom1, dom2 = dom2, dom1
+            values1, values2 = values2, values1
+            
+        kwargs = {}
+        keep1 = {}
+        keep2 = {}
+        maybe_entailed = 1
+        try:
+            # iterate for all values
+            for val1 in values1:
+                kwargs[var1] = val1
+                for val2 in values2:
+                    kwargs[var2] = val2
+                    if val1 in keep1 and val2 in keep2 and maybe_entailed == 0:
+                        continue
+                    if ffunc(**kwargs):
+                        keep1[val1] = 1
+                        keep2[val2] = 1
+                    else:
+                        maybe_entailed = 0
+
+            dom1.remove_values([val for val in values1 if val not in keep1])
+            dom2.remove_values([val for val in values2 if val not in keep2])
+            
+        except ConsistencyFailure:
+            raise ConsistencyFailure('Inconsistency while applying %s' % \
+                                     repr(self))
+        except Exception:
+            print self, kwargs
+            raise 
+        return maybe_entailed
+
+
+def make_expression(variables, formula, constraint_type=None):
+    """create a new constraint of type Expression or BinaryExpression
+    The chosen class depends on the number of variables in the constraint"""
+    # encode unicode
+    vars = []
+    for var in variables:
+        if type(var) == type(u''):
+            vars.append(var.encode())
+        else:
+            vars.append(var)
+    if len(vars) == 2:
+        if constraint_type is not None:
+            return BinaryExpression(vars, formula, constraint_type)
+        else:
+            return BinaryExpression(vars, formula)
+
+    else:
+        if constraint_type is not None:
+            return Expression(vars, formula, constraint_type)
+        else:
+            return Expression(vars, formula)
+
+
+class Equals(BasicConstraint):
+    """A basic constraint variable == constant value"""
+    def __init__(self, variable, reference):
+        BasicConstraint.__init__(self, variable, reference, operator.eq)
+
+class NotEquals(BasicConstraint):
+    """A basic constraint variable != constant value"""
+    def __init__(self, variable, reference):
+        BasicConstraint.__init__(self, variable, reference, operator.ne)
+
+class LesserThan(BasicConstraint):
+    """A basic constraint variable < constant value"""
+    def __init__(self, variable, reference):
+        BasicConstraint.__init__(self, variable, reference, operator.lt)
+
+class LesserOrEqual(BasicConstraint):
+    """A basic constraint variable <= constant value"""
+    def __init__(self, variable, reference):
+        BasicConstraint.__init__(self, variable, reference, operator.le)
+
+class GreaterThan(BasicConstraint):
+    """A basic constraint variable > constant value"""
+    def __init__(self, variable, reference):
+        BasicConstraint.__init__(self, variable, reference, operator.gt)
+
+class GreaterOrEqual(BasicConstraint):
+    """A basic constraint variable >= constant value"""
+    def __init__(self, variable, reference):
+        BasicConstraint.__init__(self, variable, reference, operator.ge)

Added: pypy/dist/pypy/lib/logic/store_without_cs/test_unification.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/lib/logic/store_without_cs/test_unification.py	Thu Jan 26 15:48:35 2006
@@ -0,0 +1,376 @@
+import unification as u
+import variable as v
+import constraint as c
+from py.test import raises, skip
+from threading import Thread
+
+class FunThread(Thread):
+
+    def __init__(self, fun, *args):
+        Thread.__init__(self)
+        self.fun = fun
+        self.args = args
+
+    def run(self):
+        self.fun(self, *self.args)
+
+class TestUnification:
+    
+    def setup_method(self, meth):
+        u._store = u.Store()
+
+    def test_already_in_store(self):
+        x = u.var('x')
+        raises(v.AlreadyInStore, u.var, 'x')
+
+    def test_already_bound(self):
+        x = u.var('x')
+        u.bind(x, 42)
+        raises(u.AlreadyBound, u.bind, x, 42)
+
+    def test_bind_var_var(self):
+        x = u.var('x')
+        y = u.var('y')
+        z = u.var('z')
+        u.bind(x, z)
+        assert x.val == u.EqSet([x, z])
+        assert y.val == u.EqSet([y])
+        assert z.val == u.EqSet([x, z])
+
+    def test_bind_var_val(self):
+        x, y, z = u.var('x'), u.var('y'), u.var('z')
+        u.bind(x, z)
+        u.bind(y, 42)
+        u.bind(z, 3.14)
+        assert x.val == 3.14
+        assert y.val == 42
+        assert z.val == 3.14
+
+    def test_unify_same(self):
+        x,y,z,w = (u.var('x'), u.var('y'),
+                   u.var('z'), u.var('w'))
+        u.bind(x, [42, z])
+        u.bind(y, [z, 42])
+        u.bind(w, [z, 43])
+        raises(u.UnificationFailure, u.unify, x, w)
+        u.unify(x, y)
+        assert z.val == 42
+
+    def test_double_unification(self):
+        x, y, z = (u.var('x'), u.var('y'),
+                   u.var('z'))
+        u.bind(x, 42)
+        u.bind(y, z)
+        u.unify(x, y)
+        assert z.val == 42
+        u.unify(x, y)
+        assert (z.val == x.val) and (x.val == y.val)
+
+
+    def test_unify_values(self):
+        x, y = u.var('x'), u.var('y')
+        u.bind(x, [1, 2, 3])
+        u.bind(y, [1, 2, 3])
+        u.unify(x, y)
+        assert x.val == [1, 2, 3]
+        assert y.val == [1, 2, 3]
+
+    def test_unify_lists_success(self):
+        x,y,z,w = (u.var('x'), u.var('y'),
+                   u.var('z'), u.var('w'))
+        u.bind(x, [42, z])
+        u.bind(y, [w, 44])
+        u.unify(x, y)
+        assert x.val == [42, z]
+        assert y.val == [w, 44]
+        assert z.val == 44
+        assert w.val == 42
+
+    def test_unify_dicts_success(self):
+        x,y,z,w = (u.var('x'), u.var('y'),
+                   u.var('z'), u.var('w'))
+        u.bind(x, {1:42, 2:z})
+        u.bind(y, {1:w,  2:44})
+        u.unify(x, y)
+        assert x.val == {1:42, 2:z}
+        assert y.val == {1:w,  2:44}
+        assert z.val == 44
+        assert w.val == 42
+
+    def test_unify_failure(self):
+        x,y,z = u.var('x'), u.var('y'), u.var('z')
+        u.bind(x, [42, z])
+        u.bind(y, [z, 44])
+        raises(u.UnificationFailure, u.unify, x, y)
+        # check store consistency
+        assert x.val == [42, z]
+        assert y.val == [z, 44]
+        assert z.val == u.EqSet([z])
+
+    def test_unify_failure2(self):
+        x,y,z,w = (u.var('x'), u.var('y'),
+                   u.var('z'), u.var('w'))
+        u.bind(x, [42, z])
+        u.bind(y, [w, 44])
+        u.bind(z, w)
+        assert u._store.in_transaction == False
+        raises(u.UnificationFailure, u.unify, x, y)
+        assert u._store.in_transaction == False
+        # check store consistency
+        assert x.val == [42, z]
+        assert y.val == [w, 44]
+        assert z.val == u.EqSet([z,w])
+        assert w.val == u.EqSet([z,w])
+
+    def test_unify_circular(self):
+        x, y, z, w, a, b = (u.var('x'), u.var('y'),
+                            u.var('z'), u.var('w'),
+                            u.var('a'), u.var('b'))
+        u.bind(x, [y])
+        u.bind(y, [x])
+        raises(u.UnificationFailure, u.unify, x, y)
+        u.bind(z, [1, w])
+        u.bind(w, [z, 2])
+        raises(u.UnificationFailure, u.unify, z, w)
+        u.bind(a, {1:42, 2:b})
+        u.bind(b, {1:a,  2:42})
+        raises(u.UnificationFailure, u.unify, a, b)
+        # check store consistency
+        assert x.val == [y]
+        assert y.val == [x]
+        assert z.val == [1, w]
+        assert w.val == [z, 2]
+        assert a.val == {1:42, 2:b}
+        assert b.val == {1:a,  2:42}
+        
+        
+    def test_threads_creating_vars(self):
+        def create_var(thread, *args):
+            x = u.var('x')
+
+        def create_var2(thread, *args):
+            raises(v.AlreadyExists, u.var, 'x')
+
+        t1, t2 = (FunThread(create_var),
+                  FunThread(create_var2))
+        t1.start()
+        t2.start()
+
+
+    def test_threads_binding_vars(self):
+        
+        def do_stuff(thread, var, val):
+            thread.raised = False
+            try:
+                u.bind(var, val)
+            except u.AlreadyBound:
+                thread.raised = True
+            
+        x = u.var('x')
+        vars_ = []
+        for nvar in range(1000):
+            v = u.var('x-'+str(nvar))
+            u.bind(x, v)
+            vars_.append(v)
+
+        for var in u._store.vars:
+            assert var.val == x.val
+
+        t1, t2 = (FunThread(do_stuff, x, 42),
+                  FunThread(do_stuff, x, 43))
+        t1.start()
+        t2.start()
+        t1.join()
+        t2.join()
+        #check that every var is really bound to 42 or 43
+        for var in u._store.vars:
+            assert var.val == x.val
+        assert (t2.raised and not t1.raised) or \
+               (t1.raised and not t2.raised)
+    
+
+    def test_set_var_domain(self):
+        x = u.var('x')
+        u.set_domain(x, [1, 3, 5])
+        assert x.dom == c.FiniteDomain([1, 3, 5])
+
+    def test_bind_with_domain(self):
+        x = u.var('x')
+        u.set_domain(x, [1, 2, 3])
+        raises(u.OutOfDomain, u.bind, x, 42)
+        u.bind(x, 3)
+        assert x.val == 3
+
+    def test_bind_with_incompatible_domains(self):
+        x, y = u.var('x'), u.var('y')
+        u.set_domain(x, [1, 2])
+        u.set_domain(y, [3, 4])
+        raises(u.IncompatibleDomains, u.bind, x, y)
+        u.set_domain(y, [2, 4])
+        u.bind(x, y)
+        # check x and y are in the same equiv. set
+        assert x.val == y.val
+
+
+    def test_unify_with_domains(self):
+        x,y,z = u.var('x'), u.var('y'), u.var('z')
+        u.bind(x, [42, z])
+        u.bind(y, [z, 42])
+        u.set_domain(z, [1, 2, 3])
+        raises(u.UnificationFailure, u.unify, x, y)
+        u.set_domain(z, [41, 42, 43])
+        u.unify(x, y)
+        assert z.val == 42
+        assert z.dom == c.FiniteDomain([41, 42, 43])
+
+    def test_add_constraint(self):
+        x,y,z = u.var('x'), u.var('y'), u.var('z')
+        raises(c.DomainlessVariables,
+               c.Expression, [x, y, z], 'x == y + z')
+        x.dom = c.FiniteDomain([1, 2])
+        y.dom = c.FiniteDomain([2, 3])
+        z.dom = c.FiniteDomain([3, 4])
+        k = c.Expression([x, y, z], 'x == y + z')
+        u.add_constraint(k)
+        assert k in u._store.constraints
+
+    def test_narrowing_domains_failure(self):
+        x,y,z = u.var('x'), u.var('y'), u.var('z')
+        x.dom = c.FiniteDomain([1, 2])
+        y.dom = c.FiniteDomain([2, 3])
+        z.dom = c.FiniteDomain([3, 4])
+        k = c.Expression([x, y, z], 'x == y + z')
+        raises(c.ConsistencyFailure, k.narrow)
+
+    def test_narrowing_domains_success(self):
+        x,y,z = u.var('x'), u.var('y'), u.var('z')
+        x.dom = c.FiniteDomain([1, 2, 5])
+        y.dom = c.FiniteDomain([2, 3])
+        z.dom = c.FiniteDomain([3, 4])
+        k = c.Expression([x, y, z], 'x == y + z')
+        k.narrow()
+        assert x.dom == c.FiniteDomain([5])
+        assert y.dom == c.FiniteDomain([2])
+        assert z.dom == c.FiniteDomain([3])
+
+    def test_store_satisfiable_success(self):
+        x,y,z = u.var('x'), u.var('y'), u.var('z')
+        x.dom = c.FiniteDomain([1, 2, 5])
+        y.dom = c.FiniteDomain([2, 3])
+        z.dom = c.FiniteDomain([3, 4])
+        k = c.Expression([x, y, z], 'x == y + z')
+        u.add_constraint(k)
+        assert u.satisfiable(k) == True
+        assert x.dom == c.FiniteDomain([1, 2, 5])
+        assert y.dom == c.FiniteDomain([2, 3])
+        assert z.dom == c.FiniteDomain([3, 4])
+        
+    def test_store_satisfiable_failure(self):
+        x,y,z = u.var('x'), u.var('y'), u.var('z')
+        x.dom = c.FiniteDomain([1, 2])
+        y.dom = c.FiniteDomain([2, 3])
+        z.dom = c.FiniteDomain([3, 4])
+        k = c.Expression([x, y, z], 'x == y + z')
+        u.add_constraint(k)
+        assert u.satisfiable(k) == False
+        assert x.dom == c.FiniteDomain([1, 2])
+        assert y.dom == c.FiniteDomain([2, 3])
+        assert z.dom == c.FiniteDomain([3, 4])
+
+    def test_satisfiable_many_const_success(self):
+        x,y,z,w = (u.var('x'), u.var('y'),
+                   u.var('z'), u.var('w'))
+        x.dom = c.FiniteDomain([1, 2, 5])
+        y.dom = c.FiniteDomain([2, 3])
+        z.dom = c.FiniteDomain([3, 4])
+        w.dom = c.FiniteDomain([1, 4, 5])
+        k1 = c.Expression([x, y, z], 'x == y + z')
+        k2 = c.Expression([z, w], 'z < w')
+        u.add_constraint(k1)
+        u.add_constraint(k2)
+        assert u.satisfiable(k1) == True
+        assert x.dom == c.FiniteDomain([1, 2, 5])
+        assert y.dom == c.FiniteDomain([2, 3])
+        assert z.dom == c.FiniteDomain([3, 4])
+        assert w.dom == c.FiniteDomain([1, 4, 5])
+        assert u.satisfiable(k2) == True
+        assert x.dom == c.FiniteDomain([1, 2, 5])
+        assert y.dom == c.FiniteDomain([2, 3])
+        assert z.dom == c.FiniteDomain([3, 4])
+        assert w.dom == c.FiniteDomain([1, 4, 5])
+        narrowed_doms = u.get_satisfying_domains(k1)
+        assert narrowed_doms == {x:c.FiniteDomain([5]),
+                                 y:c.FiniteDomain([2]),
+                                 z:c.FiniteDomain([3]),
+                                 w:c.FiniteDomain([4, 5])}
+        narrowed_doms = u.get_satisfying_domains(k2)
+        assert narrowed_doms == {x:c.FiniteDomain([5]),
+                                 y:c.FiniteDomain([2]),
+                                 z:c.FiniteDomain([3]),
+                                 w:c.FiniteDomain([4, 5])}
+
+
+    def test_satisfiable_many_const_failure(self):
+        x,y,z,w = (u.var('x'), u.var('y'),
+                   u.var('z'), u.var('w'))
+        x.dom = c.FiniteDomain([1, 2, 5])
+        y.dom = c.FiniteDomain([2, 3])
+        z.dom = c.FiniteDomain([3, 4])
+        w.dom = c.FiniteDomain([1])
+        k1 = c.Expression([x, y, z], 'x == y + z')
+        k2 = c.Expression([z, w], 'z < w')
+        u.add_constraint(k1)
+        u.add_constraint(k2)
+        assert u.satisfiable(k1) == False
+        assert x.dom == c.FiniteDomain([1, 2, 5])
+        assert y.dom == c.FiniteDomain([2, 3])
+        assert z.dom == c.FiniteDomain([3, 4])
+        assert w.dom == c.FiniteDomain([1])
+        assert u.satisfiable(k2) == False
+        assert x.dom == c.FiniteDomain([1, 2, 5])
+        assert y.dom == c.FiniteDomain([2, 3])
+        assert z.dom == c.FiniteDomain([3, 4])
+        assert w.dom == c.FiniteDomain([1])
+        narrowed_doms = u.get_satisfying_domains(k1)
+        assert narrowed_doms == {}
+        narrowed_doms = u.get_satisfying_domains(k2)
+        assert narrowed_doms == {}
+
+    def test_satisfy_many_const_failure(self):
+        x,y,z,w = (u.var('x'), u.var('y'),
+                   u.var('z'), u.var('w'))
+        x.dom = c.FiniteDomain([1, 2, 5])
+        y.dom = c.FiniteDomain([2, 3])
+        z.dom = c.FiniteDomain([3, 4])
+        w.dom = c.FiniteDomain([1])
+        k1 = c.Expression([x, y, z], 'x == y + z')
+        k2 = c.Expression([z, w], 'z < w')
+        u.add_constraint(k1)
+        u.add_constraint(k2)
+        raises(u.ConsistencyFailure, u.satisfy, k1)
+        assert x.dom == c.FiniteDomain([1, 2, 5])
+        assert y.dom == c.FiniteDomain([2, 3])
+        assert z.dom == c.FiniteDomain([3, 4])
+        assert w.dom == c.FiniteDomain([1])
+        raises(u.ConsistencyFailure, u.satisfy, k2)
+        assert x.dom == c.FiniteDomain([1, 2, 5])
+        assert y.dom == c.FiniteDomain([2, 3])
+        assert z.dom == c.FiniteDomain([3, 4])
+        assert w.dom == c.FiniteDomain([1])
+        
+    def test_satisfy_many_const_success(self):
+        x,y,z,w = (u.var('x'), u.var('y'),
+                   u.var('z'), u.var('w'))
+        x.dom = c.FiniteDomain([1, 2, 5])
+        y.dom = c.FiniteDomain([2, 3])
+        z.dom = c.FiniteDomain([3, 4])
+        w.dom = c.FiniteDomain([1, 4, 5])
+        k1 = c.Expression([x, y, z], 'x == y + z')
+        k2 = c.Expression([z, w], 'z < w')
+        u.add_constraint(k1)
+        u.add_constraint(k2)
+        u.satisfy(k2)
+        assert x.dom == c.FiniteDomain([5])
+        assert y.dom == c.FiniteDomain([2])
+        assert z.dom == c.FiniteDomain([3])
+        assert w.dom == c.FiniteDomain([4, 5])

Added: pypy/dist/pypy/lib/logic/store_without_cs/test_variable.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/lib/logic/store_without_cs/test_variable.py	Thu Jan 26 15:48:35 2006
@@ -0,0 +1,67 @@
+from py.test import raises
+import unification as u
+import variable as v
+from threading import Thread
+
+class Consumer(Thread):
+
+    def give_var(self, var):
+        self.var = var
+
+    def run(self):
+        val = self.var.get()
+
+class NConsumer(Thread):
+
+    def give_vars(self, vars_):
+        self.vars = vars_
+
+    def run(self):
+        val = [var.get() for var in self.vars]
+
+class TestVariable:
+
+    def setup_method(self, meth):
+        u._store = u.Store()
+
+    def test_no_same_name(self):
+        x = u.var('x')
+        raises(u.AlreadyInStore, u.var, 'x')
+
+    def test_get_by_name(self):
+        x = u.var('x')
+        assert x == u._store.get_var_by_name('x')
+        raises(u.NotInStore, u._store.get_var_by_name, 'y')
+
+    def test_one_thread_reading_one_var(self):
+        cons = Consumer()
+        x = u.var('x')
+        cons.give_var(x)
+        cons.start()
+        u.bind(x, 42)
+        cons.join()
+        assert cons.var.val == 42
+
+    def test_many_threads_reading_one_var(self):
+        conss = [Consumer() for i in range(10)]
+        x = u.var('x')
+        for cons in conss:
+            cons.give_var(x)
+            cons.start()
+        u.bind(x, 42)
+        for cons in conss:
+            cons.join()
+        assert cons.var.val == 42
+
+    def test_many_thread_reading_many_var(self):
+        conss = [NConsumer() for i in range(10)]
+        vars_ = [u.var(str(i)) for i in range(10)]
+        for cons in conss:
+            cons.give_vars(vars_)
+            cons.start()
+        for var in vars_:
+            u.bind(var, var.name)
+        for cons in conss:
+            cons.join()
+        for i in range(10):
+            assert vars_[i].val == str(i)

Added: pypy/dist/pypy/lib/logic/store_without_cs/unification.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/lib/logic/store_without_cs/unification.py	Thu Jan 26 15:48:35 2006
@@ -0,0 +1,573 @@
+## Oz-like unification of dataflow variables in Python 2.4
+## within a single assignment store
+## crude ...
+
+## Unification over dataflow variables in Oz is a powerful mechanism
+## which is the basis of relational, logic and constraint programming.
+## Please read the stuff in variable.py to understand the nature of
+## dataflow variables.
+##
+## Unification in Oz (almost verbatim from CTM*)
+## =============================================
+##
+## Unification as put in Oz is "a very powerful operation". It is
+## provided through the '=' operator.
+##
+## Binding a variable to a value is a special case of an operation
+## called unification. The unification <TERM1>=<TERM2> makes the
+## partial values <TERM1> and <TERM2> equal, if possible, by adding
+## zero or more bindings to the store. For example, f(X Y)=f(1 2) does
+## two bindings: X=1 and Y=2. If the two terms cannot be made equal,
+## then an exception is raised. Unification exists because of partial
+## values; if there would be only complete values, then it would have
+## no meaning.
+##
+## Unification adds information to the single-assignment store (a set
+## of dataflow variables, where each variable is either unbound or
+## bound to some other store entity).
+##
+## Example : if the store has X=foo(A) and Y=foo(25), then doing X=Y
+## will bind A to 25.
+##
+## *failure* is the exception raised when unification cannot
+## happen. For instance (unification of two records) :
+##
+##  person(name:X1 age:23)
+##  person(name:"Auc" age:32)
+##
+## will raise it. The fail statement raises this exception.
+##
+## It is symetric : X=Y means the same as Y=X
+## It can create cyclic structures, as in : X=person(grandfather:X)
+## It can bind cyclic structures :
+##
+##  X=f(a:X b:_)
+##  Y=f(a:_ b:Y)
+##  X=Y
+##
+## creates a two cycles structure writable as : X=f(a:X b:X)
+##
+## Unification algorithm :
+##
+## operation unify(x, y) that unify two partial values x and y in the
+## store st.
+##
+## The Store consists of a set of k variables : x1, ... xk that are
+## partitioned as follows:
+##
+## * set of unbound variables that are equal (also called equivalence
+##   sets of variables). The variables in each set are equal to each
+##   other but not to any other variables.
+##
+## * variables bound to a number, record or procedure (also called
+##   determined variables).
+##
+## Example : st = {x1=foo(a:x2), x2=25, x3=x4=x5, x6, x7=x8} that has
+## 8 variables. It has three equivalence sets :
+## {x3,x4,x5},{x6},{x7,x8}. It has two determined variables x1 and x2.
+##
+## The primitive bind operation : unification is defined in terms of a
+## primitive bind operation on the store st. The operation binds all
+## variables in an equivalence set:
+##
+## * bind(ES,<v>) binds all variables in the equivalence set ES to the
+##   number or record <v>. For example, the operation
+##   bind({x7,x8},foo(a:x2)) binds x7 and x8, which no longer are in
+##   an equivalence set.
+##
+## * bind(ES1,ES2) merges the equivalence set ES1 with the equivalence
+##   set ES2.
+##
+## Algorithm unify(x,y)
+##
+## 1. if x is in the equivalence set ESx and y is in the equivalence
+##    set ESy, then do bind(ESx,ESy). It is a noop if ESx == ESy.
+##
+## 2. if x is in the equivalence set ESx and y is determined**, then do
+##    bind(ESx,y)
+##
+## 3. if y is in the equivalence set ESy and y is determined, then do
+##    bind(ESy,x)
+##
+## 4. if x is bound to l(l1:x1,...,ln:xn) and y is bound to
+##    l'(l'1:y1,...,l'm:ym) with l!=l' or {l1,...,ln}!={l'1,...,l'm},
+##    then raise a failure exception
+##
+## 5. if x is bound to l(l1:x1,...,ln:xn) and y is bound to
+##    l(l1:y1,...,ln:yn), then for i from 1 to n do unify(xi,yi).
+##
+##
+## With cycles : the above algorithm does not handle unification of
+## partial values with cycles. For example, on x=f(a:x) and y=f(a:y) a
+## call to unify(x,y) results in the recursive call unify(x,y), which
+## is identical to the original call, looping forever.  A simple fix
+## is to make sure that unify(x,y) is called at most once for each
+## possible pair of two variables (x,y); We do this through
+## memoization of called pairs.
+
+## * CTM : Concepts, Techniques and Models of Computer Programming
+## ** determined == bound
+
+#TODO :
+# * understand this :
+#   http://www.mozart-oz.org/papers/abstracts/ProgrammingConstraintServices.html
+# * support '_' as shown above
+# * turn Var into some dataflow-ish thing (as far as Python allows)
+# * ensure that the store supports concurrent access
+#   (using the implicit blocking provided by dataflow vars)
+# * add entailment checks
+# * add constraints support
+
+import threading
+
+from variable import EqSet, Var, \
+     VariableException, NotAVariable, AlreadyInStore
+from constraint import FiniteDomain, ConsistencyFailure
+
+#----------- Store Exceptions ----------------------------
+class UnboundVariable(VariableException):
+    def __str__(self):
+        return "%s has no value yet" % self.name
+
+class AlreadyBound(VariableException):
+    def __str__(self):
+        return "%s is already bound" % self.name
+
+class NotInStore(VariableException):
+    def __str__(self):
+        return "%s not in the store" % self.name
+
+class OutOfDomain(VariableException):
+    def __str__(self):
+        return "value not in domain of %s" % self.name
+
+class UnificationFailure(Exception):
+    def __init__(self, var1, var2, cause=None):
+        self.var1, self.var2 = (var1, var2)
+        self.cause = cause
+    def __str__(self):
+        diag = "%s %s can't be unified"
+        if self.cause:
+            diag += " because %s" % self.cause
+        return diag % (self.var1, self.var2)
+        
+class IncompatibleDomains(Exception):
+    def __init__(self, var1, var2):
+        self.var1, self.var2 = (var1, var2)
+    def __str__(self):
+        return "%s %s have incompatible domains" % \
+               (self.var1, self.var2)
+    
+#----------- Store ------------------------------------
+class Store(object):
+    """The Store consists of a set of k variables
+       x1,...,xk that are partitioned as follows: 
+       * set of unbound variables that are equal
+         (also called equivalence sets of variables).
+         The variables in each set are equal to each
+         other but not to any other variables.
+       * variables bound to a number, record or procedure
+         (also called determined variables)."""
+    
+    def __init__(self):
+        self.vars = set()
+        # mapping of names to vars (all of them)
+        self.names = {}
+        # set of all constraints 
+        self.constraints = set()
+        # consistency-preserving stuff
+        self.in_transaction = False
+        self.lock = threading.RLock()
+
+    #-- Variables ----------------------------
+
+    def var(self, name):
+        """creates a variable of name name and put
+           it into the store"""
+        v = Var(name, self)
+        self.add_unbound(v)
+        return v
+
+    def add_unbound(self, var):
+        """add unbound variable to the store"""
+        if var in self.vars:
+            raise AlreadyInStore(var.name)
+        print "adding %s to the store" % var
+        self.vars.add(var)
+        self.names[var.name] = var
+        # put into new singleton equiv. set
+        var.val = EqSet([var])
+
+    def set_domain(self, var, dom):
+        """bind variable to domain"""
+        assert(isinstance(var, Var) and (var in self.vars))
+        if var.is_bound():
+            raise AlreadyBound
+        var.dom = FiniteDomain(dom)
+
+    def get_var_by_name(self, name):
+        try:
+            return self.names[name]
+        except KeyError:
+            raise NotInStore(name)
+    
+    #-- Constraints -------------------------
+
+    def add_constraint(self, constraint):
+        self.constraints.add(constraint)
+
+    def get_variables_with_a_domain(self):
+        varset = set()
+        for var in self.vars:
+            if var.dom: varset.add(var)
+        return varset
+
+    def satisfiable(self, constraint):
+        """ * satisfiable (k) checks that the constraint k
+              can be satisfied wrt its variable domains
+              and other constraints on these variables
+            * does NOT mutate the store
+        """
+        # Satisfiability of one constraints entails
+        # satisfiability of the transitive closure
+        # of all constraints associated with the vars
+        # of our given constraint.
+        # We make a copy of the domains
+        # then traverse the constraints & attached vars
+        # to collect all (in)directly affected vars
+        # then compute narrow() on all (in)directly
+        # affected constraints.
+        assert constraint in self.constraints
+        varset = set()
+        constset = set()
+        compute_dependant_vars(constraint, varset, constset)
+        old_domains = collect_domains(varset)
+        
+        for const in constset:
+            try:
+                const.narrow()
+            except ConsistencyFailure:
+                restore_domains(old_domains)
+                return False
+        restore_domains(old_domains)
+        return True
+
+
+    def get_satisfying_domains(self, constraint):
+        assert constraint in self.constraints
+        varset = set()
+        constset = set()
+        compute_dependant_vars(constraint, varset, constset)
+        old_domains = collect_domains(varset)
+
+        for const in constset:
+            try:
+                const.narrow()
+            except ConsistencyFailure:
+                restore_domains(old_domains)
+                return {}
+        narrowed_domains = collect_domains(varset)
+        restore_domains(old_domains)
+        return narrowed_domains
+
+    def satisfy(self, constraint):
+        assert constraint in self.constraints
+        varset = set()
+        constset = set()
+        compute_dependant_vars(constraint, varset, constset)
+        old_domains = collect_domains(varset)
+
+        for const in constset:
+            try:
+                const.narrow()
+            except ConsistencyFailure:
+                restore_domains(old_domains)
+                raise
+
+    def satisfy_all(self):
+        old_domains = collect_domains(self.vars)
+        for const in self.constraints:
+            try:
+                const.narrow()
+            except ConsistencyFailure:
+                restore_domains(old_domains)
+                raise
+                
+        
+    #-- BIND -------------------------------------------
+
+    def bind(self, var, val):
+        """1. (unbound)Variable/(unbound)Variable or
+           2. (unbound)Variable/(bound)Variable or
+           3. (unbound)Variable/Value binding
+        """
+        try:
+            self.lock.acquire()
+            assert(isinstance(var, Var) and (var in self.vars))
+            if var == val:
+                return
+            if _both_are_vars(var, val):
+                if _both_are_bound(var, val):
+                    raise AlreadyBound(var.name)
+                if var._is_bound(): # 2b. var is bound, not var
+                    self.bind(val, var)
+                elif val._is_bound(): # 2a.var is bound, not val
+                    self._bind(var.val, val.val)
+                else: # 1. both are unbound
+                    self._merge(var, val)
+            else: # 3. val is really a value
+                print "%s, is that you ?" % var
+                if var._is_bound():
+                    raise AlreadyBound(var.name)
+                self._bind(var.val, val)
+        finally:
+            self.lock.release()
+
+
+    def _bind(self, eqs, val):
+        # print "variable - value binding : %s %s" % (eqs, val)
+        # bind all vars in the eqset to val
+        for var in eqs:
+            if var.dom != None:
+                if val not in var.dom.get_values():
+                    # undo the half-done binding
+                    for v in eqs:
+                        v.val = eqs
+                    raise OutOfDomain(var)
+            var.val = val
+
+    def _merge(self, v1, v2):
+        for v in v1.val:
+            if not _compatible_domains(v, v2.val):
+                raise IncompatibleDomains(v1, v2)
+        self._really_merge(v1.val, v2.val)
+
+    def _really_merge(self, eqs1, eqs2):
+        # print "unbound variables binding : %s %s" % (eqs1, eqs2)
+        if eqs1 == eqs2: return
+        # merge two equisets into one
+        eqs1 |= eqs2
+        # let's reassign everybody to the merged eq
+        for var in eqs1:
+            var.val = eqs1
+
+    #-- UNIFY ------------------------------------------
+
+    def unify(self, x, y):
+        self.in_transaction = True
+        try:
+            try:
+                self._really_unify(x, y)
+                for var in self.vars:
+                    if var.changed:
+                        var._commit()
+            except Exception, cause:
+                for var in self.vars:
+                    if var.changed:
+                        var._abort()
+                if isinstance(cause, UnificationFailure):
+                    raise
+                raise UnificationFailure(x, y, cause)
+        finally:
+            self.in_transaction = False
+
+    def _really_unify(self, x, y):
+        # print "unify %s with %s" % (x,y)
+        if not _unifiable(x, y): raise UnificationFailure(x, y)
+        if not x in self.vars:
+            if not y in self.vars:
+                # duh ! x & y not vars
+                if x != y: raise UnificationFailure(x, y)
+                else: return
+            # same call, reverse args. order
+            self._unify_var_val(y, x)
+        elif not y in self.vars:
+            # x is Var, y a value
+            self._unify_var_val(x, y)
+        elif _both_are_bound(x, y):
+            self._unify_bound(x,y)
+        elif x._is_bound():
+            self.bind(x,y)
+        else:
+            self.bind(y,x)
+
+    def _unify_var_val(self, x, y):
+        if x.val != y:
+            try:
+                self.bind(x, y)
+            except AlreadyBound:
+                raise UnificationFailure(x, y)
+        
+    def _unify_bound(self, x, y):
+        # print "unify bound %s %s" % (x, y)
+        vx, vy = (x.val, y.val)
+        if type(vx) in [list, set] and isinstance(vy, type(vx)):
+            self._unify_iterable(x, y)
+        elif type(vx) is dict and isinstance(vy, type(vx)):
+            self._unify_mapping(x, y)
+        else:
+            if vx != vy:
+                raise UnificationFailure(x, y)
+
+    def _unify_iterable(self, x, y):
+        print "unify sequences %s %s" % (x, y)
+        vx, vy = (x.val, y.val)
+        idx, top = (0, len(vx))
+        while (idx < top):
+            self._really_unify(vx[idx], vy[idx])
+            idx += 1
+
+    def _unify_mapping(self, x, y):
+        # print "unify mappings %s %s" % (x, y)
+        vx, vy = (x.val, y.val)
+        for xk in vx.keys():
+            self._really_unify(vx[xk], vy[xk])
+
+
+def _compatible_domains(var, eqs):
+    """check that the domain of var is compatible
+       with the domains of the vars in the eqs
+    """
+    if var.dom == None: return True
+    empty = set()
+    for v in eqs:
+        if v.dom == None: continue
+        if v.dom.intersection(var.dom) == empty:
+            return False
+    return True
+
+
+def compute_dependant_vars(constraint, varset,
+                           constset):
+    if constraint in constset: return
+    constset.add(constraint)
+    for var in constraint.affectedVariables():
+        varset.add(var)
+        dep_consts = var.constraints
+        for const in dep_consts:
+            if const in constset:
+                continue
+            compute_dependant_vars(const, varset, constset)
+
+
+#-- collect / restore utilities for domains
+
+def collect_domains(varset):
+    """makes a copy of domains of a set of vars
+       into a var -> dom mapping
+    """
+    dom = {}
+    for var in varset:
+        if var.dom:
+            dom[var] = FiniteDomain(var.dom)
+    return dom
+
+def restore_domains(domains):
+    """sets the domain of the vars in the domains mapping
+       to their (previous) value 
+    """
+    for var, dom in domains.items():
+        var.dom = dom
+
+
+#-- Unifiability checks---------------------------------------
+#--
+#-- quite costly & could be merged back in unify
+
+def _iterable(thing):
+    return type(thing) in [list, set]
+
+def _mapping(thing):
+    return type(thing) is dict
+
+# memoizer for _unifiable
+_unifiable_memo = set()
+
+def _unifiable(term1, term2):
+    global _unifiable_memo
+    _unifiable_memo = set()
+    return _really_unifiable(term1, term2)
+        
+def _really_unifiable(term1, term2):
+    """Checks wether two terms can be unified"""
+    if ((id(term1), id(term2))) in _unifiable_memo: return False
+    _unifiable_memo.add((id(term1), id(term2)))
+    # print "unifiable ? %s %s" % (term1, term2)
+    if _iterable(term1):
+        if _iterable(term2):
+            return _iterable_unifiable(term1, term2)
+        return False
+    if _mapping(term1) and _mapping(term2):
+        return _mapping_unifiable(term1, term2)
+    if not(isinstance(term1, Var) or isinstance(term2, Var)):
+        return term1 == term2 # same 'atomic' object
+    return True
+        
+def _iterable_unifiable(c1, c2):
+   """Checks wether two iterables can be unified"""
+   # print "unifiable sequences ? %s %s" % (c1, c2)
+   if len(c1) != len(c2): return False
+   idx, top = (0, len(c1))
+   while(idx < top):
+       if not _really_unifiable(c1[idx], c2[idx]):
+           return False
+       idx += 1
+   return True
+
+def _mapping_unifiable(m1, m2):
+    """Checks wether two mappings can be unified"""
+    # print "unifiable mappings ? %s %s" % (m1, m2)
+    if len(m1) != len(m2): return False
+    if m1.keys() != m2.keys(): return False
+    v1, v2 = (m1.items(), m2.items())
+    v1.sort()
+    v2.sort()
+    return _iterable_unifiable([e[1] for e in v1],
+                               [e[1] for e in v2])
+
+#-- Some utilities -----------------------------------------------
+
+def _both_are_vars(v1, v2):
+    return isinstance(v1, Var) and isinstance(v2, Var)
+    
+def _both_are_bound(v1, v2):
+    return v1._is_bound() and v2._is_bound()
+
+#--
+#-- the global store
+_store = Store()
+
+#-- global accessor functions
+def var(name):
+    v = Var(name, _store)
+    _store.add_unbound(v)
+    return v
+
+def set_domain(var, dom):
+    return _store.set_domain(var, dom)
+
+def add_constraint(constraint):
+    return _store.add_constraint(constraint)
+
+def satisfiable(constraint):
+    return _store.satisfiable(constraint)
+
+def get_satisfying_domains(constraint):
+    return _store.get_satisfying_domains(constraint)
+
+def satisfy(constraint):
+    return _store.satisfy(constraint)
+
+def bind(var, val):
+    return _store.bind(var, val)
+
+def unify(var1, var2):
+    return _store.unify(var1, var2)
+
+def bound():
+    return map(_store.vars,
+               lambda v: v.is_bound())
+
+def unbound():
+    return map(_store.vars,
+               lambda v: not v.is_bound())

Added: pypy/dist/pypy/lib/logic/store_without_cs/variable.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/lib/logic/store_without_cs/variable.py	Thu Jan 26 15:48:35 2006
@@ -0,0 +1,125 @@
+# First cut at representing Oz dataflow variable
+
+import threading
+
+#----------- Exceptions ---------------------------------
+class VariableException(Exception):
+    def __init__(self, name):
+        self.name = name
+
+class AlreadyInStore(VariableException):
+    def __str__(self):
+        return "%s already in store" % self.name
+
+class NotAVariable(VariableException):
+    def __str__(self):
+        return "%s is not a variable" % self.name
+
+#----------- Variables ----------------------------------
+class EqSet(set):
+    """An equivalence set for variables"""
+
+##     def __str__(self):
+##         if len(self) == 0:
+##             return ''
+##         for var in self:
+##             '='.join(var.name)
+
+class NoValue:
+    pass
+
+class Var(object):
+
+    def __init__(self, name, store):
+        if name in store.names:
+            raise AlreadyInStore(name)
+        self.name = name
+        self.store = store
+        # top-level 'commited' binding
+        self._val = NoValue
+        # domains in a flat world
+        self.dom = None        
+        # domains in multiple spaces
+        self.doms = {}
+        # constraints
+        self.constraints = set()
+        # when updated in a 'transaction', keep track
+        # of our initial value (for abort cases)
+        self.previous = None
+        self.changed = False
+        # a condition variable for concurrent access
+        self.mutex = threading.Lock()
+        self.value_condition = threading.Condition(self.mutex)
+
+    # for consumption by the global store
+
+    def _is_bound(self):
+        return not isinstance(self._val, EqSet) \
+               and self._val != NoValue
+
+    # 'transaction' support
+
+    def _commit(self):
+        self.changed = False
+
+    def _abort(self):
+        self.val = self.previous
+        self.changed = False
+
+    # value accessors
+    def _set_val(self, val):
+        self.value_condition.acquire()
+        if self.store.in_transaction:
+            if not self.changed:
+                self.previous = self._val
+                self.changed = True
+        self._val = val
+        self.value_condition.notifyAll()
+        self.value_condition.release()
+        
+    def _get_val(self):
+        return self._val
+    val = property(_get_val, _set_val)
+
+    def __str__(self):
+        if self.is_bound():
+            return "%s = %s" % (self.name, self.val)
+        return "%s" % self.name
+
+    def __repr__(self):
+        return self.__str__()
+
+    def __eq__(self, thing):
+        return isinstance(thing, Var) \
+               and self.name == thing.name
+
+    def __hash__(self):
+        return self.name.__hash__()
+
+
+    def add_constraint(self, constraint):
+        self.constraints.add(constraint)
+
+    is_bound = _is_bound
+
+    def cs_set_dom(self, cs, dom):
+        self.doms[cs] = dom
+
+    def cs_get_dom(self, cs):
+        return self.doms[cs]
+
+    #---- Concurrent public ops --------------------------
+    # should be used by threads that want to block on
+    # unbound variables
+
+    def get(self):
+        """Make threads wait on the variable
+           being bound
+        """
+        try:
+            self.value_condition.acquire()
+            while not self._is_bound():
+                self.value_condition.wait()
+            return self.val
+        finally:
+            self.value_condition.release()



More information about the Pypy-commit mailing list