From: timv Date: Mon, 3 Jun 2013 18:58:00 +0000 (-0400) Subject: using an actual priority queue, no more ``_delete`` global variable. X-Git-Url: https://hydra-www.ietfng.org/gitweb/?a=commitdiff_plain;h=5349e0ec3b0b9195bdc9d00b806f4a40d6bc515d;p=dyna2 using an actual priority queue, no more ``_delete`` global variable. global error suppression flag --- diff --git a/src/Dyna/Backend/Python/Backend.hs b/src/Dyna/Backend/Python/Backend.hs index 279d6b1..ba5619c 100644 --- a/src/Dyna/Backend/Python/Backend.hs +++ b/src/Dyna/Backend/Python/Backend.hs @@ -217,11 +217,12 @@ pdope_ (OPEmit h r i vs) = , pretty r , pretty i , varmap + , "delete=delete" ] where -- A python map of variable name to value varmap = encloseSep lbrace rbrace comma - $ map (\v -> let v' = pretty v in dquotes v' <+> colon <+> v') vs + $ map (\v -> let v' = pretty v in dquotes v' <> colon <+> v') vs -- | Render a dopamine sequence's checks and loops above a (indended) core. pdope :: Actions PyDopeBS -> Doc e @@ -250,7 +251,7 @@ printInitializer :: Handle -> Rule -> Actions PyDopeBS -> IO () printInitializer fh rule@(Rule _ h _ r _ _ ucruxes _) dope = do displayIO fh $ renderPretty 1.0 100 $ "@initializer" <> parens (uncurry pfa $ MA.fromJust $ findHeadFA h ucruxes) - `above` "def" <+> char '_' <> tupled [] <+> colon + `above` "def" <+> char '_' <> tupled ["delete"] <+> colon `above` pdope dope <> line @@ -259,7 +260,7 @@ printUpdate :: Handle -> Rule -> Maybe DFunctAr -> (DVar, DVar) -> Actions PyDop printUpdate fh rule@(Rule _ h _ r _ _ _ _) (Just (f,a)) (hv,v) dope = do displayIO fh $ renderPretty 1.0 100 $ "@register" <> parens (pfa f a) - `above` "def" <+> char '_' <> tupled (map pretty [hv,v]) <+> colon + `above` "def" <+> char '_' <> tupled (map pretty [hv,v,"delete"]) <+> colon `above` pdope dope <> line diff --git a/src/Dyna/Backend/Python/interpreter.py b/src/Dyna/Backend/Python/interpreter.py index 1d552e5..06d4a1f 100644 --- a/src/Dyna/Backend/Python/interpreter.py +++ b/src/Dyna/Backend/Python/interpreter.py @@ -5,40 +5,61 @@ 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. @@ -136,7 +157,8 @@ import os, sys 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 @@ -168,9 +190,9 @@ class aggregator_declaration(object): return None +error_suppression = False trace = None -_delete = False -agenda = set() +agenda = prioritydict() agg_decl = aggregator_declaration() chart = chart_indirect() @@ -246,15 +268,18 @@ class Chart(object): 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: @@ -345,7 +370,7 @@ def initializer(_): initializer.handlers = [] -def update_dispatcher(item, val): +def update_dispatcher(item, val, delete): """ Passes update to relevant handlers. """ @@ -353,10 +378,13 @@ def update_dispatcher(item, val): 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): @@ -377,29 +405,19 @@ 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 = {} @@ -410,7 +428,7 @@ def _go(): changed.clear() while agenda: - item = agenda.pop() + item = agenda.pop_smallest() print >> trace print >> trace, magenta % 'pop ', item, @@ -418,10 +436,6 @@ def _go(): 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) + ')' @@ -430,12 +444,12 @@ def _go(): 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 @@ -500,10 +514,13 @@ def do(filename): 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() diff --git a/src/Dyna/Backend/Python/utils.py b/src/Dyna/Backend/Python/utils.py index c6313b5..396116f 100644 --- a/src/Dyna/Backend/Python/utils.py +++ b/src/Dyna/Backend/Python/utils.py @@ -65,3 +65,81 @@ def read_anf(e): 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