import math, operator
from collections import defaultdict, Counter
+from utils import red, green, blue, magenta
# Call indirection tables defines mathematical operators and the like.
'//2': operator.div,
'-/2': operator.sub,
'+/2': operator.add,
- 'log': math.log,
- 'exp': math.exp,
- '^/2': math.pow}
-
-
-# Update handler indirection -- a true hack. Allow us to have many handlers on
-# the same functor/arity
-
-# TODO: fuse update handlers instead of dispatching to each one independently.
-
-def register(functor_arity):
- """
- Decorator for registering update handlers. Used by update dispatcher.
-
- Note: registration is with a global/mutable table.
-
- For testing purposes clear current handlers
- >>> register.handlers.clear()
-
- >>> @register("test")
- ... def _(H, V):
- ... return 'OK', H, V
- ...
- >>> [h] = register.handlers['test']
- >>> h("head", "value")
- ('OK', 'head', 'value')
-
- """
-
- def wrap(handler):
- register.handlers[functor_arity].append(handler)
- # you can't call these guys directly. Must go thru handler
- # indirection table
- return None
-
- return wrap
-
-# NOTE: global & mutable
-register.handlers = defaultdict(list)
-
-
-def update_dispatcher(fn, idx, value):
- """
- Passes update to relevant handlers.
- """
- print 'dispatching update', (fn, idx, value)
- for handler in register.handlers[fn]:
- print 'sending update to handler'
- handler(idx, value)
+ '-/1': operator.neg,
-def emit(item, value):
- (fn, idx) = item
- row = chart[fn].data[idx]
- args = row[:-1]
- was = row[-1]
+# '~/1': operator.inv, # differs
+# '|/1': operator.or_,
+# '&/2': operator.and_,
- print 'emit: %s %s%s <== %s (current value: %s)' % (item, fn, args, value, was)
- aggregator[item][value] += 1 # timv: retract passes in -1?
- agenda.append(item)
+ # comparisons
+ '</2': operator.lt,
+ '<=/2': operator.le,
+ '>/2': operator.gt,
+ '>=/2': operator.ge,
+ '!=/2': operator.ne,
+ '==/2': operator.eq,
+# '<</2': operator.lshift,
+# '>>/2': operator.rshift,
-def peel(fn, (fa, idx)):
- """
- Lookup `idx` in the intern table. Asserts that idx matches
- `functor_arity`. Returns arguments of term as a arity-tuple of intern idxs and
- constants.
- """
- assert fa == fn
- return chart[fn].data[idx][:-1] # minus value
-
-
-def build(fn, *args):
+ 'mod/1': operator.mod,
+ 'abs/1': operator.abs,
+ 'log': math.log,
+ 'exp': math.exp,
- idx = chart[fn].lookup(*args) # lookup doesn't require value?
-
- if idx is None:
- idx = chart[fn].insert(args + (None,)) # don't know value yet.
-
- return (fn, idx)
+ '**/2': math.pow,
+ '^/2': math.pow} # differs from python
# TODO: as soon as we have safe names for these things we can get rid of this.
nonslice = [(i, val) for i, val in enumerate(item) if not isinstance(val, slice)]
slices = [i for i, val in enumerate(item) if isinstance(val, slice)]
- for row in self.data.itervalues(): # XXX: very inefficient
+ for row in self.data.values(): # XXX: very inefficient
+ if row[-1] is None:
+ continue
if all(row[i] == val for i, val in nonslice):
yield tuple(row[i] for i in slices)
def lookup(self, *args):
"find index for these args"
- assert len(args) == self.ncols - 1 # XXX: lookup doesn't want value?
+ assert len(args) == self.ncols - 1 # XXX: lookup doesn't want val?
+ args = list(args)
for idx, row in self.data.iteritems(): # XXX: very inefficient
if row[:-1] == args:
return idx # stop on first
def update(self, ix, args):
"Update chart"
+ assert len(args) == self.ncols and isinstance(args, list)
self.data[ix] = args
def insert(self, args):
+
+ # debug: simple integrity check
+ assert self.lookup(*args[:-1]) is None, '%s already in chart' % (args,)
+
idx = self.next_id()
self.update(idx, list(args))
return idx
fn = ''.join(fn.split('/')[:-1]) # drop arity from name.
return '%s%s' % (fn, tuple(args)) # TODO: need to recurse.
-def prettify(x):
+def prettify(x):
if isinstance(x, tuple):
return pretty(x)
elif isinstance(x, list):
raise ValueError("Don't know what to do with %r" % x)
-aggregator = defaultdict(Counter)
-agenda = []
+# Update handler indirection -- a true hack. Allow us to have many handlers on
+# the same functor/arity
+# TODO: fuse update handlers instead of dispatching to each one independently.
-def run_agenda():
- "the main loop"
- while agenda:
- item = agenda.pop()
- new = aggregate(item) # compute new value for item
- propagate(item, new)
+def register(functor_arity):
+ """
+ Decorator for registering update handlers. Used by update dispatcher.
+ Note: registration is with a global/mutable table.
-def aggregate(item):
- print 'aggregate:', pretty(item), aggregator[item],
+ For testing purposes clear current handlers
+ >>> register.handlers.clear()
- val = 0.0
- for k,v in aggregator[item].iteritems():
- val += k*v # value*multiplicity; TODO: use correct aggregator
+ >>> @register("test")
+ ... def _(H, V):
+ ... return 'OK', H, V
+ ...
+ >>> [h] = register.handlers['test']
+ >>> h("head", "val")
+ ('OK', 'head', 'val')
- print 'result:', val
+ """
- return val
+ def wrap(handler):
+ register.handlers[functor_arity].append(handler)
+ # you can't call these guys directly. Must go thru handler
+ # indirection table
+ return None
+ return wrap
-def delete(item, val):
- print 'TODO: implement deletion.'
+# NOTE: global & mutable
+register.handlers = defaultdict(list)
+
+
+def update_dispatcher((fn, idx), val):
+ """
+ Passes update to relevant handlers.
+ """
+ print 'dispatch', pretty((fn, idx)), '=', val
+ for handler in register.handlers[fn]:
+ print 'handler'
+ handler((fn, idx), val)
+
+
+def peel(fn, (fa, idx)):
+ """
+ Lookup `idx` in the intern table. Asserts that idx matches
+ `functor_arity`. Returns arguments of term as a arity-tuple of intern idxs and
+ constants.
+ """
+ assert fa == fn
+ return chart[fn].data[idx][:-1] # minus val
+def build(fn, *args):
+ idx = chart[fn].lookup(*args) # lookup doesn't require val?
+ if idx is None:
+ idx = chart[fn].insert(args + (None,)) # don't know val yet.
+ return (fn, idx)
-def propagate(item, now):
- fn, idx = item
+_delete = False
- was = chart[fn].data[idx][-1] # last is value
+def emit(item, val):
+ (fn, idx) = item
+ row = chart[fn].data[idx]
+ was = row[-1]
- print 'propagate: %-30s was %s now %s' % (pretty(item), was, now)
+ print (red if _delete else green) \
+ % 'emit %s (val %s; was: %s)' % (pretty(item), val, was)
- if was == now:
- return
+ if _delete:
+ aggregator[item][val] -= 1
+ else:
+ aggregator[item][val] += 1
- if was is not None:
- delete(item, was)
+ agenda.add(item)
- chart[fn].data[idx][-1] = now
- aggregator[item][now] += 1
- # enqueue work to the agenda
- agenda.append(item)
+aggregator = defaultdict(Counter)
+agenda = set()
+
+
+def run_agenda():
+ "the main loop"
+ while agenda:
+ (fn, idx) = item = agenda.pop()
+
+ print
+ print 'pop', pretty(item)
+
+ was = chart[fn].data[idx][-1] # last cell is val
+
+ if was is not None:
+ delete(item, was)
+
+ now = aggregate(item) # compute new val for item
+
+ print ' was %s now %s' % (was, now)
+
+ if was == now:
+ print ' unchanged'
+ return
+
+ chart[fn].data[idx][-1] = now
+
+ update_dispatcher(item, now)
+
+
+def aggregate(item):
+ print ' aggregate:', pretty(item), aggregator[item],
+ val = 0.0
+ for k,v in aggregator[item].iteritems():
+ val += k*v # val*multiplicity; TODO: use correct aggregator
+ print 'result:', val
+ return val
+
+
+def delete(item, val):
+ # XXX: very ugly handling of deletion
+ global _delete
+ _delete = True
+ update_dispatcher(item, val)
+ _delete = False
+
def papa_example():
map(chart['rewrite/3'].insert,
- [( "S", "S", ".", 1),
- ( "S", "NP", "VP", 1),
- ("NP", "Det", "N", 1),
- ("NP", "NP", "PP", 1),
- ("VP", "V", "NP", 1),
- ("VP", "VP", "PP", 1),
- ("PP", "P", "NP", 1)])
+ [( "S", "S", ".", 1.),
+ ( "S", "NP", "VP", 1.),
+ ("NP", "Det", "N", 1.),
+ ("NP", "NP", "PP", 1.),
+ ("VP", "V", "NP", 1.),
+ ("VP", "VP", "PP", 1.),
+ ("PP", "P", "NP", 1.)])
map(chart['rewrite/2'].insert,
- [( "NP", "Papa", 1),
- ( "N", "caviar", 1),
- ( "N", "spoon", 1),
- ( "V", "ate", 1),
- ( "P", "with", 1),
- ("Det", "the", 1),
- ("Det", "a", 1)])
+ [( "NP", "Papa", 1.),
+ ( "N", "caviar", 1.),
+ ( "N", "spoon", 1.),
+ ( "V", "ate", 1.),
+ ( "P", "with", 1.),
+ ("Det", "the", 1.),
+ ("Det", "a", 1.)])
for i, word in enumerate('Papa ate the caviar with a spoon .'.split()):
idx = chart['phrase/3'].insert((word, i, i + 1, None))
- propagate(('phrase/3', idx), 1)
+
+ emit(('phrase/3', idx), 1.0)