[pypy-svn] r22658 - pypy/dist/pypy/lib/logic

auc at codespeak.net auc at codespeak.net
Wed Jan 25 17:24:10 CET 2006


Author: auc
Date: Wed Jan 25 17:24:07 2006
New Revision: 22658

Modified:
   pypy/dist/pypy/lib/logic/computationspace.py
   pypy/dist/pypy/lib/logic/distributor.py
   pypy/dist/pypy/lib/logic/test_computationspace.py
   pypy/dist/pypy/lib/logic/unification.py
   pypy/dist/pypy/lib/logic/variable.py
Log:
(ale, auc)
* added scope discipline to domains in spaces
* preliminary Choose support (with distribute & friends)
* many fixes & adjustments
* removed the GPL snippet
* maybe discovered a bug in CPython


Modified: pypy/dist/pypy/lib/logic/computationspace.py
==============================================================================
--- pypy/dist/pypy/lib/logic/computationspace.py	(original)
+++ pypy/dist/pypy/lib/logic/computationspace.py	Wed Jan 25 17:24:07 2006
@@ -198,12 +198,17 @@
 class ComputationSpace(object):
 
     def __init__(self, program, parent=None):
+        if parent is None:
+            self.store = Store()
+            self.root = self.store.var('root')
+            self.store.bind(self.root, program(self))
+        else:
+            self.store = parent.store
+            self.root = parent.root
         self.program = program
         self.parent = parent
-        self.store = Store()
         self.status = Unprocessed
-        self.root = self.store.var('root')
-        self.store.bind(self.root, program(self))
+        self.children = set()
 
     def _stable(self):
         #XXX: really ?
@@ -234,10 +239,14 @@
         else:
             self.status = Succeeded
 
-    def make_child(self):
-        return ComputationSpace(self.program, parent=self)
+    def make_children(self):
+        for dommap in self.distributor.distribute():
+            cs = ComputationSpace(lambda cs : True,
+                                  parent=self)
+            self.children.add(cs)
+            for var, dom in dommap.items():
+                var.cs_set_dom(cs, dom)
+
+
 
-    def choose(self, alternative):
-        """distribute the domains to new spaces
-           create the spaces"""
         

Modified: pypy/dist/pypy/lib/logic/distributor.py
==============================================================================
--- pypy/dist/pypy/lib/logic/distributor.py	(original)
+++ pypy/dist/pypy/lib/logic/distributor.py	Wed Jan 25 17:24:07 2006
@@ -1,71 +1,61 @@
-# (c) 2000-2001 LOGILAB S.A. (Paris, FRANCE).
-# http://www.logilab.fr/ -- mailto:contact at logilab.fr
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free Software
-# Foundation; either version 2 of the License, or (at your option) any later
-# version.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307
-# USA.
-
-"""
-distributors - part of Logilab's constraint satisfaction solver.
-"""
-
-__revision__ = '$Id: distributors.py,v 1.25 2005/01/14 15:16:21 alf Exp $'
-
 import math, random
 
-def make_new_domains(domains):
-    """return a shallow copy of dict of domains passed in argument"""
-    domain = {}
-    for key, value in domains.items():
-        domain[key] = value.copy()
-    return domain
+def make_new_domains(variables):
+    """updates variables with copied domains indexed by computation space"""
+    new_doms = {}
+    for var in variables:
+        new_doms[var] = var.dom.copy()
+    return new_doms
 
 class AbstractDistributor(object):
     """_distribute is left unimplemented."""
 
-    def __init__(self, nb_subspaces=2):
+    def __init__(self, c_space, nb_subspaces=2):
         self.nb_subspaces = nb_subspaces
+        self.c_space = c_space
         self.verbose = 0
-        
-    def findSmallestDomain(self, domains):
+            
+    def findSmallestDomain(self):
         """returns the variable having the smallest domain.
         (or one of such varibles if there is a tie)
         """
-        domlist = [(dom.size(), variable ) for variable, dom in domains.items()
-                                           if dom.size() > 1]
-        domlist.sort()
-        return domlist[0][1]
+        vars_ = [var for var in self.c_space.store.get_variables_with_a_domain()
+                 if var.dom.size() > 1]
+        
+        best = vars_[0]
+        for var in vars_:
+            if var.dom.size() < best.dom.size():
+                best = var
+        
+        return best
 
-    def findLargestDomain(self, domains):
+    def findLargestDomain(self):
         """returns the variable having the largest domain.
         (or one of such variables if there is a tie)
         """
-        domlist = [(dom.size(), variable) for variable, dom in domains.items()
-                                          if dom.size() > 1]
-        domlist.sort()
-        return domlist[-1][1]
+        vars_ = [var for var in self.c_space.store.get_variables_with_a_domain()
+                 if var.dom.size() > 1]
+
+        best = vars_[0]
+        for var in vars_:
+            if var.dom.size() > best.dom.size():
+                best = var
+        
+        return best
+
 
     def nb_subdomains(self, domains):
         """return number of sub domains to explore"""
         return self.nb_subspaces
 
-    def distribute(self, domains, verbose=0):
+    def distribute(self, verbose=0):
         """do the minimal job and let concrete class distribute variables
         """
         self.verbose = verbose
+        variables = self.c_space.store.get_variables_with_a_domain()
         replicas = []
-        for i in range(self.nb_subdomains(domains)):
-            replicas.append(make_new_domains(domains))
+        for i in range(self.nb_subdomains(variables)):
+            replicas.append(make_new_domains(variables))
         modified_domains = self._distribute(*replicas)
         for domain in modified_domains:
             domain.reset_flags()
@@ -85,9 +75,6 @@
     The first new domain has a size of one,
     and the second has all the other values"""
 
-    def __init__(self):
-        AbstractDistributor.__init__(self)
-        
     def _distribute(self, dom1, dom2):
         """See AbstractDistributor"""
         variable = self.findSmallestDomain(dom1)
@@ -103,9 +90,6 @@
 class RandomizingDistributor(AbstractDistributor):
     """distributes domains as the NaiveDistrutor, except that the unique
     value of the first domain is picked at random."""
-
-    def __init__(self):
-        AbstractDistributor.__init__(self)
         
     def _distribute(self, dom1, dom2):
         """See AbstractDistributor"""
@@ -127,16 +111,17 @@
     If nb_subspaces is 0, then the smallest domain is split in
     domains of size 1"""
     
-    def __init__(self, nb_subspaces=3):
-        AbstractDistributor.__init__(self, nb_subspaces)
+    def __init__(self, c_space, nb_subspaces=3):
+        AbstractDistributor.__init__(self, c_space, nb_subspaces)
         self.__to_split = None
+
     def nb_subdomains(self, domains):
         """See AbstractDistributor"""
-        self.__to_split = self.findSmallestDomain(domains)
+        self.__to_split = self.findSmallestDomain()
         if self.nb_subspaces:
-            return min(self.nb_subspaces, domains[self.__to_split].size())
+            return min(self.nb_subspaces, self.__to_split.dom.size()) #domains[self.__to_split].size())
         else:
-            return domains[self.__to_split].size()
+            return self.__to_split.dom.size() # domains[self.__to_split].size()
     
     def _distribute(self, *args):
         """See AbstractDistributor"""
@@ -159,14 +144,14 @@
 class DichotomyDistributor(SplitDistributor):
     """distributes domains by splitting the smallest domain in
     two equal parts or as equal as possible"""
-    def __init__(self):
-        SplitDistributor.__init__(self, 2)
+    def __init__(self, c_space):
+        SplitDistributor.__init__(self, c_space, 2)
 
 
 class EnumeratorDistributor(SplitDistributor):
     """distributes domains by splitting the smallest domain
     in domains of size 1."""
-    def __init__(self):
-        SplitDistributor.__init__(self, 0)
+    def __init__(self, c_space):
+        SplitDistributor.__init__(self, c_space, 0)
 
 DefaultDistributor = DichotomyDistributor

Modified: pypy/dist/pypy/lib/logic/test_computationspace.py
==============================================================================
--- pypy/dist/pypy/lib/logic/test_computationspace.py	(original)
+++ pypy/dist/pypy/lib/logic/test_computationspace.py	Wed Jan 25 17:24:07 2006
@@ -5,6 +5,16 @@
 import distributor as di
 from py.test import raises
 
+#-- utility ------------------
+
+class hdict(dict):
+    """a hashable dict"""
+
+    def __hash__(self):
+        return id(self)
+
+#-- helpers -----------------
+
 def satisfiable_problem(computation_space):
     cs = computation_space
     s = cs.store 
@@ -17,7 +27,7 @@
     s.add_constraint(c.Expression([x, y, z], 'x == y + z'))
     s.add_constraint(c.Expression([z, w], 'z < w'))
     # set up a distribution strategy
-    cs.set_distributor(di.DichotomyDistributor())
+    cs.set_distributor(di.DichotomyDistributor(cs))
     return (x, w, y)
 
 def unsatisfiable_problem(computation_space):
@@ -32,9 +42,10 @@
     s.add_constraint(c.Expression([x, y, z], 'x == y + z'))
     s.add_constraint(c.Expression([z, w], 'z < w'))
     # set up a distribution strategy
-    cs.set_distributor(di.DichotomyDistributor())
+    cs.set_distributor(di.DichotomyDistributor(cs))
     return (x, w, y)
 
+#-- meat ------------------------
 
 class TestComputationSpace:
 
@@ -63,19 +74,50 @@
     def test_distribute(self):
         spc = cs.ComputationSpace(satisfiable_problem)
         spc.process()
-        domains = dict([(var, var.dom) for var in spc.store.vars
-                        if var.dom])
-        new_domains = spc.distributor.distribute(domains)
+        new_domains = [d.items() for d in
+                       spc.distributor.distribute()]
+        x, y, z, w = (spc.store.get_var_by_name('x'),
+                      spc.store.get_var_by_name('y'),
+                      spc.store.get_var_by_name('z'),
+                      spc.store.get_var_by_name('w'))
+        expected_domains = [{x: c.FiniteDomain([6]),
+                             y: c.FiniteDomain([2]),
+                             z: c.FiniteDomain([4]),
+                             w: c.FiniteDomain([5])}.items(),
+                            {x: c.FiniteDomain([6]),
+                             y: c.FiniteDomain([2]),
+                             z: c.FiniteDomain([4]),
+                             w: c.FiniteDomain([6, 7])}.items()]
+        for (d1, d2) in zip(new_domains, expected_domains):
+            for (e1, e2) in zip(d1, d2):
+                print e1, '=?', e2
+                assert e1 == e2
+        # the following assertion fails for mysterious reasons
+        # have we discovered a bug in CPython ?
+        # assert set(new_domains) == set(expected_domains)
+
+    def test_make_children(self):
+        spc = cs.ComputationSpace(satisfiable_problem)
         x, y, z, w = (spc.store.get_var_by_name('x'),
                       spc.store.get_var_by_name('y'),
                       spc.store.get_var_by_name('z'),
                       spc.store.get_var_by_name('w'))
-        assert new_domains == [{x: c.FiniteDomain([6]),
-                                y: c.FiniteDomain([2]),
-                                z: c.FiniteDomain([4]),
-                                w: c.FiniteDomain([5])},
-                               {x: c.FiniteDomain([6]),
-                                y: c.FiniteDomain([2]),
-                                z: c.FiniteDomain([4]),
-                                w: c.FiniteDomain([6, 7])}]
-                               
+        spc.process()
+        spc.make_children()
+        assert len(spc.children) == 2
+        new_domains = []
+        all_vars = spc.store.get_variables_with_a_domain()
+        for child in spc.children:
+            new_domains.append([(var, var.cs_get_dom(child))
+                                for var in all_vars])
+            
+        expected_domains = [{x: c.FiniteDomain([6]),
+                             y: c.FiniteDomain([2]),
+                             z: c.FiniteDomain([4]),
+                             w: c.FiniteDomain([5])}.items(),
+                            {x: c.FiniteDomain([6]),
+                             y: c.FiniteDomain([2]),
+                             z: c.FiniteDomain([4]),
+                             w: c.FiniteDomain([6, 7])}.items()]
+
+            

Modified: pypy/dist/pypy/lib/logic/unification.py
==============================================================================
--- pypy/dist/pypy/lib/logic/unification.py	(original)
+++ pypy/dist/pypy/lib/logic/unification.py	Wed Jan 25 17:24:07 2006
@@ -216,6 +216,12 @@
     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

Modified: pypy/dist/pypy/lib/logic/variable.py
==============================================================================
--- pypy/dist/pypy/lib/logic/variable.py	(original)
+++ pypy/dist/pypy/lib/logic/variable.py	Wed Jan 25 17:24:07 2006
@@ -37,8 +37,10 @@
         self.store = store
         # top-level 'commited' binding
         self._val = NoValue
-        # domain
+        # domain in a flat world
         self.dom = None
+        # domains in multiple spaces
+        self.doms = {}
         # constraints
         self.constraints = set()
         # when updated in a 'transaction', keep track
@@ -100,6 +102,11 @@
 
     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



More information about the Pypy-commit mailing list