Monday, August 23, 2010

From what I can gather after some reading [1], [2], [3]; McCarthy's amb operator, would allow you to give the possible values for variables; then give functions that constrain the values; and finally extract values for those variables that satisfy the constraints just by calling amb without any arguments.

I decided to work backwards from how I thought it should be used to an implementation.

Given the problem of finding Pythagorean triples for integers <=10, i.e define three variables x, y, and z that can have values 1..10 subject to the constraint that x*x +y*y == z*z. What are the possible values of x, y and z that satisfy the constraint? For an amb-type solution adapted a little to python, being amb-like is all in the way that the problem is expressed. I would like to write something like:

I took a little liberty in the way I had seen other implementations work, as I had seen examples where the call of amb with an argument of a function just set things up and 'registered' the function, then subsequent calls of amb() without any arguments, would set the global variables used in the lambda function to successive values that satisfy the constraint. I reasoned that calls to amb after registering the constraint function should follow the Python iterator protocol ( with an __iter__ and a next method), so all the values, if any, could be accessed in a loop. I liked the idea of automatically setting global variables so decided to give that a go (but I was prepared to jettison this setting global variables if it proved long-winded).

The solution follows. To modify global names, I noted that functions have f.func_globals which is the globals environment in which the function was created. I get this from the lambda expression. I also cheat a little in requiring the parameter names of the constraint function to be the global names that were previously assigned value ranges.

def__call__(self, arg=None):if hasattr(arg, 'func_globals'):#### Called with a constraint function. ## globls = arg.func_globals# Names used in constraint argv = arg.__code__.co_varnames[:arg.__code__.co_argcount]for name in argv:if name notin self._names2values:assert name in globls, \ "Global name %s not found in function globals" % name self._names2values[name] = globls[name]# Gather the range of values of all names used in the constraint valuesets = [self._names2values[name] for name in argv] self._valueiterator = _itertools.product(*valuesets) self._func = arg self._funcargnames = argvreturn selfelif arg isnot None:#### Assume called with an iterable set of values## arg = frozenset(arg)return argelse:#### blank call tries to return next solution##return self._nextinsearch()

Hi Kay, I read the blog post on Choosers. It seemed to be something quite like amb, but where I had looked at amb merely as an intellectual curiosity, Choosers are applied to the field of software test.

This got me thinking about my field of hardware test where some testbenches rely heavily on the pseudo-random generation of values subject to a set of constraints. Amb would be a nice way to specify constraints and value-sets.

It would be interesting to see this kind of algorithm applied to the Java PathFinder project. I have no idea how you'd go about it, but it would make for a really cool, fairly straightforward research topic.

Hi programmingpraxis, your article was one I skimmed in my research into just what amb is and how it would work in Python. I had also seen this: http://c2.com/cgi/wiki?AmbInPython, which helped me on the way, but did not like the way things were done there. I liked better, what was done here: http://www.randomhacks.net/articles/2005/10/11/amb-operator in Ruby.

To the anonymous who wants to know what amb means. It is indeed short for ambiguous, but you would have to do more reading of John McCarthy's papers to find out why he thought it apt.