]> hydra-www.ietfng.org Git - dyna2/commitdiff
using an actual priority queue, no more ``_delete`` global variable.
authortimv <tim.f.vieira@gmail.com>
Mon, 3 Jun 2013 18:58:00 +0000 (14:58 -0400)
committertimv <tim.f.vieira@gmail.com>
Mon, 3 Jun 2013 18:58:00 +0000 (14:58 -0400)
global error suppression flag

src/Dyna/Backend/Python/Backend.hs
src/Dyna/Backend/Python/interpreter.py
src/Dyna/Backend/Python/utils.py

index 279d6b1364a195a486f2182438f889efb7e270e5..ba5619ce888ac3b5b60106f78a69d823c711a390 100644 (file)
@@ -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
 
index 1d552e52a818013afbc15f45b7f5d726f9646b37..06d4a1fc345ba64ea21b361584431c4895531d37 100644 (file)
@@ -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()
 
index c6313b52c5f0cf2bd9366c7d27817a2b9db9da50..396116f87688a69358eb0227d7f874cd3e49dd37 100644 (file)
@@ -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