MISC
====
- - TODO: create an Interpreter object to hold what is now global state.
-
- - FIXME: timv: I think that set= is wrong .. needs to keep counts like bag=
+ - TODO: create an Interpreter object to hold state.
- TODO: sorted order of Chart seems to have changed. Check that this changed order
makes sense
- - "initializers" aren't just initializers, they are the fully-naive bottom-up
- inference rules.
-
- - XXX: we should probably fuse update handlers instead of dispatching to each
- one independently.
+ timv: look at the diff--what were we doing before?
- TODO: deleting a rule: (1) remove update handlers (2) run initializers in
delete mode (3) remove initializers.
- TODO: hooks from introspection, eval, and prioritization.
+ whats the default prioritization?
+
- TODO: Term's should only be aggregated with ``=`` or ``:=``. We should
disallow ``a += &b.``
+ equals (=) aggregation (only one value allowed, multiplicity >0 on only one value)
+
+ a = b for c
+ a = d for e
+
+ c and e might be mutually exclusive in all FP, but not during computation
+ -- this is the same as the error problem (e.g. division problem)
+
- TODO: doc tests for Dyna code!
+ - TODO: repl needs to pass parser a rule index pragma to start from.
+
+ - TODO: build hypergraph from unrolled circuit. This requires a little bit of
+ thinking because we don't yet know what things in the chart have been
+ touched.
+
+
+Use ruleix to make := work at the REPL
+
+:- ruleix 100
+
+
+
+NOTES
+=====
+ - "initializers" aren't just initializers, they are the fully-naive bottom-up
+ inference rules.
PARSER
======
- - Singled quoted strings:
+ - TODO: Singled quoted strings:
x += f('result = 5').
- - Nested expressions:
+ - TODO: Nested expressions:
out(0) dict= _VALUE is (rewrite(X,Y) + rewrite(X,Y,Z)), _VALUE.
from collections import defaultdict, namedtuple
from argparse import ArgumentParser
-from utils import ip, red, green, blue, magenta, yellow, dynahome, notimplemented
+from utils import ip, red, green, blue, magenta, yellow, dynahome, \
+ notimplemented, prioritydict
from defn import aggregator
return None
+error_suppression = False
trace = None
-_delete = False
-agenda = set()
+agenda = prioritydict()
agg_decl = aggregator_declaration()
chart = chart_indirect()
if isinstance(x, slice):
continue
if candidates is None:
+ # initial candidates determined by first non-bound column (if any)
candidates = ix[x].copy()
else:
candidates &= ix[x]
if not len(candidates):
break
+ # all arguments must be bound.
if candidates is None:
candidates = self.intern.values()
+ # handle the value column separately because we don't index it yet.
if isinstance(val, slice):
for term in candidates:
if term.value is not None:
initializer.handlers = []
-def update_dispatcher(item, val):
+def update_dispatcher(item, val, delete):
"""
Passes update to relevant handlers.
"""
return
for handler in register.handlers[item.fn]:
try:
- handler(item, val)
+ handler(item, val, delete=delete)
except (TypeError, ZeroDivisionError) as e:
- #print >> trace,
- print '%s on update %s = %s' % (e, item, val)
+ if error_suppression:
+ #print >> trace,
+ print '%s on update %s = %s' % (e, item, val)
+ else:
+ raise e
def peel(fn, item):
return item.args
-def emit(item, val, ruleix, variables):
+def emit(item, val, ruleix, variables, delete):
- print >> trace, (red % 'delete' if _delete else green % 'update'), \
+ print >> trace, (red % 'delete' if delete else green % 'update'), \
'%s (val %s; curr: %s)' % (item, val, item.value)
assert not isinstance(val, tuple) or isinstance(val, Term)
- if _delete:
+ if delete:
item.aggregator.dec(val, ruleix, variables)
else:
item.aggregator.inc(val, ruleix, variables)
- agenda.add(item)
-
-
-def delete(item, val):
- # XXX: very ugly handling of deletion by global variable; should probably
- # target only handler at a time, because this will get called more times
- # than it should.
- global _delete
- _delete = True
- update_dispatcher(item, val)
- _delete = False
+ agenda[item] = 0
changed = {}
changed.clear()
while agenda:
- item = agenda.pop()
+ item = agenda.pop_smallest()
print >> trace
print >> trace, magenta % 'pop ', item,
was = item.value
print >> trace, '(was: %s,' % (was,),
- # TODO: in the case of set and bag the `now` value is the same as `was`
- # because the change happens in place. Thus, sadly, `was == now` and the
- # change will now propagate.
-
now = item.aggregator.fold()
print >> trace, 'now:', str(now) + ')'
continue
if was is not None:
- delete(item, was)
+ update_dispatcher(item, was, delete=True)
item.value = now
if now is not None:
- update_dispatcher(item, now)
+ update_dispatcher(item, now, delete=False)
changed[item] = now
for init in initializer.handlers: # assumes we have cleared
try:
- init()
+ init(delete=False)
except (TypeError, ZeroDivisionError) as e:
- #print >> trace,
- print e, 'in initializer.'
+ if error_suppression:
+ #print >> trace,
+ print e, 'in initializer.'
+ else:
+ raise e
go()
def notimplemented(*_,**__):
raise NotImplementedError
+
+from heapq import heapify, heappush, heappop
+
+class prioritydict(dict):
+ """Dictionary that can be used as a priority queue.
+
+ Keys of the dictionary are items to be put into the queue, and values
+ are their respective priorities. All dictionary methods work as expected.
+ The advantage over a standard heapq-based priority queue is
+ that priorities of items can be efficiently updated (amortized O(1))
+ using code as 'thedict[item] = new_priority.'
+
+ The 'smallest' method can be used to return the object with lowest
+ priority, and 'pop_smallest' also removes it.
+
+ The 'sorted_iter' method provides a destructive sorted iterator.
+
+ This implemented is based on:
+
+ Matteo Dell'Amico's implementation
+ http://code.activestate.com/recipes/522995-priority-dict-a-priority-queue-with-updatable-prio/
+
+ which is based on David Eppstein's implementation
+ http://code.activestate.com/recipes/117228/
+
+ """
+
+ def __init__(self, *args, **kwargs):
+ super(prioritydict, self).__init__(*args, **kwargs)
+ self._rebuild_heap()
+
+ def _rebuild_heap(self):
+ self._heap = [(v, k) for k, v in self.iteritems()]
+ heapify(self._heap)
+
+ def smallest(self):
+ """
+ Return the item with the lowest priority.
+
+ Raises IndexError if the object is empty.
+ """
+
+ heap = self._heap
+ v, k = heap[0]
+ while k not in self or self[k] != v:
+ heappop(heap)
+ v, k = heap[0]
+ return k
+
+ def pop_smallest(self):
+ """
+ Return the item with the lowest priority and remove it.
+
+ Raises IndexError if the object is empty.
+ """
+
+ heap = self._heap
+ v, k = heappop(heap)
+ while k not in self or self[k] != v:
+ v, k = heappop(heap)
+ del self[k]
+ return k
+
+ def __setitem__(self, key, val):
+ # We are not going to remove the previous value from the heap,
+ # since this would have a cost O(n).
+
+ super(prioritydict, self).__setitem__(key, val)
+
+ if len(self._heap) < 2 * len(self):
+ heappush(self._heap, (val, key))
+ else:
+ # When the heap grows larger than 2 * len(self), we rebuild it
+ # from scratch to avoid wasting too much memory.
+ self._rebuild_heap()
+
+ setdefault = None
+ update = None