From c5499403b5073170f836e5e7f9008620075bcb25 Mon Sep 17 00:00:00 2001 From: timv Date: Fri, 14 Dec 2012 12:23:21 -0500 Subject: [PATCH] minor bugfix in plus equals and times equals aggregator, regarding None values. This tweaks the reactivity issues I was having with the papa2 example. I've also made a few minor, cleanup/tweaks to stdlib. --- bin/defn.py | 4 +- bin/prototype.py | 10 --- bin/stdlib.py | 150 +++++++++++++++++++++++--------------------- examples/papa2.dyna | 8 +-- 4 files changed, 84 insertions(+), 88 deletions(-) diff --git a/bin/defn.py b/bin/defn.py index bd78868..ef1579b 100644 --- a/bin/defn.py +++ b/bin/defn.py @@ -73,12 +73,12 @@ def agg_bind(agg_decl, table): return min(s) def plus_equals(item): - s = [k*m for k, m in table[item].iteritems()] + s = [k*m for k, m in table[item].iteritems() if m != 0] if len(s): return reduce(operator.add, s) def times_equals(item): - s = [k**m for k, m in table[item].iteritems()] + s = [k**m for k, m in table[item].iteritems() if m != 0] if len(s): return reduce(operator.mul, s) diff --git a/bin/prototype.py b/bin/prototype.py index 23c670e..d8a08e1 100644 --- a/bin/prototype.py +++ b/bin/prototype.py @@ -158,11 +158,9 @@ def circuit(anf): g = Hypergraph() for var, op, args in evals: - #if not args: args, op = [op], 'const' # todo: useless special case? g.edge(head=var, label=op, body=args) for var, op, args in unifs: -# g.edge(head=var, label='& %s(%s)' % (op, ','.join(map(str, args))), body=args) g.edge(head=var, label='& %s' % op, body=args) g.head = head @@ -330,14 +328,6 @@ function selectline(lineno) { """ % (bline, pretty_code) -# print magenta % '%s:%s' % (bline, eline) -# print yellow % code - -# from debug import ip; ip() - - # connect code lines with update; anf; and rendered circuit - # examples/papa2.dyna:37:1 - examples/papa2.dyna:37:24 : - print >> html, '' print >> html, '' diff --git a/bin/stdlib.py b/bin/stdlib.py index 366bb5a..4032315 100644 --- a/bin/stdlib.py +++ b/bin/stdlib.py @@ -8,11 +8,12 @@ use of. import os, sys from collections import defaultdict, Counter +from argparse import ArgumentParser from utils import red, green, blue, magenta - from defn import agg_bind, call + # TODO: as soon as we have safe names for these things we can get rid of this. class chart_indirect(dict): def __missing__(self, key): @@ -22,7 +23,12 @@ class chart_indirect(dict): ncols = arity + 1) # add one for output variable return c + chart = chart_indirect() +_delete = False +agenda = set() +aggregator = defaultdict(Counter) + def dump_charts(out=sys.stdout): print >> out @@ -33,14 +39,7 @@ def dump_charts(out=sys.stdout): fns.sort() for x in fns: - print >> out, x - print >> out, '=====================================' - - rows = [(pretty((x,idx)), idx, row, pretty(row[-1])) for idx, row in chart[x].data.items() if row[-1] is not None] - rows.sort() - - for p, _, _, v in rows: - print >> out, '%-30s := %s' % (p, v) + print >> out, chart[x] print >> out @@ -52,12 +51,19 @@ class Chart(object): self.data = {} self._id = 0 + def __repr__(self): + rows = [(r, pretty((self.name, i)), pretty(r[-1])) for i, r in self.data.items() if r[-1] is not None] + x = '\n'.join('%-30s := %s' % (p, v) for _, p, v in sorted(rows)) + return '%s\n=================\n%s' % (self.name, x) + def __getitem__(self, item): assert isinstance(item, tuple) and len(item) == self.ncols, \ 'item width mismatch: ncols %s, item %s' % (self.ncols, len(item)) 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.values(): # XXX: very inefficient + + # XXX: very inefficient + for row in self.data.values(): # note: dict changes size during iteration if row[-1] is None: continue if all(row[i] == val for i, val in nonslice): @@ -74,7 +80,7 @@ class Chart(object): emit(item, now) go() - def lookup(self, *args): + def lookup(self, args): "find index for these args" assert len(args) == self.ncols - 1 # XXX: lookup doesn't want val? args = list(args) @@ -87,13 +93,16 @@ class Chart(object): assert len(args) == self.ncols and isinstance(args, list) self.data[ix] = args - def insert(self, args): + def insert(self, args, val): + + args = list(args) + args.append(val) # debugging check: row is not already in chart. - assert self.lookup(*args[:-1]) is None, '%s already in chart' % (args,) + assert self.lookup(args[:-1]) is None, '%s already in chart' % (args,) idx = self.next_id() - self.update(idx, list(args)) + self.update(idx, args) return idx def next_id(self): @@ -103,7 +112,7 @@ class Chart(object): def pretty(item): - "Pretty print a term. Will retrieve the complete (ground) term for the chart." + "Pretty print a term. Will retrieve the complete (ground) term from the chart." if not isinstance(item, tuple): return repr(item) (fn, idx) = item @@ -159,7 +168,6 @@ def register(fn): return wrap -# NOTE: global & mutable register.handlers = defaultdict(list) @@ -188,33 +196,29 @@ def update_dispatcher(item, val): handler(item, val) -def peel(fn, x): +def peel(fn, item): """ - Lookup `idx` in the intern table. Asserts that idx matches functor/arity, - `fn`. Returns the arguments of term as a tuple of intern idxs and constants. + Find item's args in the appropriate chart. Assert that idx matches + functor/arity, `fn`. Returns the arguments of term as a tuple of intern idxs + and constants (possibly an empty tuple). """ - assert isinstance(x, tuple) - (fa, idx) = x + assert isinstance(item, tuple) + (fa, idx) = item 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? + idx = chart[fn].lookup(args) if idx is None: - idx = chart[fn].insert(args + (None,)) # don't know val yet. + idx = chart[fn].insert(args, None) # don't know val yet. return (fn, idx) -_delete = False - def emit(item, val): - (fn, idx) = item - row = chart[fn].data[idx] - was = row[-1] print (red if _delete else green) \ - % 'emit %s (val %s; was: %s)' % (pretty(item), val, was) + % 'emit %s (val %s; curr: %s)' % (pretty(item), val, lookup(item)) if _delete: aggregator[item][val] -= 1 @@ -234,16 +238,17 @@ def delete(item, val): _delete = False - -aggregator = defaultdict(Counter) - def aggregate(item): (fn, _) = item - return agg[fn](item) # agg is defined after updates are loaded + v = agg[fn](item) + if v is None: + print 'aggregator empty' + return v - -agenda = set() +def lookup(item): + (fn, idx) = item + return chart[fn].data[idx][-1] def _go(): @@ -254,14 +259,13 @@ def _go(): print print 'pop', pretty(item) - was = chart[fn].data[idx][-1] # last cell is val + was = lookup(item) + now = aggregate(item) - now = aggregate(item) # compute new val for item - - print ' was %s now %s' % (was, now) + print 'was %s, now %s' % (was, now) if was == now: - print ' unchanged' + print 'unchanged' continue if was is not None: @@ -280,24 +284,22 @@ def go(): pass finally: dump_charts() - - with file(dyna + '.chart', 'wb') as f: + with file(argv.source + '.chart', 'wb') as f: dump_charts(f) -try: - [dyna] = sys.argv[1:] -except ValueError: - print 'Dyna interpreter:\n\tplease specify a source file.' - sys.exit(1) +parser = ArgumentParser(description=__doc__) +parser.add_argument('source', help='Path to Dyna source file.') +argv = parser.parse_args() -cmd = """ghc -isrc Dyna.Backend.Python -e 'processFile "%s"' """ % dyna + +cmd = """ghc -isrc Dyna.Backend.Python -e 'processFile "%s"' """ % argv.source assert 0 == os.system(cmd), 'command failed:\n\t' + cmd agg_decl = None # filled in after exec, this only here to satisfy lint checker. - -execfile(dyna + '.plan') +# load generated code. +execfile(argv.source + '.plan') # bind aggregators to definitions @@ -312,6 +314,9 @@ go() +#_____________ +# REPL stuff. + class UserChart(object): def __init__(self, _chart): @@ -322,48 +327,51 @@ class UserChart(object): def __getitem__(self, item): assert isinstance(item, tuple) and len(item) == self.ncols - 1 - nonslice = [(i, val) for i, val in enumerate(item) if not isinstance(val, slice)] + nonslice = [(i, v) for i, v in enumerate(item) if not isinstance(v, slice)] for idx, row in self.data.iteritems(): - if row[-1] is None: + val = row[-1] + if val is None: continue if all(row[i] == val for i, val in nonslice): - yield (self.name, idx) +# yield (self.name, idx) + print pretty((self.name, idx)), ':=', val - def __setitem__(self, item, now): - # XXX: can't add new things only change old things.. + def __setitem__(self, args, now): - assert isinstance(item, tuple) and len(item) == self.ncols - 1 - nonslice = [(i, v) for i, v in enumerate(item) if not isinstance(v, slice)] + assert isinstance(args, tuple) and len(args) == self.ncols - 1 + nonslice = [(i, v) for i, v in enumerate(args) if not isinstance(v, slice)] foundone = False for idx, row in self.data.iteritems(): if all(row[i] == val for i, val in nonslice): item = (self.name, idx) - # timv: technically, should restrict to ":=" and extensional - aggregator[item].clear() - if now is not None: - aggregator[item][now] += 1 - agenda.add(item) - + colon_equals(item, now) foundone = True - if not foundone and len(nonslice) == len(item): - idx = self._chart.insert(item + (None,)) + if not foundone and len(nonslice) == len(args): + idx = self._chart.insert(args, None) item = (self.name, idx) - aggregator[item].clear() - if now is not None: - aggregator[item][now] += 1 - agenda.add(item) + colon_equals(item, now) go() -#constructor_names = {''.join(functor.split('/')[:-1]) for functor in chart} +def colon_equals(item, now): + # timv: technically, should restrict to ":=" and extensional + aggregator[item].clear() + if now is not None: + aggregator[item][now] += 1 + agenda.add(item) # XXX: probably shouldn't queue automatically. + + for _fn in chart: try: exec '%s = UserChart(chart[%r])' % (_fn.replace('/', ''), _fn) except: pass -from IPython import embed; embed() + +from IPython.frontend.terminal.embed import InteractiveShellEmbed +ipshell = InteractiveShellEmbed(banner1 = 'Dropping into IPython\n') +ipshell() diff --git a/examples/papa2.dyna b/examples/papa2.dyna index a9c8140..7f8a833 100644 --- a/examples/papa2.dyna +++ b/examples/papa2.dyna @@ -1,8 +1,8 @@ % Parsing a simple sentence. % CKY-like parsing -phrase(X,I,K,t(X,TY)) max= phrase(Y,I,K,TY) * rewrite(X,Y). -phrase(X,I,K,t(X,TY,TZ)) max= phrase(Y,I,J,TY) * phrase(Z,J,K,TZ) * rewrite(X,Y,Z). +phrase(X,I,K,t(X,TY)) += phrase(Y,I,K,TY) * rewrite(X,Y). +phrase(X,I,K,t(X,TY,TZ)) += phrase(Y,I,J,TY) * phrase(Z,J,K,TZ) * rewrite(X,Y,Z). goal(P) += phrase("S", 0, *length, P). @@ -36,6 +36,4 @@ word( "a", 5). word( "spoon", 6). word( ".", 7). -% for fun, try making the above words facts instead of +=1's. - -phrase(W, I, I+1, W) max= (word(W, I) > 0) & 1. +phrase(W, I, I+1, W) += word(W, I), 1. -- 2.50.1