- print nullary terms together at the top.
- don't print empty charts
- heading is "Solution" instead of "Charts"
REPL cmd 'chart' renamed to 'sol'
Added lots of help documentation for REPL
-Charts
-============
+Solution
+========
+end := "MyHouse"
+goal := 2410
+start := "FriendHouse"
+
edge/2
-=================
+======
edge("BOS","JFK") := 187
edge("BOS","MIA") := 1258
edge("DFW","LAX") := 1235
edge("ORD","DFW") := 802
edge("ORD","MyHouse") := 20
-end/0
-=================
-end := "MyHouse"
-
-goal/0
-=================
-goal := 2410
-
path/1
-=================
+======
path("BOS") := 10
path("DFW") := 1588
path("FriendHouse") := 0
path("ORD") := 2390
path("SFO") := 2779
-start/0
-=================
-start := "FriendHouse"
-
-Charts
-============
-a/0
-=================
-a := 0
-
-b/0
-=================
-b := 0
-
-by_evl_cross/0
-=================
-by_evl_cross := true
-
-by_evl_refl/0
-=================
-by_evl_refl := true
-
-by_is_0a/0
-=================
-by_is_0a := true
-
-by_is_ab/0
-=================
-
-
-by_syn_cross/0
-=================
-by_syn_cross := false
-
-by_syn_refl/0
-=================
-by_syn_refl := true
+Solution
+========
+a := 0
+b := 0
+by_evl_cross := true
+by_evl_refl := true
+by_is_0a := true
+by_syn_cross := false
+by_syn_refl := true
-Charts
-============
+Solution
+========
+lim := 10
+
f/1
-=================
+===
f(1) := 1
f(2) := 1
f(3) := 2
f(8) := 21
f(9) := 34
-lim/0
-=================
-lim := 10
-
-Charts
-============
-a/0
-=================
-
-
-b/0
-=================
-
-
-c/0
-=================
-
+Solution
+========
cols/1
-=================
+======
cols(a) := 2
cols(b) := 3
cols(c) := 3
m/3
-=================
+===
m(a,1,1) := 1
m(a,1,2) := 0
m(a,2,1) := 0
m(c,2,3) := 0
product/2
-=================
+=========
product(a,b) := c
rows/1
-=================
+======
rows(a) := 2
rows(b) := 2
rows(c) := 2
shape/3
-=================
+=======
shape(a,2,2) := true
shape(b,2,3) := true
shape(c,2,3) := true
times/4
-=================
+=======
times(a,b,1,1) := 3
times(a,b,1,2) := 0
times(a,b,1,3) := 1
-Charts
-============
-best/0
-=================
-best := pair(1,t("S",t("S",t("NP","Papa"),t("VP",t("VP",t("V","ate"),t("NP",t("Det","the"),t("N","caviar"))),t("PP",t("P","with"),t("NP",t("Det","a"),t("N","spoon"))))),"."))
-
-bestParse/0
-=================
-bestParse := t("S",t("S",t("NP","Papa"),t("VP",t("VP",t("V","ate"),t("NP",t("Det","the"),t("N","caviar"))),t("PP",t("P","with"),t("NP",t("Det","a"),t("N","spoon"))))),".")
-
-bestScore/0
-=================
-bestScore := 1
+Solution
+========
+best := pair(1,t("S",t("S",t("NP","Papa"),t("VP",t("VP",t("V","ate"),t("NP",t("Det","the"),t("N","caviar"))),t("PP",t("P","with"),t("NP",t("Det","a"),t("N","spoon"))))),"."))
+bestParse := t("S",t("S",t("NP","Papa"),t("VP",t("VP",t("V","ate"),t("NP",t("Det","the"),t("N","caviar"))),t("PP",t("P","with"),t("NP",t("Det","a"),t("N","spoon"))))),".")
+bestScore := 1
+length := 8
goal/1
-=================
+======
goal(t("S",t("S",t("NP","Papa"),t("VP",t("V","ate"),t("NP",t("NP",t("Det","the"),t("N","caviar")),t("PP",t("P","with"),t("NP",t("Det","a"),t("N","spoon")))))),".")) := 1
goal(t("S",t("S",t("NP","Papa"),t("VP",t("VP",t("V","ate"),t("NP",t("Det","the"),t("N","caviar"))),t("PP",t("P","with"),t("NP",t("Det","a"),t("N","spoon"))))),".")) := 1
-length/0
-=================
-length := 8
-
-pair/2
-=================
-
-
phrase/4
-=================
+========
phrase(".",7,8,".") := 1
phrase("Det",2,3,t("Det","the")) := 1
phrase("Det",5,6,t("Det","a")) := 1
phrase("with",4,5,"with") := 1
rewrite/2
-=================
+=========
rewrite("Det","a") := 1
rewrite("Det","the") := 1
rewrite("N","caviar") := 1
rewrite("V","ate") := 1
rewrite/3
-=================
+=========
rewrite("NP","Det","N") := 1
rewrite("NP","NP","PP") := 1
rewrite("PP","P","NP") := 1
rewrite("VP","V","NP") := 1
rewrite("VP","VP","PP") := 1
-t/2
-=================
-
-
-t/3
-=================
-
-
word/2
-=================
+======
word(".",7) := true
word("Papa",0) := true
word("a",5) := true
-Charts
-============
-a/0
-=================
-a := true
-
-b/0
-=================
-b := true
-
-c/0
-=================
-c := true
+Solution
+========
+a := true
+b := true
+c := true
ds <- get
-- A python map of variable name to value
- let varmap = braces $ align $ fillPunct (comma <> space) $
- ("'nodes'" <> colon <> (encloseSep lbracket rbracket comma $ map (("d"<>).pretty) [0..ds-1]))
- : (map (\v -> let v' = pretty v in dquotes v' <> colon <+> v') vs)
+ let varmap = brackets $ align $ fillPunct (comma <> space) $
+ parens ("'nodes'" <> comma <> (encloseSep lbracket rbracket comma $ map (("d"<>).pretty) [0..ds-1]))
+ : (map (\v -> let v' = pretty v in parens (dquotes v' <> comma <+> v')) vs)
return $ "emit" <> tupled [ pretty h
, pretty r
, pretty i
- , varmap
+ , "tuple" <> (parens $ varmap)
]
-- | Render a dopamine sequence's checks and loops above a (indended) core.
return aggregator(self.agg_name)
def __repr__(self):
+
rows = [term for term in self.intern.values() if term.value is not None]
+
+ if not rows:
+ return ''
+
+ if self.arity == 0:
+ return '%s := %s' % (term, _repr(term.value))
+
x = '\n'.join('%-30s := %s' % (term, _repr(term.value)) for term in sorted(rows))
- return '%s\n=================\n%s' % (self.name, x)
+ return '%s\n%s\n%s\n' % (self.name, '='*len(self.name), x)
def __getitem__(self, s):
assert len(s) == self.arity + 1, \
class BAggregator(Counter, Aggregator):
-# def __init__(self):
+# def __init__(self):
# super(BAggregator, self).__init__()
def inc(self, val, ruleix, variables):
self[val] += 1
self[val] -= 1
def fromkeys(self, *_):
assert False, "This method should never be called."
-
+
class PlusEquals(object):
__slots__ = 'pos', 'neg'
"Post process the variables past to emit (which passes them to aggregator)."
# remove the 'u' prefix on user variables 'uX'
# Note: We also ignore user variables with an underscore prefix
- return tuple((name[1:], val) for name, val in variables.items()
+ return tuple((name[1:], val) for name, val in variables
if name.startswith('u') and not name.startswith('u_'))
+from term import _repr
+def drepr(vs):
+ return '{%s}' % ', '.join('%s=%s' % (k, _repr(v)) for k,v in vs.iteritems())
+
+from collections import namedtuple
+class Result(namedtuple('Result', 'value variables')):
+ def __repr__(self):
+ return 'Result(value=%s, variables=%s)' % (_repr(self.value), drepr(dict(self.variables)))
+
+
class DictEquals(BAggregator):
def inc(self, val, ruleix, variables):
self[val, vs] -= 1
def fold(self):
- return list((v, dict(b)) for (v, b), cnt in self.iteritems())
+ return tuple(Result(v, b) for (v, b), cnt in self.iteritems() if cnt > 0)
class majority_equals(BAggregator):
Maybe subscription to diff is a different beast, only available as a
procedural world.
+ - TODO: True and 1 are equivalent. This sometimes leads to strange behavior.
+
- New syntax for doing repl stuff (@nwf): load, subscribe, post-process
- sheebang
- crash handler
- - where to errors go?
+ - where do errors go?
- @nwf suggests temporary measure for LSA students: time-stamped file
sitting in the users home directory,
- Collect all query modes used by the planner. Consider indexing value column
if plans need it.
- - dynac should provide routines for building terms. We can hack something
- together with anf output, but this will be prety kludgy and inefficient.
-
- better default prioritization (currently FIFO)
- BAggregators aren't very efficient.
from collections import defaultdict
from hashlib import sha1
from time import time
+from path import path
import load, post
def dump_charts(self, out=sys.stdout):
print >> out
- print >> out, 'Charts'
- print >> out, '============'
+ print >> out, 'Solution'
+ print >> out, '========'
fns = self.chart.keys()
fns.sort()
- for x in fns:
- print >> out, self.chart[x]
+ nullary = [x for x in fns if x.endswith('/0')]
+ others = [x for x in fns if not x.endswith('/0')]
+ # show nullary charts first
+ for x in nullary:
+ y = str(self.chart[x]) # skip empty chart
+ if y:
+ print >> out, y
+ if nullary:
print >> out
+ for x in others:
+ y = str(self.chart[x]) # skip empty chart
+ if y:
+ print >> out, y
self.dump_errors(out)
def dump_errors(self, out=sys.stdout):
return
print >> out
print >> out, 'Errors'
- print >> out, '============'
+ print >> out, '======'
for item, (val, es) in self.error.items():
print >> out, 'because %r is %s:' % (item, _repr(val))
for e, h in es:
rule.updaters.append(handler)
handler.rule = rule
-
def gbc(self, fn, *args):
# TODO: need to distinguish `unknown` from `null`
item.aggregator.dec(val, ruleix, variables)
else:
item.aggregator.inc(val, ruleix, variables)
-# self.agenda[item] = 0 # everything is high priority
self.agenda[item] = time() # FIFO
def repl(self):
import repl
- repl.REPL(self, dotdynadir / 'dyna.hist').cmdloop()
+ repl.REPL(self).cmdloop()
def do(self, filename, initialize=True):
"""
assert item.fn == fn
return item.args
-from path import path
+
def main():
parser = argparse.ArgumentParser(description="The dyna interpreter!")
parser.add_argument('source', nargs='?', type=path,
from matrix import matrix
from pickled import pickled
-import re
+import re as _re
def run(interp, line):
- [(name, module, args)] = re.findall('^([a-z][a-zA-Z_0-9]*) = ([a-z][a-zA-Z_0-9]*)\((.*)\)', line)
+ [(name, module, args)] = _re.findall('^([a-z][a-zA-Z_0-9]*) = ([a-z][a-zA-Z_0-9]*)\((.*)\)', line)
m = getattr(__import__('load'), module)(interp, name)
exec 'm.main(%s)' % args
interp.go()
-import re
+import re as _re
from save import save
from graph import graph
from draw_circuit import draw_circuit
-from dump_chart import dump_chart
+from dump_solution import dump_solution
def run(interp, line):
- [(name, args)] = re.findall('([a-z][a-zA-Z_0-9]*)\((.*)\)$', line)
+ [(name, args)] = _re.findall('([a-z][a-zA-Z_0-9]*)\((.*)\)$', line)
m = globals()[name](interp)
eval('m.main(%s)' % args)
-"""
-Crude visualization of circuit pertaining to state of the interpreter.
-"""
-
import webbrowser
from debug import Hypergraph
from cStringIO import StringIO
# Use rule initializers to find all active hyperedges in the current Chart.
def _emit(item, _, ruleix, variables):
- b = variables['nodes']
+ b = dict(variables)['nodes']
b.sort()
b = tuple(b)
edges.add((item, ruleix, b))
class draw_circuit(object):
+ """
+ Crude visualization of circuit pertaining to state of the interpreter.
+ """
def __init__(self, interp):
self.interp = interp
-
-
-"""
-Save interpreter state using python's pickle protocol.
-"""
-
import sys
-
-class dump_chart(object):
-
+class dump_solution(object):
+ """
+ Print solution
+ """
def __init__(self, interp):
self.interp = interp
-"""
-Postprocessor for animated visualization of basic elements such as lines and
-text.
-
-We look for the following patterns in the dynabase
-
- visual element
- v
- frame(T, &text(String, tuple(X, Y))).
- ^
- time index
-
-Frames should have value true. The example above places a text element reading
-`String` at position `(X,Y)` in a frame at time `T`. This element can be
-specified by dyna rule.
-"""
-
import pylab as pl
from matplotlib.animation import FuncAnimation
from collections import defaultdict
class graph(object):
+ """
+ Postprocessor for animated visualization of basic elements such as lines and
+ text.
+
+ We look for the following patterns in the dynabase
+
+ visual element
+ v
+ frame(T, &text(String, tuple(X, Y))).
+ ^
+ time index
+
+ Frames should have value true. The example above places a text element reading
+ `String` at position `(X,Y)` in a frame at time `T`. This element can be
+ specified by dyna rule.
+ """
def __init__(self, interp):
self.interp = interp
- def main(self, outfile):
-
+ def main(self, outfile, fps=30):
+
frame = defaultdict(list)
for _, [t, item], val in self.interp.chart['frame/2'][:,:,:]:
if val:
frame[t].append(item)
-
+
nframes = max(frame)
-
+
def draw_frame(t):
ax.cla()
ax.set_title(t)
ax.text(x,y,s)
else:
print 'dont know how to render', item
-
+
fig = pl.figure()
ax = pl.axes()
-
- print 'creating animation..'
anim = FuncAnimation(fig, draw_frame, frames=nframes)
- print 'saving...'
- anim.save(outfile, fps=30, extra_args=['-vcodec', 'libx264'])
- print 'wrote examples/force.dyna.mp4'
+ anim.save(outfile, fps=fps, extra_args=['-vcodec', 'libx264'])
-"""
-Save interpreter state using python's pickle protocol.
-"""
import cPickle
-
class save(object):
+ """
+ Save interpreter state using python's pickle protocol.
+ """
def __init__(self, interp):
self.interp = interp
-import os, cmd, readline
+"""
+TODO: unsubscribe
+
+TODO: should probably remove the new rule after we get the results.
+
+TODO: probably should show "changed"
+
+TODO: queries are all maintained... should probably toss out the query rule. If
+users want queries to be kept up-to-date user should subscribe instead.
+
+TODO: help should print call signature of loads and post-processors in addition
+to help.
+
+"""
+
+import re, os, cmd, readline
import debug, interpreter
from utils import ip
from interpreter import Interpreter, foo, none
+from term import _repr
+from defn import drepr
+
+
+def lexer(term):
+ return re.findall('"[^"]*"' # string
+ '|[a-z][a-zA-Z_0-9]*' # functor
+ '|[A-Z][a-zA-Z0-9_]*' # variable
+ '|[(), ]' # parens and comma
+ '|[^(), ]+', term) # everything else
+
+
+def subst(term, v):
+ """
+ >>> subst('f("asdf",*g(1,X, Y), X+1)', {'X': 1234})
+ 'f("asdf",*g(1,1234, Y), 1234+1)'
+
+ >>> subst('f("asdf",*g(1,X, Y), XX+1)', {'X': 1234})
+ 'f("asdf",*g(1,1234, Y), XX+1)'
+
+ >>> subst('f("asdf",*g(1,uX, Y), X_+1)', {'X': 1234})
+ 'f("asdf",*g(1,uX, Y), X_+1)'
+
+ """
+ assert isinstance(v, dict)
+ return ''.join((_repr(v[x]) if x in v else x) for x in lexer(term))
+
class REPL(cmd.Cmd, object):
- def __init__(self, interp, hist):
+ def __init__(self, interp, hist=dotdynadir / 'dyna.hist'):
self.interp = interp
cmd.Cmd.__init__(self)
self.hist = hist
readline.read_history_file(hist)
self.lineno = 0
+ # create help routines based on doc string.
+ for x, v in REPL.__dict__.iteritems():
+ if x.startswith('do_') and hasattr(v, '__doc__'):
+ def show_doc(d=v.__doc__):
+ print d
+ setattr(self, 'help_' + x[3:], show_doc)
+
@property
def prompt(self):
- return ':- ' #% self.lineno
+ return ':- '
def do_rules(self, _):
+ """
+ List rules in the program.
+ """
self.interp.dump_rules()
def do_retract_rule(self, idx):
+ """
+ Retract rule from program by rule index.
+
+ :- a += 1.
+ :- b += 1.
+ :- c += a*b.
+
+ :- rules
+ 0: a += 1.
+ 1: b += 1.
+ 2: c += a * b.
+
+ :- retract_rule 0
+
+ This removes rule 0 from the program. Now, let's inspect the changes to
+ the solution.
+
+ :- sol
+
+ Solution
+ ========
+ b := 1.
+
+ """
self.interp.retract_rule(int(idx))
def do_exit(self, _):
+ """
+ Exit REPL by typing exit or control-d. See also EOF.
+ """
readline.write_history_file(self.hist)
return -1
self.lineno += 1
return stop
- def do_chart(self, _):
+ def do_sol(self, _):
+ """
+ Show solution.
+ """
self.interp.dump_charts()
def emptyline(self):
pass
def do_ip(self, _):
+ """
+ Development tool. Jump into an interactive python shell.
+ """
ip()
def do_debug(self, line):
+ """
+ Development tool. Used for view Dyna's intermediate representations.
+ """
with file(dotdynadir / 'repl-debug-line.dyna', 'wb') as f:
f.write(line)
debug.main(f.name)
- def do_query(self, line):
-
- if line.endswith('.'):
+ def _query(self, q):
+ if q.endswith('.'):
print "Queries don't end with a dot."
return
-
- query = 'out(%s) dict= %s.' % (self.lineno, line)
-
- self.default(query)
-
+ query = '$out(%s) dict= %s.' % (self.lineno, q)
+ self.default(query, show_changed=False)
try:
- [(_, _, results)] = self.interp.chart['out/1'][self.lineno,:]
+ [(_, _, results)] = self.interp.chart['$out/1'][self.lineno,:]
except ValueError:
+ return []
+ return results
+
+ def do_vquery(self, q):
+ """
+ See query.
+ """
+ results = self._query(q)
+ if results is None:
+ return
+ if len(results) == 0:
print 'No results.'
return
-
for val, bindings in results:
- print ' ', val, 'when', bindings
+ print ' ', _repr(val), 'when', drepr(dict(bindings))
+ print
+
+ def do_query(self, q):
+ """
+ Query solution.
+
+ Consider the following example;
+
+ :- f(1) := 1.
+ :- f(2) := 4.
+
+ There a few versions of query:
+
+ - `vquery` shows variable bindings
+
+ :- vquery f(X)
+ 1 when {X=1}
+ 4 when {X=1}
+
+ - `query` shows variable bindings applied to query
+
+ :- query f(X)
+ 1 is f(1)
+ 4 is f(2)
+
+ """
+ results = self._query(q)
+ if results is None:
+ return
+ if len(results) == 0:
+ print 'No results.'
+ return
+ for term, result in sorted((subst(q, dict(result.variables)), result) for result in results):
+ print ' ', _repr(result.value), 'is', term
print
- def default(self, line):
+ def default(self, line, show_changed=True):
"""
Called on an input line when the command prefix is not recognized. In
that case we execute the line as Python code.
print '> new rule(s) were not added to program.'
print
else:
- self._changed(changed)
+ if show_changed:
+ self._changed(changed)
def _changed(self, changed):
if not changed:
print '============='
for x, v in sorted(changed.items()):
print '%s := %s' % (x, _repr(v))
+
+ def _changed_subscriptions(self, changed):
+
+ # TODO: this doesn't show changes - it redumps everything.
+
+ if not changed:
+ return
+ for x, _ in sorted(changed.items()):
+ if x.fn == '$subscribed/2':
+ [i, q] = x.args
+ if x.value:
+ print '%s: %s' % (i, q)
+ for result in x.value:
+ print ' ', _repr(result.value), 'when', drepr(dict(result.variables))
print
self.interp.dump_errors()
readline.write_history_file(self.hist)
def do_subscribe(self, line):
+ """
+ Establish a subscription to the results of a query.
+
+ For example,
+
+ :- subscribe f(X,X)
+ :- f(1,1) := 1. f(1,2) := 2. f(2,2) := 3.
+ Changes
+ =======
+ f(X,X):
+ 1 when {X=1}
+
+ To view all subscriptions:
+
+ :- subscriptions
+ f(X):
+ 1 when {X=1}
+ 2 when {X=2}
+
+ """
if line.endswith('.'):
print "Queries don't end with a dot."
return
# subscriptions are maintained via forward chaining.
- query = 'subscribed(%s, %s) dict= %s.' % (self.lineno, _repr(line), line)
+ query = '$subscribed(%s, %s) dict= %s.' % (self.lineno, _repr(line), line)
self.default(query)
def do_subscriptions(self, _):
- for (_, [_, q], answers) in self.interp.chart['subscribed/2'][:,:,:]:
- print
- print q
- for [value, vs] in answers:
- print ' %s when {%s}' \
- % (value, ', '.join('%s=%s' % (k, _repr(v)) for k,v in vs.items()))
+ "List subscriptions. See subscribe."
+ for (_, [_, q], results) in self.interp.chart['$subscribed/2'][:,:,:]:
+ if results:
+ print q
+ for result in results:
+ print ' ', _repr(result.value), 'when', drepr(dict(result.variables))
print
+ def do_help(self, line):
+ mod = line.split()
+ if len(mod) <= 1:
+ return super(REPL, self).do_help(line)
+ else:
+ if len(mod) == 2:
+ [cmd, sub] = mod
+ if cmd in ('load', 'post'):
+ try:
+ print getattr(globals()[cmd], sub).__doc__
+ except (KeyError, AttributeError):
+ print 'No help available for "%s %s"' % (cmd, sub)
+ return
+ else:
+ return
+ print 'Error: Did not understand help command.'
+
def do_load(self, line):
+ """
+ Execute load command.
+
+ Available loaders:
+
+ {loaders}
+
+ For more information about a particular loader type the following (in
+ this case we get help for the `tsv` loader):
+
+ :- help load tsv
+
+ Examples:
+
+ :- load data = tsv("examples/data/data.csv", delim=',')
+ :- sol
+ Solution
+ ========
+ data/3
+ ======
+ data(2,"cow","boy") := true
+
+ data/4
+ ======
+ data(0,"a","b","3.0") := true
+ data(1,"c","d","4.0") := true
+
+ """
try:
load.run(self.interp, line)
-# self.interp.dump_charts()
except:
show_traceback()
readline.write_history_file(self.hist)
+ do_load.__doc__ = do_load.__doc__.format(loaders=', '.join(x for x in dir(load) if not x.startswith('_')))
+
def do_post(self, line):
+ """
+ Execute post-processor.
+
+ Available post-processors:
+
+ {post}
+
+ For more information about a particular post processor (in this case
+ `save`)
+
+ :- help post save
+
+ """
try:
post.run(self.interp, line)
except:
show_traceback()
readline.write_history_file(self.hist)
+
+ do_post.__doc__ = do_post.__doc__.format(post=', '.join(x for x in dir(post) if not x.startswith('_')))
:- :- =============
a := 1
-
:- DynaCompilerError:
FATAL: Encountered error in input program:
Conflicting aggregators; rule /home/timv/.dyna/tmp/966093dc38b755a6f17b02774b5c656931163a3a.dyna:5:1-/home/timv/.dyna/tmp/966093dc38b755a6f17b02774b5c656931163a3a.dyna:5:3
:- :- :- 0: a += b * c.
:- =============
b := 2
-
:- 0: a += b * c.
1: b := 2.
:- =============
a := 6
c := 3
-
:- 0: a += b * c.
1: b := 2.
2: c := 3.
b += 1.
a += 1.
rules
-chart
+sol
retract_rule 0
retract_rule 1
-chart" |./dyna > $0.out
+sol" |./dyna > $0.out
diff $0.expect $0.out && echo pass
:- :- =============
a := 1
-
:- =============
b := 1
-
:- =============
a := 2
-
:- 0: a += 1.
1: b += 1.
2: a += 1.
:-
-Charts
-============
-a/0
-=================
-a := 2
-
-b/0
-=================
-b := 1
+Solution
+========
+a := 2
+b := 1
:- :- :-
-Charts
-============
-a/0
-=================
-a := 1
-
-b/0
-=================
-
+Solution
+========
+a := 1
:- exit