[pypy-svn] r40891 - in pypy/dist/pypy/module/cclp: . constraint
auc at codespeak.net
auc at codespeak.net
Wed Mar 21 12:19:51 CET 2007
Author: auc
Date: Wed Mar 21 12:19:50 2007
New Revision: 40891
Added:
pypy/dist/pypy/module/cclp/app.py (contents, props changed)
Modified:
pypy/dist/pypy/module/cclp/__init__.py
pypy/dist/pypy/module/cclp/constraint/__init__.py
pypy/dist/pypy/module/cclp/constraint/constraint.py
pypy/dist/pypy/module/cclp/constraint/domain.py
pypy/dist/pypy/module/cclp/constraint/variable.py
pypy/dist/pypy/module/cclp/cspace.py
pypy/dist/pypy/module/cclp/interp_var.py
pypy/dist/pypy/module/cclp/scheduler.py
pypy/dist/pypy/module/cclp/thunk.py
pypy/dist/pypy/module/cclp/types.py
pypy/dist/pypy/module/cclp/variable.py
Log:
big cleanup
Modified: pypy/dist/pypy/module/cclp/__init__.py
==============================================================================
--- pypy/dist/pypy/module/cclp/__init__.py (original)
+++ pypy/dist/pypy/module/cclp/__init__.py Wed Mar 21 12:19:50 2007
@@ -7,6 +7,7 @@
"""
appleveldefs = {
+ 'make_expression':'app.make_expression'
}
interpleveldefs = {
@@ -21,12 +22,13 @@
'reset_scheduler':'scheduler.reset_scheduler',
'newspace':'cspace.newspace',
+ 'dorkspace':'cspace.dorkspace',
'choose':'cspace.choose',
'tell':'cspace.tell',
- 'distribute':'constraint.distributor.distribute',
+ 'distribute':'cspace.distribute',
- 'make_expression':'constraint.constraint.make_expression',
+ '_make_expression':'constraint.constraint._make_expression',
'all_diff': 'constraint.constraint.make_alldistinct'
}
Added: pypy/dist/pypy/module/cclp/app.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/module/cclp/app.py Wed Mar 21 12:19:50 2007
@@ -0,0 +1,7 @@
+from cclp import _make_expression
+
+def make_expression(variables, formula):
+ func = 'lambda %s:%s' % (','.join([name_of(var)
+ for var in variables]),
+ formula)
+ return _make_expression(variables, formula, eval(func))
Modified: pypy/dist/pypy/module/cclp/constraint/__init__.py
==============================================================================
--- pypy/dist/pypy/module/cclp/constraint/__init__.py (original)
+++ pypy/dist/pypy/module/cclp/constraint/__init__.py Wed Mar 21 12:19:50 2007
@@ -1 +1 @@
-# pass
+#
Modified: pypy/dist/pypy/module/cclp/constraint/constraint.py
==============================================================================
--- pypy/dist/pypy/module/cclp/constraint/constraint.py (original)
+++ pypy/dist/pypy/module/cclp/constraint/constraint.py Wed Mar 21 12:19:50 2007
@@ -1,320 +1,55 @@
-from pypy.rlib.objectmodel import we_are_translated
-from pypy.interpreter.error import OperationError
-from pypy.interpreter.baseobjspace import Wrappable
-from pypy.interpreter import baseobjspace, typedef, gateway
-from pypy.interpreter.gateway import interp2app
+from pypy.interpreter import baseobjspace
from pypy.interpreter.function import Function
+from pypy.interpreter.error import OperationError
+
from pypy.objspace.std.listobject import W_ListObject
from pypy.objspace.std.stringobject import W_StringObject
-from pypy.objspace.std.dictobject import W_DictObject
-
-from pypy.module.cclp.types import W_Constraint, W_AbstractDomain, W_Root, \
- W_Var, W_CVar as W_Variable
-
-from pypy.objspace.std.model import StdObjSpaceMultiMethod
-
-from pypy.module.cclp.constraint.btree import BTree
-#from pypy.objspace.constraint.util import sort
-
-all_mms = {}
-
-
-class W_AbstractConstraint(W_Constraint):
-
- def __init__(self, object_space, w_variables):
- """variables is a list of variables which appear in the formula"""
- W_Constraint.__init__(self, object_space)
- assert isinstance(w_variables, W_ListObject)
- assert self._space.is_true(self._space.ge(self._space.len(w_variables),
- self._space.newint(1)))
- self._names_to_vars = {}
- for var in w_variables.wrappeditems:
- assert isinstance(var, W_Var)
- self._names_to_vars[var.name_w()] = var
- self._variables = w_variables.wrappeditems #unwrap once ...
-
- def w_affected_variables(self):
- """ Return a list of all variables affected by this constraint """
- return self._space.newlist(self._variables)
- def affected_variables(self):
- return self._variables
+from pypy.module.cclp.types import W_CVar
- def w_revise(self):
- return self._space.newbool(self.revise())
-
-W_AbstractConstraint.typedef = typedef.TypeDef(
- "W_AbstractConstraint",
- W_Constraint.typedef,
- affected_variables = interp2app(W_AbstractConstraint.w_affected_variables),
- revise = interp2app(W_AbstractConstraint.w_revise))
-
-
-
-from pypy.module.__builtin__.compiling import eval as ev
-def make_filter__List_String(object_space, w_variables, w_formula):
- """NOT RPYTHON"""
+def check_variables(space, w_variables, min_nb):
+ if not isinstance(w_variables, W_ListObject):
+ raise OperationError(space.w_TypeError,
+ space.wrap("variables must be in a list or tuple."))
assert isinstance(w_variables, W_ListObject)
- assert isinstance(w_formula, W_StringObject)
- items = object_space.unpackiterable(w_variables)
- lst = []
- for it in items:
- assert isinstance(it, W_Variable)
- lst.append(it.name_w())
- var_ids = ','.join(lst) #[var.name_w()
- # for var in items])
- func_head = 'lambda ' + var_ids + ':'
- expr = func_head + object_space.str_w(w_formula)
- func_obj = ev(object_space, object_space.wrap(expr), object_space.newdict(),
- object_space.newdict())
- assert isinstance(func_obj, Function)
- return func_obj
-
-make_filter_mm = StdObjSpaceMultiMethod('make_filter', 2)
-make_filter_mm.register(make_filter__List_String, W_ListObject, W_StringObject)
-all_mms['make_filter'] = make_filter_mm
-
-class Quadruple(W_Root):
- def __init__(self, zero, one, two, three):
- self.zero = zero
- self.one = one
- self.two = two
- self.three = three
-
-class W_Expression(W_AbstractConstraint):
- """A constraint represented as a python expression."""
-
- def __init__(self, object_space, w_variables, w_formula):
- """variables is a list of variables which appear in the formula
- formula is a python expression that will be evaluated as a boolean"""
- W_AbstractConstraint.__init__(self, object_space, w_variables)
- self.formula = self._space.str_w(w_formula)
- # self.filter_func is a function taking keyword arguments and returning a boolean
- self.filter_func = self._space.make_filter(w_variables, w_formula)
-
- def copy(self):
- if we_are_translated():
- raise NotImplementedError
- else:
- newvars = [var.copy(self._space) for var in self._variables]
- const = W_Expression(self._space, self._space.newlist(newvars), self._space.wrap(self.formula))
- return const
-
- def _init_result_cache(self):
- """key = (variable,value), value = [has_success,has_failure]"""
- result_cache = self._space.newdict()
- for var in self._variables:
- assert isinstance(var, W_Variable)
- self._space.setitem(result_cache, var.w_name(), self._space.newdict())
- return result_cache
-
- def _assign_values(self):
- kwargs = self._space.newdict()
- variables = BTree()
- for variable in self._variables:
- assert isinstance(variable, W_Variable)
- domain = variable.w_dom
- assert isinstance(domain, W_AbstractDomain)
- values = domain.get_values()
- assert isinstance(values, list)
- ds = domain.size()
- assert isinstance(ds, int)
- w_name = variable.w_name()
- lval = len(values)
- variables.add(ds, Quadruple(w_name, values, 0, lval))
- # was meant to be:
- #variables.append((domain.size(),
- # [w_name, values, 0, len(values)]))
- first_value = values[0]
- assert isinstance(first_value, W_Root)
- kwargs.content[variable.w_name()] = first_value
- # get sorted variables to instanciate those with fewer possible values first
- variables = variables.values()
- self._assign_values_state = variables
- return kwargs
-
- def _next_value(self, kwargs):
-
- # try to instanciate the next variable
- variables = self._assign_values_state
+ if len(w_variables.wrappeditems) < min_nb:
+ raise OperationError(space.w_RuntimeError,
+ space.wrap("there must be at least %s variables." % min_nb))
+ return w_variables
+
+def cvars_to_names(cvars):
+ variables = []
+ for w_var in cvars:
+ assert isinstance(w_var, W_CVar)
+ variables.append(w_var.w_nam)
+ return variables
- for curr in variables:
- assert isinstance(curr, Quadruple)
- w_name = curr.zero
- dom_values = curr.one
- dom_index = curr.two
- dom_len = curr.three
- assert isinstance(w_name, W_StringObject)
- assert isinstance(dom_values, list)
- assert isinstance(dom_index, int)
- assert isinstance(dom_len, int)
- if dom_index < dom_len:
- kwargs.content[w_name] = dom_values[dom_index]
- curr.two = dom_index + 1
- break
- else:
- curr.two = 0
- kwargs.content[w_name] = dom_values[0]
- else:
- # it's over
- raise StopIteration
- return kwargs
- def revise(self):
- """generic propagation algorithm for n-ary expressions"""
- sp = self._space
- maybe_entailed = True
- ffunc = self.filter_func
- result_cache = self._init_result_cache()
- assert isinstance(result_cache, W_DictObject)
+from pypy.module._cslib.constraint import interp_make_expression, \
+ make_alldistinct as mkalldiff
- kwargs = self._assign_values()
- assert isinstance(kwargs, W_DictObject)
- while 1:
- try:
- kwargs = self._next_value(kwargs)
- assert isinstance(kwargs, W_DictObject)
- except StopIteration:
- break
- if maybe_entailed:
- for varname, val in kwargs.content.iteritems():
- val_dict = result_cache.content[varname]
- assert isinstance(val_dict, W_DictObject)
- if val not in val_dict.content:
- break
- else:
- continue
- if sp.is_true(sp.call(sp.wrap(ffunc),
- sp.newlist([]), kwargs)):
- for var, val in kwargs.content.items():
- var_dict = result_cache.content[var]
- assert isinstance(var_dict, W_DictObject)
- var_dict.content[val] = sp.w_True
- else:
- maybe_entailed = False
-
- try:
- for varname, keep in result_cache.content.items():
- var = self._names_to_vars[sp.str_w(varname)]
- assert isinstance(var, W_Variable)
- assert isinstance(keep, W_DictObject)
- domain = var.w_dom
- assert isinstance(domain, W_AbstractDomain)
- domain.remove_values([val
- for val in domain._values.content.keys()
- if val not in keep.content])
- except KeyError:
- # There are no more value in result_cache
- pass
-
- return maybe_entailed
-
-
- def __repr__(self):
- return '<%s>' % self.formula
-
-W_Expression.typedef = typedef.TypeDef("W_Expression",
- W_AbstractConstraint.typedef,
- revise = interp2app(W_Expression.w_revise))
-
-
-
-def make_expression(space, w_variables, w_formula):
+def _make_expression(space, w_variables, w_formula, w_filter_func):
"""create a new constraint of type Expression or BinaryExpression
The chosen class depends on the number of variables in the constraint"""
- assert isinstance(w_variables, W_ListObject)
- assert isinstance(w_formula, W_StringObject)
- assert len(w_variables.wrappeditems) > 0
- return W_Expression(space, w_variables, w_formula)
-make_expression.unwrap_spec = [baseobjspace.ObjSpace,
- baseobjspace.W_Root,
- baseobjspace.W_Root]
-
-
-class W_AllDistinct(W_AbstractConstraint):
- """Contraint: all values must be distinct"""
-
- def __init__(self, object_space, w_variables):
- W_AbstractConstraint.__init__(self, object_space, w_variables)
- # worst case complexity
- #self.__cost = len(w_variables.wrappeditems) * (len(w_variables.wrappeditems) - 1) / 2
-
- def copy(self):
- if we_are_translated():
- raise NotImplementedError
- else:
- newvars = [var.copy(self._space) for var in self._variables]
- const = W_AllDistinct(self._space, self._space.newlist(newvars))
- return const
-
- def revise(self):
- _spc = self._space
-
- ord_vars = BTree()
- for var in self._variables:
- assert isinstance(var, W_Variable)
- dom = var.w_dom
- assert isinstance(dom, W_AbstractDomain)
- sz = dom.size()
- ord_vars.add(sz, var)
- variables = ord_vars.values()
-
- # if a domain has a size of 1,
- # then the value must be removed from the other domains
- for var in variables:
- assert isinstance(var, W_Variable)
- dom = var.w_dom
- assert isinstance(dom, W_AbstractDomain)
- if dom.size() == 1:
- #print "AllDistinct removes values"
- for _var in variables:
- assert isinstance(_var, W_Variable)
- _dom = _var.w_dom
- assert isinstance(_dom, W_AbstractDomain)
- if not _var._same_as(var):
- try:
- _dom.remove_value(dom.get_values()[0])
- except KeyError, e:
- # we ignore errors caused by the removal of
- # non existing values
- pass
-
- # if there are less values than variables, the constraint fails
- values = {}
- for var in variables:
- assert isinstance(var, W_Variable)
- dom = var.w_dom
- assert isinstance(dom, W_AbstractDomain)
- for val in dom.w_get_values().wrappeditems:
- values[val] = 0
-
- if len(values) < len(variables):
- #print "AllDistinct failed"
- raise OperationError(_spc.w_RuntimeError,
- _spc.wrap("ConsistencyFailure"))
-
- # the constraint is entailed if all domains have a size of 1
- for var in variables:
- assert isinstance(var, W_Variable)
- dom = var.w_dom
- assert isinstance(dom, W_AbstractDomain)
- if not dom.size() == 1:
- return False
-
- # Question : did we *really* completely check
- # our own alldistinctness predicate ?
- #print "All distinct entailed"
- return True
-
-W_AllDistinct.typedef = typedef.TypeDef(
- "W_AllDistinct", W_AbstractConstraint.typedef,
- revise = interp2app(W_AllDistinct.w_revise))
+ w_variables = check_variables(space, w_variables, 1)
+ assert isinstance(w_filter_func, Function)
+ if not isinstance(w_formula, W_StringObject):
+ raise OperationError(space.w_TypeError,
+ space.wrap("formula must be a string."))
+ variables = cvars_to_names(w_variables.wrappeditems)
+ return interp_make_expression(space, space.newlist(variables),
+ w_formula, w_filter_func)
+
+_make_expression.unwrap_spec = [baseobjspace.ObjSpace,
+ baseobjspace.W_Root,
+ baseobjspace.W_Root,
+ baseobjspace.W_Root]
+
+def make_alldistinct(space, w_variables):
+ w_variables = check_variables(space, w_variables, 2)
+ variables = cvars_to_names(w_variables.wrappeditems)
+ return mkalldiff(space, space.newlist(variables))
-#function bolted into the space to serve as constructor
-def make_alldistinct(object_space, w_variables):
- assert isinstance(w_variables, W_ListObject)
- assert len(w_variables.wrappeditems) > 0
- return object_space.wrap(W_AllDistinct(object_space, w_variables))
make_alldistinct.unwrap_spec = [baseobjspace.ObjSpace,
- baseobjspace.W_Root]
+ baseobjspace.W_Root]
Modified: pypy/dist/pypy/module/cclp/constraint/domain.py
==============================================================================
--- pypy/dist/pypy/module/cclp/constraint/domain.py (original)
+++ pypy/dist/pypy/module/cclp/constraint/domain.py Wed Mar 21 12:19:50 2007
@@ -1,165 +1,31 @@
-from pypy.interpreter.error import OperationError
-
-from pypy.interpreter import typedef, gateway, baseobjspace
-from pypy.interpreter.gateway import interp2app
-
-from pypy.objspace.std.listobject import W_ListObject, W_TupleObject
-from pypy.objspace.std.intobject import W_IntObject
-
-from pypy.objspace.std.model import StdObjSpaceMultiMethod
-
-from pypy.module.cclp.types import W_AbstractDomain, W_Var, W_Root, ConsistencyError
+from pypy.module.cclp.types import W_Var
from pypy.module.cclp.interp_var import interp_bind, interp_free
-all_mms = {}
+from pypy.module._cslib import fd
-class W_FiniteDomain(W_AbstractDomain):
+class _DorkFiniteDomain(fd._FiniteDomain):
"""
- Variable Domain with a finite set of possible values
+ this variant accomodates synchronization needs
+ of the dorkspace
"""
- def __init__(self, space, w_values):
- """values is a list of values in the domain
- This class uses a dictionnary to make sure that there are
- no duplicate values"""
- W_AbstractDomain.__init__(self, space)
- #XXX a pure dict used to work there (esp. in revise)
- assert isinstance(w_values, W_ListObject)
- self._values = space.newdict()
- self.set_values(w_values)
-
- def copy(self):
- return W_FiniteDomain(self._space, self.w_get_values())
+ def __init__(self, space, w_values, values):
+ fd._FiniteDomain.__init__(self, w_values, values)
+ self.space = space
+ self._changevar = W_Var(space)
def clear_change(self):
"create a fresh change synchonizer"
- assert not interp_free(self._changed)
- self._changed = W_Var(self._space)
-
- def give_synchronizer(self):
- return self._changed
-
-
- def contains(self, w_val):
- sp = self._space
- assert isinstance(w_val, W_Root)
- return sp.is_true(sp.contains(self._values, w_val))
- __contains__ = contains
+ assert not interp_free(self._changevar)
+ self._changevar = W_Var(self.space)
+ def one_shot_watcher(self):
+ return self._changevar
+
def _value_removed(self):
- "The implementation of remove_value should call this method"
- #atomic
- interp_bind(self._changed, self._space.w_True)
+ fd._FiniteDomain._value_removed(self)
+ interp_bind(self._changevar, self.space.w_True)
self.clear_change()
- #/atomic
-
- if self.size() == 0:
- raise ConsistencyError, "tried to make a domain empty"
-
- def set_values(self, w_values):
- """XXX Objects in the value set can't be unwrapped unless we
- specialize on specific types - this will need specialization
- of revise & friends
- """
- for w_v in w_values.wrappeditems:
- self._space.setitem(self._values, w_v, self._space.w_True)
-
- def w_remove_value(self, w_value):
- try:
- self.remove_value(w_value)
- except ConsistencyError:
- raise OperationError(self._space.w_ConsistencyError,
- self._space.wrap("tried to empty a domain"))
-
- def remove_value(self, w_value):
- """Remove value of domain and check for consistency"""
- assert isinstance(w_value, baseobjspace.W_Root)
- self._space.delitem(self._values, w_value)
- self._value_removed()
-
- def w_remove_values(self, w_values):
- """Remove values of domain and check for consistency"""
- assert isinstance(w_values, W_ListObject)
- try:
- self.remove_values(w_values.wrappeditems)
- except KeyError:
- raise OperationError(self._space.w_RuntimeError,
- self._space.wrap("attempt to remove unkown value from domain"))
-
- def remove_values(self, values):
- assert isinstance(values, list)
- if len(values) > 0:
- for w_val in values:
- self._space.delitem(self._values,w_val)
- self._value_removed()
-
- def w_size(self):
- return self._space.newint(self.size())
-
- def size(self):
- """computes the size of a finite domain"""
- l = self._space.len(self._values)
- assert isinstance(l, W_IntObject)
- return l.intval
- __len__ = size
-
- def w_get_values(self):
- """return all the values in the domain
- in an indexable sequence"""
- return self._space.newlist(self.get_values())
-
- def get_values(self):
- return [x for x in self._space.unpackiterable(self._values)]
-
- def __repr__(self):
- return '<FD %s>' % str(self.w_get_values())
-
- def __eq__(self, w_other):
- if not isinstance(w_other, W_FiniteDomain):
- return self._space.newbool(False)
- return self._space.newbool(self._space.eq_w(self._values, w_other._values))
-
- def __ne__(self, w_other):
- if not isinstance(w_other, W_FiniteDomain):
- return self._space.newbool(True)
- if self._space.eq_w(self._values, w_other._values):
- return self._space.newbool(False)
- return self._space.newbool(True)
-
-
-
-# function bolted into the space to serve as constructor
-def make_fd(space, w_values):
- assert isinstance(w_values, W_ListObject)
- return W_FiniteDomain(space, w_values)
-app_make_fd = gateway.interp2app(make_fd)
-
-
-def intersection(space, w_fd1, w_fd2):
- return space.intersection(w_fd1, w_fd2)
-app_intersection = gateway.interp2app(intersection)
-
-
-def intersection__FiniteDomain_FiniteDomain(space, w_fd1, w_fd2):
- w_v1 = space.unpackiterable(w_fd1._values)
- res = [w_v for w_v in space.unpackiterable(w_fd2._values)
- if w_v in w_v1]
- return make_fd(space, space.newlist(res))
-
-intersection_mm = StdObjSpaceMultiMethod('intersection', 2)
-intersection_mm.register(intersection__FiniteDomain_FiniteDomain,
- W_FiniteDomain, W_FiniteDomain)
-all_mms['intersection'] = intersection_mm
-
-W_FiniteDomain.typedef = typedef.TypeDef(
- "W_FiniteDomain",
- W_AbstractDomain.typedef,
- remove_value = interp2app(W_FiniteDomain.w_remove_value),
- remove_values = interp2app(W_FiniteDomain.w_remove_values),
- get_values = interp2app(W_FiniteDomain.w_get_values),
- __eq__ = interp2app(W_FiniteDomain.__eq__),
- __ne__ = interp2app(W_FiniteDomain.__ne__),
- size = interp2app(W_FiniteDomain.w_size))
Modified: pypy/dist/pypy/module/cclp/constraint/variable.py
==============================================================================
--- pypy/dist/pypy/module/cclp/constraint/variable.py (original)
+++ pypy/dist/pypy/module/cclp/constraint/variable.py Wed Mar 21 12:19:50 2007
@@ -2,21 +2,22 @@
from pypy.objspace.std.listobject import W_ListObject
from pypy.objspace.std.stringobject import W_StringObject
-from pypy.module.cclp.constraint.domain import W_FiniteDomain
-
from pypy.module.cclp.types import deref, W_Var, W_CVar
from pypy.module.cclp.variable import bind_mm, raise_unification_failure, _alias, \
_assign_aliases, bind__Var_Root, bind__Var_Var
from pypy.module.cclp.misc import w
+from pypy.module._cslib import fd
+
+
W_Root = baseobjspace.W_Root
def domain(space, w_values, w_name):
assert isinstance(w_values, W_ListObject)
assert isinstance(w_name, W_StringObject)
- w_dom = W_FiniteDomain(space, w_values)
+ w_dom = fd.W_FiniteDomain(w_values, None)
w_var = W_CVar(space, w_dom, w_name)
- #w("CVAR", str(w_var))
+ w("CVAR", str(w_var))
return w_var
app_domain = gateway.interp2app(domain)
@@ -24,20 +25,24 @@
def bind__CVar_Root(space, w_cvar, w_obj):
#XXX we should (want to) be able to test membership
# in a wrapped against wrappeds into a non-wrapped dict
- if [True for elt in space.unpackiterable(w_cvar.w_dom._values)
+ if [True for elt in w_cvar.w_dom.domain.get_wvalues_in_rlist()
if space.is_true(space.eq(w_obj, elt))]:
return bind__Var_Root(space, w_cvar, w_obj)
raise_unification_failure(space, "value not in variable domain")
def bind__CVar_CVar(space, w_cvar1, w_cvar2):
- w_inter_dom = space.intersection(w_cvar1.w_dom, w_cvar2.w_dom)
- if w_inter_dom.__len__() > 0:
- if w_inter_dom.__len__() == 1:
- w_value = w_inter_dom.get_values()[0]
+ d1 = w_cvar1.w_dom.domain
+ d2 = w_cvar2.w_dom.domain
+ dinter = d1.intersect(d2)
+ if dinter.size() > 0:
+ if dinter.size() == 1:
+ w_value = dinter.get_wvalues_in_rlist()[0]
_assign_aliases(space, w_cvar1, w_value)
_assign_aliases(space, w_cvar2, w_value)
else:
- w_cvar1.w_dom = w_cvar2.w_dom = w_inter_dom
+ w_interdom = fd.W_FiniteDomain(space.newlist([]), None)
+ w_interdom.domain = dinter
+ w_cvar1.w_dom = w_cvar2.w_dom = w_interdom
_alias(space, w_cvar1, w_cvar2)
else:
raise_unification_failure(space, "incompatible domains")
Modified: pypy/dist/pypy/module/cclp/cspace.py
==============================================================================
--- pypy/dist/pypy/module/cclp/cspace.py (original)
+++ pypy/dist/pypy/module/cclp/cspace.py Wed Mar 21 12:19:50 2007
@@ -1,51 +1,82 @@
from pypy.rlib.objectmodel import we_are_translated
from pypy.interpreter import baseobjspace, gateway, argument, typedef
-from pypy.interpreter.function import Function
-from pypy.interpreter.pycode import PyCode
from pypy.interpreter.error import OperationError
from pypy.objspace.std.intobject import W_IntObject
from pypy.objspace.std.listobject import W_ListObject, W_TupleObject
+from pypy.objspace.std.stringobject import W_StringObject
-from pypy.module._stackless.interp_coroutine import AbstractThunk
-
-from pypy.module.cclp.misc import get_current_cspace, w
+from pypy.module.cclp.misc import get_current_cspace, w, v
from pypy.module.cclp.thunk import CSpaceThunk, PropagatorThunk
from pypy.module.cclp.global_state import sched
from pypy.module.cclp.variable import newvar
-from pypy.module.cclp.types import ConsistencyError, Solution, W_Var, \
- W_CVar, W_AbstractDomain, W_AbstractDistributor, SpaceCoroutine
+from pypy.module.cclp.types import FailedSpace, ConsistencyError, W_Var, W_CVar
from pypy.module.cclp.interp_var import interp_bind, interp_free
-from pypy.module.cclp.constraint.distributor import distribute
from pypy.module.cclp.scheduler import W_ThreadGroupScheduler
-from pypy.module._stackless.clonable import AppClonableCoroutine
-from pypy.module._stackless.coroutine import AppCoroutine
-from pypy.rlib.rgc import gc_clone
-
+from pypy.module._cslib import fd
+from pypy.rlib.cslib import rdistributor as rd
+from pypy.module._stackless.coroutine import AppCoroutine
+import pypy.rlib.rgc as rgc
+def gc_swap_pool(pool):
+ if we_are_translated():
+ return rgc.gc_swap_pool(pool)
+
+def gc_clone(data, pool):
+ if we_are_translated():
+ return rgc.gc_clone(data, pool)
+
def newspace(space, w_callable, __args__):
"application level creation of a new computation space"
args = __args__.normalize()
- dist_thread = SpaceCoroutine(space)
+ # allocate in a new pool
+ saved_pool = gc_swap_pool(None)
+ dist_thread = AppCoroutine(space)
thunk = CSpaceThunk(space, w_callable, args, dist_thread)
dist_thread.bind(thunk)
- dist_thread.hello_local_pool()
- try:
- w_space = W_CSpace(space, dist_thread)
- finally:
- dist_thread.goodbye_local_pool()
+ w_space = W_CSpace(space, dist_thread, saved_pool)
+ w_space.goodbye_local_pool()
+ # /allocate
if not we_are_translated():
- w("NEWSPACE, (distributor) thread",
- str(id(dist_thread)), "for", str(w_callable.name))
+ w("NEWSPACE, (distributor) thread %d for %s" %
+ ( id(dist_thread), str(w_callable.name) ) )
return w_space
+
newspace.unwrap_spec=[baseobjspace.ObjSpace,
baseobjspace.W_Root,
argument.Arguments]
+
+def dorkspace(space, w_callable, __args__):
+ "application level creation of a new complicated computation space"
+ args = __args__.normalize()
+ dist_thread = AppCoroutine(space)
+ thunk = CSpaceThunk(space, w_callable, args, dist_thread)
+ dist_thread.bind(thunk)
+
+ saved_pool = gc_swap_pool(None)
+ try:
+ w_space = W_ComplicatedSpace(space, dist_thread, saved_pool)
+ w_space.goodbye_local_pool()
+ except:
+ gc_swap_pool(saved_pool)
+ raise OperationError(space.w_RuntimeError,
+ space.wrap("Unknown error in newspace"))
+ if not we_are_translated():
+ w("NEWSPACE, (distributor) thread %d for %s" %
+ ( id(dist_thread), str(w_callable.name) ) )
+ return w_space
+
+dorkspace.unwrap_spec=[baseobjspace.ObjSpace,
+ baseobjspace.W_Root,
+ argument.Arguments]
+
+
def choose(space, w_n):
+ "non deterministic choice from within a c.space"
if not isinstance(w_n, W_IntObject):
raise OperationError(space.w_TypeError,
space.wrap('choose only accepts an integer.'))
@@ -64,33 +95,94 @@
space.wrap("this space is finished"))
try:
return cspace.choose(n)
- except ConsistencyError:
- raise OperationError(space.w_ConsistencyError,
- space.wrap("this space is failed"))
+ except Exception, e:
+ if not we_are_translated():
+ import traceback
+ traceback.print_exc()
+ w('whack whack whack')
+ raise OperationError(space.w_RuntimeError,
+ space.wrap("something wacky happened %s" % e))
choose.unwrap_spec = [baseobjspace.ObjSpace,
baseobjspace.W_Root]
-from pypy.module.cclp.constraint import constraint
+from pypy.module._cslib.constraint import W_AbstractConstraint
def tell(space, w_constraint):
- if not isinstance(w_constraint, constraint.W_AbstractConstraint):
+ "adding a constraint to a c.space (from within)"
+ if not isinstance(w_constraint, W_AbstractConstraint):
raise OperationError(space.w_TypeError,
space.wrap('Tell only accepts object of '
'(sub-)types Constraint.'))
- get_current_cspace(space).tell(w_constraint)
+ get_current_cspace(space).tell(w_constraint.constraint)
tell.unwrap_spec = [baseobjspace.ObjSpace,
baseobjspace.W_Root]
+def distribute(space, w_strategy):
+ assert isinstance(w_strategy, W_StringObject)
+ strat = space.str_w(w_strategy)
+ cspace = get_current_cspace(space)
+ # constraint distributor thread main loop
+ cspace.distribute(strat)
+distribute.unwrap_spec = [baseobjspace.ObjSpace,
+ baseobjspace.W_Root]
+
+
+# base space
+# non concurrent propagators
+# hence much less weird synchronization stuff
+# a specific pool object
+# XXX maybe use a descr_method__new__ to create the pool before allocation
class W_CSpace(W_ThreadGroupScheduler):
+ local_pool = None
- def __init__(self, space, dist_thread):
+ def dump(self):
+ w('-- DUMPing C.Space data --')
+ w(':local pool %s' % id(self.local_pool))
+ w(':saved pool %s' % id(self.saved_pool))
+ v(':threads :')
+ curr = stop = self._head
+ while 1:
+ v('%s ' % id(curr))
+ curr = curr._next
+ if curr == stop:
+ break
+ w('')
+ v(':blocked :')
+ for th in self._blocked.keys():
+ v('%s ' % id(th))
+ w('')
+ w(':blocked_on')
+ for var, thl in self._blocked_on.items():
+ v(' var %s : ' % id(var))
+ for th in thl:
+ v('%s ' % id(th))
+ w('')
+ w(':blocked_byneed')
+ for var, thl in self._blocked_byneed.items():
+ v(' var %s : ' % id(var))
+ for th in thl:
+ v('%s ' % id(th))
+ w('')
+ w(':traced vars')
+ for th, varl in self._traced.items():
+ v(' thread %s : ' % id(th))
+ for var in varl:
+ v('%s ' % id(var))
+ w('')
+ w('-- /DUMP --')
+
+ def __init__(self, space, dist_thread, saved_pool):
W_ThreadGroupScheduler.__init__(self, space)
+ # pool
+ self.local_pool = None
+ self.saved_pool = saved_pool
+ # /pool
+ # thread ring
dist_thread._cspace = self
self._init_head(dist_thread)
- self.main_thread = dist_thread
+ # /ring
sched.uler.add_new_group(self)
- self.distributor = None # dist instance != thread
# choice mgmt
self._choices = newvar(space)
self._committed = newvar(space)
@@ -100,89 +192,277 @@
self._merged = False
self._finished = newvar(space)
# constraint store ...
- self._store = {} # name -> var
-
-## def hello(self):
-## self.main_thread.hello_local_pool()
-
-## def goodbye(self):
-## self.main_thread.goodbye_local_pool()
-
- def register_var(self, cvar):
- self._store[cvar.name] = cvar
+ self._constraints = []
+ self._domains = {} # varname -> domain
+ self._variables = [] # varnames
+ self._varconst = {} # varname -> constraints
+ self._cqueue = [] # constraint queue to be processed
+
+ #-- POOL & cloning stuff
+
+ def hello_local_pool(self):
+ if we_are_translated():
+ self.saved_pool = gc_swap_pool(self.local_pool)
+
+ def goodbye_local_pool(self):
+ if we_are_translated():
+ self.local_pool = gc_swap_pool(self.saved_pool)
+ self.saved_pool = None
def w_clone(self):
- cl_thread = self.main_thread._clone()
- cl_thread._cspace.finalize_cloning( self, cl_thread )
- cl_thread._cspace.main_thread = cl_thread
- sched.uler.add_new_group(cl_thread._cspace)
- return self.space.wrap(cl_thread._cspace)
+ # all this stuff is created in the local pool so that
+ # gc_clone can clone it. every object we want to clone
+ # must be reachable through objects allocated in this
+ # local pool via the data tuple.
+ self.report_bad_condition_to_applevel()
+ head = curr = self._head
+ new_shells = []
+ # within new POOL
+ self.hello_local_pool()
+ coroutines_to_clone = []
+ while 1:
+ coroutines_to_clone.append((curr, curr.frame, curr.subctx))
+ self.goodbye_local_pool()
+ # outside new POOL, build new fresh coro shells
+ new = AppCoroutine(self.space, state = curr.costate)
+ new.parent = curr.parent
+ new_shells.append(new)
+ self.hello_local_pool()
+ # /outside
+ curr = curr._next
+ if curr == head:
+ break
+ data = (self, coroutines_to_clone)
+ # /within new POOL
+ self.goodbye_local_pool()
+
+ (copy, copied_coros), copypool = gc_clone(data, self.local_pool)
+ copy.local_pool = copypool
+ copy.finalize_cloning(copied_coros, new_shells)
+ sched.uler.add_new_group(copy)
+ self.dump()
+ copy.dump()
+ return self.space.wrap(copy)
- def finalize_cloning(self, orig_cspace, cl_thread ):
+ def finalize_cloning(self, copied_coros, new_shells):
# We need to walk all threads references from this cloned
# space and replace
- # 1. our cloned thread get's a new thread ID
- orig_tid = orig_cspace.main_thread.tid
- sched.uler.register_thread( cl_thread )
- self.main_thread = cl_thread
- # walk the thread ring buffer to replace the cloned threads
- cl_thread._cspace._init_head( cl_thread ) # XXX enough for now with only one thread
- self.replace_thread( orig_tid, cl_thread )
+ # 1. our cloned thread gets a new thread ID
+ w('finalize cloning in c.space %s' % id(self))
+ self._head = None
+ for i in range(len(copied_coros)):
+ coro, cloned_frame, cloned_subctx = copied_coros[i]
+ # bolt cloned stuff on new coro shells
+ cloned_coro = new_shells[i]
+ cloned_coro.frame = cloned_frame
+ cloned_coro.subctx = cloned_subctx
+ cloned_coro._cspace = self
+ cloned_coro.thunk = coro.thunk
+ self.replace_thread(coro, cloned_coro)
+
+ def replace_thread(self, old, new):
+ # walk the list of _blocked threads:
+ if old in self._blocked.keys():
+ w('blocked : %s replaced %s' % (id(new), id(old)))
+ del self._blocked[old]
+ self._blocked[new] = True
+
+ # walk the mappings var->threads
+ for w_var in self._blocked_on:
+ threads = self._blocked_on[w_var]
+ for k in range(len(threads)):
+ if threads[k] is old:
+ w('blocked_on : %s replaced %s' % (id(new), id(old)))
+ threads[k] = new
+
+ for w_var in self._blocked_byneed:
+ threads = self._blocked_byneed[w_var]
+ for k in range(len(threads)):
+ if threads[k] is old:
+ w('blocked_byneed : %s replaced %s' % (id(new), id(old)))
+ threads[k] = new
+
+ # handled traced thread
+ for th in self._traced.keys():
+ if th is old:
+ lvars = self._traced[th]
+ del self._traced[th]
+ self._traced[new] = lvars
+
+ # insert the thread in the linked list
+ if self._head is None:
+ w('head was initialized with %s' % id(new))
+ self._head = new._next = new._prev = new
+ else:
+ w('%s was inserted in the runqueue' % id(new))
+ r = self._head
+ l = r._prev
+ l._next = new
+ r._prev = new
+ new._prev = l
+ new._next = r
+ assert new._next is not new
+ assert new._prev is not new
+
+ def _newvar(self):
+ """
+ ensure a new space-local variable is allocated
+ in the right space/pool
+ """
+ self.hello_local_pool()
+ var = newvar(self.space)
+ self.goodbye_local_pool()
+ return var
- def report_bad_state_to_applevel(self):
+ #-- / POOL
+
+ def register_var(self, cvar):
+ name = cvar.name
+ dom = cvar.w_dom.domain
+ self._domains[name] = dom
+ self._varconst[name] = []
+
+ def tell(self, rconst):
+ w('telling %s' % rconst)
+ self._constraints.append(rconst)
+ for var in rconst._variables:
+ self._varconst[var].append(rconst)
+
+ def untell(self, constraint):
+ "entailed constraint are allowed to go away"
+ self._constraints.remove(constraint)
+ for var in constraint._variables:
+ self._varconst[var].remove(constraint)
+
+ def distributable(self):
+ for dom in self._domains.values():
+ if dom.size() > 1:
+ return True
+ return False
+
+ def distribute(self, strat):
+ w('SP:start constraint propagation & distribution loop')
+ space = self.space
+ if strat == 'dichotomy':
+ dist = rd.DichotomyDistributor()
+ elif strat == 'allornothing':
+ dist = rd.AllOrNothingDistributor()
+ else:
+ raise OperationError(space.w_RuntimeError,
+ space.wrap("please pick a strategy in "
+ "(allornothing, dichotomy)."))
+ # initialize constraint queue
+ self._cqueue = [(constr.estimate_cost(self._domains), constr)
+ for constr in self._constraints]
+ self.wait_stable() # hmm
+ self.propagate()
+ while self.distributable():
+ w('SP:distribute loop')
+ w_choice = self.choose(2) # yes, two, always, all the time
+ choice = space.int_w(w_choice)
+ small_dom_var = dist.find_smallest_domain(self._domains)
+ dom = self._domains[small_dom_var]
+ dist._distribute_on_choice(dom, choice)
+ for constraint in self._varconst[small_dom_var]:
+ self._cqueue.append((0, constraint)) # *uck the cost
+ dom._changed = False
+ self.propagate()
+
+ def propagate(self): # XXX pasted from rlib.cslib.rpropagation, mixin me
+ """Prunes the domains of the variables
+ This method calls constraint.narrow() and queues constraints
+ that are affected by recent changes in the domains.
+ Returns True if a solution was found"""
+
+ # XXX : _queue.sort()
+ w('SP:propagating')
+ _queue = self._cqueue
+ _affected_constraints = {}
+ while True:
+ if not _queue:
+ # refill the queue if some constraints have been affected
+ _queue = [(constr.estimate_cost(self._domains), constr)
+ for constr in _affected_constraints]
+ if not _queue:
+ break
+ # XXX _queue.sort()
+ _affected_constraints.clear()
+ cost, constraint = _queue.pop(0)
+ entailed = constraint.revise(self._domains)
+ for var in constraint._variables:
+ # affected constraints are listeners of
+ # affected variables of this constraint
+ dom = self._domains[var]
+ if not dom._changed: # XXX
+ continue
+ for constr in self._varconst[var]:
+ if constr is not constraint:
+ _affected_constraints[constr] = True
+ dom._changed = False
+ if entailed:
+ self.untell(constraint)
+ if constraint in _affected_constraints:
+ del _affected_constraints[constraint]
+
+ for domain in self._domains.values():
+ if domain.size() != 1:
+ return 0
+ return 1
+
+ #-- Public ops
+
+ def report_bad_condition_to_applevel(self):
+ """
+ a couple of checks for methods on spaces
+ but forbidden within
+ """
+ currspace = get_current_cspace(self.space)
+ if currspace is self:
+ raise OperationError(self.space.w_RuntimeError,
+ self.space.wrap("you can't do this operation"
+ "on the current computation space"))
if not interp_free(self._finished):
raise OperationError(self.space.w_RuntimeError,
self.space.wrap("space is finished"))
+
def w_ask(self):
try:
- self.report_bad_state_to_applevel()
+ self.report_bad_condition_to_applevel()
except: # we're dead, let's avoid wait_stable ...
- return self._choices
+ return self.space.wrap(self._last_choice)
self.wait_stable()
self.space.wait(self._choices)
choices = self._choices.w_bound_to
- self.main_thread.hello_local_pool()
- self._choices = newvar(self.space)
- self.main_thread.goodbye_local_pool()
+ self._choices = self._newvar()
assert isinstance(choices, W_IntObject)
self._last_choice = self.space.int_w(choices)
return choices
def choose(self, n):
- # solver probably asks
- self.wait_stable()
- if self._failed: #XXX set by any propagator while we were not looking
- raise ConsistencyError
- assert interp_free(self._choices)
+ assert interp_free(self._choices)
assert interp_free(self._committed)
# XXX we wrap it a bit prematurely, so as to satisfy
- # type requirements (logic variables do not support rpython types)
+ # type requirements (logic variables only accept W_Roots)
interp_bind(self._choices, self.space.wrap(n)) # unblock the solver
# now we wait on a solver commit
- self.space.wait(self._committed)
+ self.space.wait(self._committed)
committed = self._committed.w_bound_to
self._committed = newvar(self.space)
return committed
def w_commit(self, w_n):
- self.report_bad_state_to_applevel()
- assert isinstance(w_n, W_IntObject)
- n = w_n.intval
+ self.report_bad_condition_to_applevel()
+ if not isinstance(w_n, W_IntObject):
+ raise OperationError(self.space.w_TypeError,
+ self.space.wrap('commit accepts only ints.'))
+ n = self.space.int_w(w_n)
assert interp_free(self._committed)
if n < 1 or n > self._last_choice:
raise OperationError(self.space.w_ValueError,
- self.space.wrap("need 0<commit<=%d" % self._last_choice))
- interp_bind(self._committed, w_n)
-
- def tell(self, w_constraint):
- space = self.space
- w_coro = AppCoroutine(space)
- w_coro._next = w_coro._prev = w_coro
- w_coro._cspace = self
- thunk = PropagatorThunk(space, w_constraint, w_coro)
- w_coro.bind(thunk)
- self.add_new_thread(w_coro)
+ self.space.wrap("need 0<commit<=%d" %
+ self._last_choice))
+ interp_bind(self._committed, w_n)
def fail(self):
self._failed = True
@@ -197,10 +477,9 @@
return self._failed
def w_merge(self):
- # let's bind the solution variables
if self._merged:
- raise OperationError(self.space.w_RuntimeError,
- self.space.wrap("space is already merged"))
+ return self._solution
+ # let's bind the solution variables
self._merged = True
sol = self._solution.w_bound_to
if isinstance(sol, W_ListObject):
@@ -209,6 +488,8 @@
self._bind_solution_variables(sol.wrappeditems)
return self._solution
+ #-- / Public ops
+
def __ne__(self, other):
if other is self:
return False
@@ -218,11 +499,10 @@
if contains_cvar(solution): # was a constraint script
for var in solution:
assert isinstance(var, W_CVar)
- realvar = self._store[var.name]
- dom = realvar.w_dom
- assert isinstance(dom, W_AbstractDomain)
+ dom = self._domains[var.name]
+ assert isinstance(dom, fd._FiniteDomain)
assert dom.size() == 1
- interp_bind(var, dom.get_values()[0])
+ interp_bind(var, dom.get_wvalues_in_rlist()[0])
def contains_cvar(lst):
@@ -235,3 +515,91 @@
clone = gateway.interp2app(W_CSpace.w_clone),
merge = gateway.interp2app(W_CSpace.w_merge),
fail = gateway.interp2app(W_CSpace.w_fail))
+
+
+import pypy.rlib.cslib.rdistributor as rd
+from pypy.module.cclp.constraint.domain import _DorkFiniteDomain
+
+
+class W_ComplicatedSpace(W_CSpace):
+ """
+ a space with concurrent propagators inside
+ it performs poorly, is needlessly complicated
+ the author should be shot
+ """
+
+ def __init__(self, space, dist_thread, saved_pool):
+ W_ThreadGroupScheduler.__init__(self, space)
+ # thread ring
+ dist_thread._cspace = self
+ self._init_head(dist_thread)
+ # /ring
+ # pool
+ self.local_pool = None
+ self.saved_pool = saved_pool
+ # /pool
+ sched.uler.add_new_group(self)
+ self.dist = None # dist instance != thread
+ # choice mgmt
+ self._choices = newvar(space)
+ self._committed = newvar(space)
+ # status, merging
+ self._solution = newvar(space)
+ self._failed = False
+ self._merged = False
+ self._finished = newvar(space)
+ # constraint store ...
+ self._store = {} # name -> var
+ self._domains = {} # varname -> domain
+
+
+ def register_var(self, cvar):
+ name = cvar.name
+ self._store[name] = cvar
+ # let's switch to dork-style finite domains
+ basic_dom = cvar.w_dom.domain
+ dom = _DorkFiniteDomain(self.space,
+ basic_dom.vlist,
+ basic_dom._values)
+ cvar.w_dom.domain = dom
+ self._domains[name] = dom
+
+ def distribute(self, strat):
+ space = self.space
+ if strat == 'dichotomy':
+ dist = rd.DichotomyDistributor()
+ elif strat == 'allornothing':
+ dist = rd.AllOrNothingDistributor()
+ else:
+ raise OperationError(space.w_RuntimeError,
+ space.wrap("please pick a strategy in "
+ "(allornothing, dichotomy)."))
+ self.wait_stable()
+ while self.distributable():
+ w_choice = self.choose(2)
+ choice = space.int_w(w_choice)
+ small_dom_var = dist.find_smallest_domain(self._domains)
+ dom = self._domains[small_dom_var]
+ w('ABOUT TO DISTRIBUTE')
+ dist._distribute_on_choice(dom, choice)
+ self.wait_stable()
+
+ def tell(self, constraint):
+ space = self.space
+ w_coro = AppCoroutine(space)
+ w_coro._next = w_coro._prev = w_coro
+ w_coro._cspace = self
+ thunk = PropagatorThunk(space, constraint, w_coro)
+ w_coro.bind(thunk)
+ self.add_new_thread(w_coro)
+
+
+ def _bind_solution_variables(self, solution):
+ if contains_cvar(solution): # was a constraint script
+ for var in solution:
+ assert isinstance(var, W_CVar)
+ realvar = self._store[var.name]
+ dom = realvar.w_dom.domain
+ assert isinstance(dom, fd._FiniteDomain)
+ assert dom.size() == 1
+ interp_bind(var, dom.get_wvalues_in_rlist()[0])
Modified: pypy/dist/pypy/module/cclp/interp_var.py
==============================================================================
--- pypy/dist/pypy/module/cclp/interp_var.py (original)
+++ pypy/dist/pypy/module/cclp/interp_var.py Wed Mar 21 12:19:50 2007
@@ -69,7 +69,7 @@
def _assign(w_var, w_val):
assert isinstance(w_var, W_Var)
if isinstance(w_var, W_CVar):
- if not w_var.w_dom.contains(w_val):
+ if not w_val in w_var.w_dom.domain.vlist:
raise ValueError, "assignment out of domain"
w_var.w_bound_to = w_val
Modified: pypy/dist/pypy/module/cclp/scheduler.py
==============================================================================
--- pypy/dist/pypy/module/cclp/scheduler.py (original)
+++ pypy/dist/pypy/module/cclp/scheduler.py Wed Mar 21 12:19:50 2007
@@ -9,7 +9,6 @@
#-- Singleton scheduler ------------------------------------------------
-AppCoroutine.tid = 0
AppCoroutine._cspace = None
class TopLevelScheduler(object):
@@ -26,25 +25,6 @@
# asking for stability
self._asking = {} # cspace -> thread set
self._asking[top_level_space] = {} # XXX
- # variables suspension lists
- self._tl_blocked = {} # set of spaces
- self._tl_blocked_on = {} # var -> spaces
- self._tl_blocked_byneed = {} # var -> spaces
- self.next_tid = 1 # 0 is reserved for untracked threads
- self.threads = {} # id->thread mapping
-
- def get_new_tid(self):
- # XXX buggy if it overflows
- tid = self.next_tid
- self.next_tid += 1
- return tid
-
- def register_thread(self, thread):
- if thread.tid==0:
- thread.tid = self.get_new_tid()
- self.threads[thread.tid] = thread
-## else:
-## assert thread.tid in self.threads, "Thread already registered"
def _chain_insert(self, group):
assert isinstance(group, W_ThreadGroupScheduler), "type error"
@@ -61,14 +41,11 @@
def schedule(self):
running = self._head
to_be_run = self._select_next()
- if not isinstance(to_be_run, W_ThreadGroupScheduler):
- w("Aaarg something wrong in top level schedule")
- assert False, "type error"
#w(".. SWITCHING (spaces)", str(id(get_current_cspace(self.space))), "=>", str(id(to_be_run)))
self._switch_count += 1
if to_be_run != running:
- running.goodbye()
- to_be_run.hello()
+ running.goodbye_local_pool()
+ to_be_run.hello_local_pool()
to_be_run.schedule()
def _select_next(self):
@@ -79,7 +56,6 @@
if to_be_run.is_runnable():
break
to_be_run = to_be_run._next
- assert isinstance(to_be_run, W_ThreadGroupScheduler), "type error"
if to_be_run == sentinel:
reset_scheduler(self.space)
w(".. SCHEDULER reinitialized")
@@ -245,12 +221,13 @@
def __init__(self, space):
self.space = space
- self._pool = None
self._switch_count = 0
self._traced = {} # thread -> vars
self.thread_count = 1
self.blocked_count = 0
+ # head thread
self._head = None
+ # thread group ring
self._next = self
self._prev = self
# accounting for blocked stuff
@@ -260,40 +237,9 @@
def _init_head(self, thread):
"sets the initial ring head"
- assert isinstance(thread, AppCoroutine), "type error"
self._head = thread
thread._next = thread._prev = thread
- sched.uler.register_thread( thread )
- w("HEAD (main) THREAD = ", str(id(self._head)))
-
-
- def replace_thread( self, orig_tid, cl_thread ):
- # walk the list of _blocked threads:
- for th in self._blocked:
- if th.tid == orig_tid:
- del self._blocked[th]
- self._blocked[cl_thread] = True
-
- # walk the mappings var->threads
- for w_var in self._blocked_on:
- threads = self._blocked_on[w_var]
- for k in range(len(threads)):
- if threads[k].tid == orig_tid:
- threads[k] = cl_thread
-
- for w_var in self._blocked_byneed:
- threads = self._blocked_byneed[w_var]
- for k in range(len(threads)):
- if threads[k].tid == orig_tid:
- threads[k] = cl_thread
-
- # handled traced thread
- for th in self._traced.keys():
- if th.tid == orig_tid:
- lvars = self._traced[th]
- del self._traced[th]
- self._traced[cl_thread] = lvars
-
+ w("HEAD (main) THREAD = ", str(id(self._head)))
def _chain_insert(self, thread):
assert isinstance(thread, AppCoroutine), "type error"
@@ -305,12 +251,11 @@
r._prev = thread
thread._prev = l
thread._next = r
- sched.uler.register_thread( thread )
- def hello(self):
+ def hello_local_pool(self):
pass
- def goodbye(self):
+ def goodbye_local_pool(self):
pass
def register_var(self, var):
@@ -352,9 +297,7 @@
asking[self] = {curr:True}
curr._cspace._blocked[curr] = True #XXX semantics please ?
curr._cspace.blocked_count += 1
- w("About to reschedule")
sched.uler.schedule()
- w("Resuming %d" % id(self) )
def schedule(self):
to_be_run = self._select_next()
@@ -449,14 +392,15 @@
for th in sched.uler._asking[home].keys():
# these asking threads must be unblocked, in their
# respective home spaces
- # was: del sched.uler._blocked[th]
- v(' ', str(id(th)))
+ v(str(id(th)))
del th._cspace._blocked[th]
th._cspace.blocked_count -= 1
w('')
sched.uler._asking[home] = {}
-
+ def distribute(self, dist):
+ raise OperationError(self.space.w_RuntimeError,
+ self.space.wrap("You can't distribute a top-level space."))
def add_new_thread(self, thread):
"insert 'thread' at end of running queue"
@@ -493,7 +437,6 @@
assert isinstance(thread, AppCoroutine), "type error"
assert isinstance(lvars, list), "type error"
#w(".. TRACING logic vars.", str(lvars), "for", str(id(thread)))
- #assert not self._traced.has_key(thread) doesn't translate
self._traced[thread] = lvars
def dirty_traced_vars(self, thread, failed_value):
Modified: pypy/dist/pypy/module/cclp/thunk.py
==============================================================================
--- pypy/dist/pypy/module/cclp/thunk.py (original)
+++ pypy/dist/pypy/module/cclp/thunk.py Wed Mar 21 12:19:50 2007
@@ -1,12 +1,11 @@
from pypy.module._stackless.coroutine import _AppThunk, AppCoroutine
from pypy.module._stackless.interp_coroutine import AbstractThunk
-from pypy.module.cclp.misc import w
+from pypy.module.cclp.misc import w, get_current_cspace
from pypy.module.cclp.global_state import sched
from pypy.module.cclp.types import W_Var, W_CVar, W_Future, W_FailedValue, \
- ConsistencyError, Solution, W_AbstractDomain, SpaceCoroutine
-from pypy.module.cclp.interp_var import interp_wait, interp_entail, \
- interp_bind, interp_free, interp_wait_or
+ ConsistencyError, FailedSpace, Solution
+from pypy.module.cclp.interp_var import interp_bind, interp_free, interp_wait_or
from pypy.objspace.std.listobject import W_ListObject
from pypy.objspace.std.listobject import W_TupleObject
@@ -40,6 +39,9 @@
_AppThunk.call(self)
except Exception, exc:
w(".! exceptional EXIT of procedure", str(id(self._coro)), "with", str(exc))
+ if not we_are_translated():
+ import traceback
+ traceback.print_exc()
sched.uler.dirty_traced_vars(self._coro, W_FailedValue(exc))
else:
w(".! clean EXIT of procedure", str(id(self._coro)))
@@ -83,7 +85,7 @@
def call(self):
space = self.space
coro = AppCoroutine.w_getcurrent(space)
- assert isinstance(coro, SpaceCoroutine)
+ assert isinstance(coro, AppCoroutine)
cspace = coro._cspace
w("-- initial DISTRIBUTOR thunk CALL in", str(id(coro)))
sched.uler.trace_vars(coro, logic_args(self.args.unpack()))
@@ -93,15 +95,20 @@
_AppThunk.call(self)
finally:
coro = AppCoroutine.w_getcurrent(space)
- assert isinstance(coro, SpaceCoroutine)
+ assert isinstance(coro, AppCoroutine)
cspace = coro._cspace
- except ConsistencyError, exc:
- w("-- EXIT of DISTRIBUTOR, space is FAILED", str(id(coro)), "with", str(exc))
+ except FailedSpace, exc:
+ w("-- EXIT of DISTRIBUTOR %s, space is FAILED with %s" % (id(coro),
+ str(exc)))
failed_value = W_FailedValue(exc)
interp_bind(cspace._solution, failed_value)
except Exception, exc:
# maybe app_level let something buble up ...
- w("-- exceptional EXIT of DISTRIBUTOR", str(id(coro)), "with", str(exc))
+ w("-- exceptional EXIT of DISTRIBUTOR %s with %s" % (id(coro),
+ str(exc)))
+ if not we_are_translated():
+ import traceback
+ traceback.print_exc()
failed_value = W_FailedValue(exc)
sched.uler.dirty_traced_vars(coro, failed_value)
interp_bind(cspace._solution, failed_value)
@@ -125,7 +132,6 @@
sched.uler.remove_thread(coro)
sched.uler.schedule()
-
class PropagatorThunk(AbstractThunk):
def __init__(self, space, w_constraint, coro):
self.space = space
@@ -135,26 +141,27 @@
def call(self):
#coro = AppCoroutine.w_getcurrent(self.space)
try:
+ space = self.space
cspace = self.coro._cspace
+ const = self.const
try:
while 1:
- entailed = self.const.revise()
+ if not interp_free(cspace._finished):
+ break
+ entailed = const.revise(cspace._domains)
if entailed:
break
# we will block on domains being pruned
wait_list = []
- _vars = self.const._variables
- assert isinstance(_vars, list)
- for var in _vars:
- assert isinstance(var, W_CVar)
- dom = var.w_dom
- assert isinstance(dom, W_AbstractDomain)
- wait_list.append(dom.give_synchronizer())
+ _doms = [cspace._domains[var]
+ for var in const._variables]
+ for dom in _doms:
+ #assert isinstance(dom, W_AbstractDomain)
+ wait_list.append(dom.one_shot_watcher())
#or the cspace being dead
wait_list.append(cspace._finished)
- interp_wait_or(self.space, wait_list)
- if not interp_free(cspace._finished):
- break
+ interp_wait_or(space, wait_list)
+ cspace = get_current_cspace(space) # might have been cloned
except ConsistencyError:
cspace.fail()
except Exception: # rpython doesn't like just except:\n ...
Modified: pypy/dist/pypy/module/cclp/types.py
==============================================================================
--- pypy/dist/pypy/module/cclp/types.py (original)
+++ pypy/dist/pypy/module/cclp/types.py Wed Mar 21 12:19:50 2007
@@ -40,9 +40,11 @@
w("FUT", str(w_self))
+from pypy.module._cslib import fd
+
class W_CVar(W_Var):
def __init__(self, space, w_dom, w_name):
- assert isinstance(w_dom, W_AbstractDomain)
+ assert isinstance(w_dom, fd.W_FiniteDomain)
W_Var.__init__(self, space)
self.w_dom = w_dom
self.name = space.str_w(w_name)
@@ -58,6 +60,12 @@
def w_name(self):
return self.w_nam
+ def assign(self, w_var):
+ if not w_var.w_dom.contains(w_val):
+ raise ValueError, "assignment out of domain"
+ w_var.w_bound_to = w_val
+
+
def domain_of(space, w_v):
if not isinstance(w_v, W_CVar):
raise OperationError(space.w_TypeError,
@@ -85,95 +93,42 @@
class Solution(Exception): pass
-#-- Constraint ---------------------------------------------
+class FailedSpace(Exception): pass
-class W_Constraint(baseobjspace.Wrappable):
- def __init__(self, object_space):
- self._space = object_space
-
-W_Constraint.typedef = typedef.TypeDef("W_Constraint")
-
-class W_AbstractDomain(baseobjspace.Wrappable):
- """Implements the functionnality related to the changed flag.
- Can be used as a starting point for concrete domains"""
-
- def __init__(self, space):
- self._space = space
- self._changed = W_Var(self._space)
-
- def give_synchronizer(self):
- pass
-
- def get_values(self):
- pass
-
- def remove_values(self, values):
- assert isinstance(values, list)
-
- def size(self):
- pass
-
-W_AbstractDomain.typedef = typedef.TypeDef("W_AbstractDomain")
-
-class W_AbstractDistributor(baseobjspace.Wrappable):
-
- def __init__(self, space, fanout):
- assert isinstance(fanout, int)
- self._space = space
- self._fanout = fanout
- self._cspace = get_current_cspace(space)
-
-W_AbstractDistributor.typedef = typedef.TypeDef("W_AbstractDistributor")
-
-#-- Space Coroutine ----------------------
-
-
-class SpaceCoroutine(AppClonableCoroutine):
- def __init__(self, space, state=None):
- AppClonableCoroutine.__init__(self, space, state)
- self._cspace = None
- self._next = self._prev = None
-
- def _clone(self):
- if not we_are_translated():
- raise NotImplementedError
-
- space = self.space
- costate = self.costate
- if costate.current is self:
- raise OperationError(space.w_RuntimeError,
- space.wrap("clone() cannot clone the "
- "current coroutine"
- "; use fork() instead"))
- copy = SpaceCoroutine(space, state=costate)
-
- # This part is a copy of InterpClonableMixin.clone_into
- # we can't use it because extradata as a different signature
- # see the comments there for explanations
- # ----------------------------------------------------------
- copy.parent = self.parent
- self.hello_local_pool()
- data = (self.frame, self.subctx, self._cspace)
- self.goodbye_local_pool()
- # clone!
- data, copy.local_pool = gc_clone(data, self.local_pool)
- copy.frame, copy.subctx, copy._cspace = data
- return copy
-
- #XXX idea for future :
- # only call AppCoroutine.hello() there
- # do main_thread.hello|goodbye_local_pool when switching spaces
- # we will need to clone the other coros stackframes and
- # restuff these into fresh AppCoroutine shells
- # (because AppCoros have finalizers, hence are not cloned)
- def hello(self):
- w('Hello coro %d' % id(self) )
- AppClonableCoroutine.hello(self)
-
- def goodbye(self):
- w('Bye coro %d' % id(self))
- AppClonableCoroutine.goodbye(self)
+#-- Ring (used by scheduling entities)
+class RingMixin(object):
+ _mixin_ = True
+ """
+ useless till we can give a type parameter
+ """
+
+ def init_head(self, head):
+ self._head = head
+ head._next = head._prev = head
+ self._count = 1
+
+ def chain_insert(self, obj):
+ r = self._head
+ l = r._prev
+ l._next = obj
+ r._prev = obj
+ obj._prev = l
+ obj._next = r
+
+ def remove(self, obj):
+ l = obj._prev
+ r = obj._next
+ l._next = r
+ r._prev = l
+ if self._head == obj:
+ self._head = r
+ if r == obj:
+ # that means obj was the last one
+ # the group is about to die
+ self._head = None
+ obj._next = obj._prev = None
+
#-- Misc ---------------------------------------------------
Modified: pypy/dist/pypy/module/cclp/variable.py
==============================================================================
--- pypy/dist/pypy/module/cclp/variable.py (original)
+++ pypy/dist/pypy/module/cclp/variable.py Wed Mar 21 12:19:50 2007
@@ -289,7 +289,7 @@
def _assign(space, w_var, w_val):
assert isinstance(w_var, W_Var)
if isinstance(w_var, W_CVar):
- if not w_val in space.unpackiterable(w_var.w_dom._values):
+ if not w_val in w_var.w_dom.domain.get_wvalues_in_rlist():
raise_unification_failure(space, "assignment out of domain")
w_var.w_bound_to = w_val
More information about the Pypy-commit
mailing list