]> hydra-www.ietfng.org Git - dyna2/commitdiff
New automata library
authorNathaniel Wesley Filardo <nwf@cs.jhu.edu>
Tue, 13 Aug 2013 23:43:58 +0000 (19:43 -0400)
committerNathaniel Wesley Filardo <nwf@cs.jhu.edu>
Tue, 13 Aug 2013 23:43:58 +0000 (19:43 -0400)
Factor out all the automata logic from Analysis.Mode.Execution.NamedInst to
its own library (Dyna.Analysis.Automata) and port everything over.  I won't
claim it's perfect, but I think it's a good start.

Analysis.Automata.Class is probably the right place to start reading.

13 files changed:
src/Dyna/Analysis/Automata/Class.hs [new file with mode: 0644]
src/Dyna/Analysis/Automata/NamedAut.hs [new file with mode: 0644]
src/Dyna/Analysis/Automata/Utilities.hs [new file with mode: 0644]
src/Dyna/Analysis/Mode/Execution/Context.hs
src/Dyna/Analysis/Mode/Execution/ContextNoAlias.hs
src/Dyna/Analysis/Mode/Execution/Functions.hs
src/Dyna/Analysis/Mode/Execution/FunctionsNoAlias.hs
src/Dyna/Analysis/Mode/Execution/NamedInst.hs
src/Dyna/Analysis/Mode/Inst.hs
src/Dyna/Analysis/Mode/Selftest/NamedInst.hs
src/Dyna/Analysis/Mode/Selftest/Term.hs
src/Dyna/Backend/Python/Backend.hs
src/Dyna/XXX/MonadUtils.hs

diff --git a/src/Dyna/Analysis/Automata/Class.hs b/src/Dyna/Analysis/Automata/Class.hs
new file mode 100644 (file)
index 0000000..fffb53d
--- /dev/null
@@ -0,0 +1,165 @@
+---------------------------------------------------------------------------
+-- | Self-contained, recursive automata, parametric in ply functor.
+--
+-- Note that particular automata implementations (instances) may have an API
+-- that goes (well) beyond what's available here.  In particular, it is
+-- expected that non-trivial construction is beyond the scope of this common
+-- API.
+
+-- Header material                                                      {{{
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# OPTIONS_GHC -Wall #-}
+
+module Dyna.Analysis.Automata.Class where
+
+import qualified Data.Traversable                  as T
+
+------------------------------------------------------------------------}}}
+-- Utility type definitions                                             {{{
+
+-- | An alias for universal quantification forcing a non-recursive ply of @f@:
+-- since there is no defined data of fully polymorphic type, any recursive
+-- positions in @f@ must not contain data.
+type NonRec f = forall x . f x
+
+------------------------------------------------------------------------}}}
+-- Basic class definition                                               {{{
+
+class Automata (a :: (* -> *) -> *) where
+  -- Construction
+
+  -- | An inverse (up to isomorphism) to 'autExpose'.
+  --
+  -- Note that this may not be efficient, so it should be used only as a
+  -- last resort.
+  autHide    :: (T.Traversable f) => f (a f) -> a f
+
+  -- | This is most frequently used as a base case in a lopsided
+  -- inductive step, in which one side is known to be acyclic, but the other
+  -- may be cyclic.  When we have reached the leaves of the former,
+  -- 'autShallow' will yield a trivial automata which may then be passed to
+  -- a binary automata function (e.g. 'autCmp') which will deal with cycles.
+  --
+  -- Parametricity ensures that this function has no access to any but the
+  -- top ply of its argument.
+  autShallow :: (T.Traversable f) => f x -> Maybe (a f)
+
+  -- Destruction
+
+  -- | Reveal the topmost ply of this automata.
+  --
+  -- Note that recursive use of this function may well diverge!
+  autExpose :: (Functor f) => a f -> f (a f)
+
+  -- Unary traversals
+
+  -- | Per-state rewriting function.  Callback will be called at most once
+  -- for each state of the automata, and at least once for all reachable
+  -- states, but there may be equivalent states within as well (but see
+  -- 'autMinimize').  The callback has visibility into the states reachable
+  -- from the current position within the automata, but no cycle-breaking is
+  -- performed by the machinery, so careless use of this ability may
+  -- diverge; an 'Ord' instance is asserted for the recursion sites to help
+  -- mitigate this situation.
+  autAtEachState :: forall f .
+                    (forall x . (Ord x) => (x -> f x) -> f x -> f x)
+                 -> a f -> a f
+
+  -- | (Indexed) algebraic reduction of an automaton.  Given a
+  -- cycle-breaking result (as a function of node identity)
+  -- and an algebraic interpretation of a single
+  -- state of the automaton (indexed, again, by node identity),
+  -- produce a result.
+  --
+  -- Note that cycles are only evaluated once: while a given spine is
+  -- active, all observations of that spine will yield the cycle-breaking
+  -- value; once a node is no longer on the active spine, observers will
+  -- observe its result.  Results of this function are only well-defined
+  -- if the callbacks are invariant to node visit order subject to this
+  -- observation constraint.
+  --
+  -- Note that one should not expect to be able to fully reconstruct the
+  -- automata given only the observations made by this function, as the
+  -- states may have meaning internal to automata (e.g. interpreted subject
+  -- to equality constraints or just as regular structure).
+  autReduceIx :: forall f r x .
+                 (T.Traversable f, Enum x, Ord x)
+              => (x -> r)
+              -> (x -> f r -> r)
+              -> a f -> r
+
+  -- | Non-indexed reduction.  See 'autReduceIx'.
+  --
+  -- This could be written in terms of 'autReduceIx', but this may be
+  -- more efficient by not having to map state names.
+  autReduce :: forall f r .
+               (T.Traversable f)
+            => r
+            -> (f r -> r)
+            -> a f -> r
+
+  -- Binary traversals
+  --
+  -- XXX Possibly these want to be split out to an AutBin class, enabling us
+  -- to specify more than just the diagonals in the space of automata?
+
+  -- | Binary reducer; see 'autReduce' for overview.
+  --
+  -- The HOF here is given three callbacks, for both "in-phase" and
+  -- "out-of-phase" work; the former allows simultaneous descent into two
+  -- states, while the others allow the merge to remain at a leaf state in
+  -- one automata while descending the other.
+  autBiReduce :: forall f r .
+                 (Ord (f ()))
+              => r
+              -> (forall x y m .
+                     (Monad m)
+                  => (x        -> NonRec f -> m r)
+                  -> (NonRec f -> y        -> m r)
+                  -> (x        -> y        -> m r)
+                  ->  f x      -> f y      -> m r)
+              -> a f -> a f -> r
+
+  -- | Total synchronous binary merge; @c@ records the type of top-down
+  -- context information needed for what is otherwise a bottom-up operation.
+  -- Since multiple paths may reach pairs of states, @c@ is used internally
+  -- as part of the index -- that is, a given pair of states may be
+  -- given to the callback repeatedly at differing @c@ values.
+  autMerge :: forall c f .
+              (Ord c, Ord (f ()))
+           => (forall x y . f x -> f y -> c)
+           -> (forall x y z m .
+                  (Monad m)
+               => (c -> x        -> NonRec f -> m z)
+               -> (c -> NonRec f -> y        -> m z)
+               -> (c -> x        -> y        -> m z)
+               ->  c -> f x      -> f y      -> m (f z))
+           -> a f -> a f -> a f
+
+
+  -- | Partial synchronous binary merge.
+  --
+  -- Note that the callbacks are total (monadic) functions: failure is
+  -- handled internally to @m@ and may short-circuit.
+  autPMerge :: forall c e f .
+               (Ord c, Ord (f ()))
+            => (forall x y . f x -> f y -> c)
+            -> (forall x y z m .
+                   (Monad m)
+                => (c -> x        -> NonRec f -> m z)
+                -> (c -> NonRec f -> y        -> m z)
+                -> (c -> x        -> y        -> m z)
+                ->  c -> f x      -> f y      -> m (Either e (f z)))
+            -> a f -> a f -> Either e (a f)
+
+------------------------------------------------------------------------}}}
+-- Minimization class definition                                        {{{
+
+class AutMinimize a where
+
+  -- | Automata minimization.
+  autMinimize :: (T.Traversable f, Ord (f Int)) => a f -> a f
+
+------------------------------------------------------------------------}}}
diff --git a/src/Dyna/Analysis/Automata/NamedAut.hs b/src/Dyna/Analysis/Automata/NamedAut.hs
new file mode 100644 (file)
index 0000000..f7147e1
--- /dev/null
@@ -0,0 +1,435 @@
+---------------------------------------------------------------------------
+-- | An implementation of our automata using existentially-quantified maps
+
+-- Header material                                                      {{{
+{-# LANGUAGE ConstraintKinds #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE InstanceSigs #-}
+{-# LANGUAGE KindSignatures #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE ViewPatterns #-}
+{-# OPTIONS_GHC -Wall #-}
+
+module Dyna.Analysis.Automata.NamedAut(NA(NA), naUnfold, naFromMap) where
+
+import           Control.Applicative
+import           Control.Lens
+import           Control.Monad.Identity
+import           Control.Monad.Trans.Either
+import           Control.Monad.State
+import qualified Data.Foldable                     as F
+import qualified Data.Map                          as M
+import qualified Data.Maybe                        as MA
+import qualified Data.Set                          as S
+import qualified Data.Traversable                  as T
+import           Dyna.Analysis.Automata.Class
+import           Dyna.XXX.DataUtils (mapInOrCons)
+import           Dyna.XXX.MonadUtils (incState, tryMapCache, trySetCache)
+
+------------------------------------------------------------------------}}}
+-- Definition                                                           {{{
+
+data NA f = forall a . (Ord a) => NA a (M.Map a (f a))
+
+------------------------------------------------------------------------}}}
+-- Internal utilities                                                   {{{
+
+naImport :: (T.Traversable f, Ord k, Functor m, Monad m)
+         => m a
+         -> (a -> f a -> m ())
+         -> (k -> f k)
+         -> k
+         -> a
+         -> m ()
+naImport nxt ins look r ir = do
+  _ <- flip runStateT M.empty $ go r ir
+  return ()
+ where
+  go k a = T.traverse rec (look k) >>= lift . ins a
+  rec = tryMapCache id (\_ -> lift nxt) go
+
+naPrune :: forall f . (F.Foldable f) => NA f -> NA f
+naPrune (NA a0 m0) = (NA a0 m')
+ where
+  m' = execState (go a0) M.empty
+  go a = tryMapCache id (\_ -> return af) (\_ _ -> F.mapM_ go af) a
+   where af = MA.fromJust $ m0 ^. at a
+
+naRelabel_ :: (Ord a, Enum o)
+           => M.Map a x
+           -> M.Map a o
+naRelabel_ m = flip evalState (toEnum 0) $ T.traverse (\_ -> incState id) m
+
+-- | Convert from whatever internal representation we're using for states
+-- to Int.
+naExposeAll :: (Functor f, Enum o, Ord o) => NA f -> (o, M.Map o (f o))
+naExposeAll (NA a0 m0) = let labels = naRelabel_ m0
+                             relabel a = MA.fromJust $ labels ^. at a
+                         in  ( relabel a0
+                             , fmap (fmap relabel)
+                               $ M.mapKeysWith (error "NA relabel collision")
+                                               relabel m0)
+
+
+-- | Downcast away a forall.
+dc :: NonRec f -> f ()
+dc i = i
+
+------------------------------------------------------------------------}}}
+-- Unfolder                                                             {{{
+
+-- | Build an automata over the functor @f@ by unfolding.  @r@ carries
+-- top-down information flow, while the user-specified 'Monad' @m@ may carry
+-- sideways state (that is, information dependent upon the unfolding order).
+-- @ix@ provides the internal label for the state being unfolded so that it
+-- may be referenced later (/e.g./ by first being stored in @m@ or @r@).
+--
+-- As a convenience, the unfolder is allowed to supply an entire (isolated)
+-- automata in liu of recursively descending.
+naUnfold :: forall f ix m r .
+            (Enum ix, Ord ix, Functor m, Monad m, Traversable f)
+         => (forall t . (MonadTrans t, Monad (t m))
+              => r -> ix -> t m (Either (NA f) (f (Either ix r))))
+         -> r -> m (NA f)
+naUnfold uf = liftM (\(r,(_,m)) -> NA r m)
+            . flip runStateT (toEnum 0,M.empty) . visit
+ where
+  visit r = do
+   i <- nxt
+   f <- uf r i
+   case f of
+     Right f' -> do
+                 f'' <- T.mapM (either return visit) f'
+                 ins i f''
+     Left (NA r' m') -> naImport nxt ins (MA.fromJust . (m' ^.) . at) r' i
+   return i
+
+  nxt = incState _1
+  ins k v = _2 . at k .= Just v
+{-# INLINABLE naUnfold #-}
+
+{-
+-- | Build an automata over the functor @f@ by unfolding.  @t@ carries
+-- top-down information flow while @s@ carries sideways state (that is,
+-- information dependent upon the unfolding order).  @ix@ provides the
+-- internal label for the state being unfolded.
+--
+-- One might use @s@ to carry a (partial) map from @t@ to @ix@ values,
+-- either for tying cyclic knots or to avoid re-unfolding structure.  It may
+-- also be safe to ignore (just pass '()').
+naUnfold :: forall f ix m s t .
+            (Enum ix, Ord ix, Traversable f) =>
+            (t -> ix -> s -> (f (Either ix t), s))  -- ^ Unfolder step
+            -> t                                    -- ^ Initial root
+            -> s                                    -- ^ Initial flow state
+            -> NA f
+naUnfold uf t0 s0 = (\(r,(_,m,_)) -> NA r m)
+                  $ flip runState (toEnum 0, M.empty, s0)
+                  $ visit t0
+ where
+  visit t = do
+    i <- incState _1
+    (f,s') <- uses _3 (uf t i)
+    _3 .= s'
+    f' <- T.mapM (either return visit) f
+    _2 . at i .= Just f'
+    return i
+-}
+
+naFromMap :: forall f oix .
+             (T.Traversable f, Ord oix)
+          => M.Map oix (f oix) -> oix -> NA f
+naFromMap m0 a0 = flip evalState (M.empty :: M.Map oix Int) (naUnfold u a0)
+ where
+  u :: forall t m . (MonadTrans t, Monad (t m), MonadState (M.Map oix Int) m)
+    => oix -> Int -> t m (Either (NA f) (f (Either Int oix)))
+  u oix iix = do
+    lift (at oix .= Just iix)
+    liftM Right $ T.mapM relabel $ MA.fromJust (m0 ^. at oix)
+
+  relabel oix = lift (use (at oix)) >>= return . maybe (Right oix) (Left)
+{-# INLINABLE naFromMap #-}
+
+{-
+naFromAut :: forall a f x .
+             (Automata a, T.Traversable f, Enum x, Ord x)
+          => a f -> (x, M.Map x (f x))
+naFromAut = autReduceIx cyc rec
+ where
+  cyc x = (x,M.empty)
+  rec x fr = (x, M.insert x frx $ M.unions frms)
+   where
+   frx = fmap fst fr
+   frms = F.toList $ fmap snd fr
+{-# INLINABLE naFromAut #-}
+-}
+
+------------------------------------------------------------------------}}}
+-- Internal data types                                                  {{{
+
+data NBinState f c a b = NBS { _nbs_next  :: Int
+                             , _nbs_ctx   :: M.Map Int (f Int)
+                             , _nbs_cache_symm :: M.Map (c,a   ,b   ) Int
+                             , _nbs_cache_lsml :: M.Map (c,a   ,f ()) Int
+                             , _nbs_cache_lsmr :: M.Map (c,f (),b   ) Int
+                             }
+$(makeLenses ''NBinState)
+type NBSC m f c a b = (Monad m, MonadState (NBinState f c a b) m)
+
+iNBS :: forall (f :: * -> *) c a b. NBinState f c a b
+iNBS = NBS 0 M.empty M.empty M.empty M.empty
+
+data MinState a c = MS
+  { _ms_classes :: M.Map c [a]
+  , _ms_clinv   :: M.Map a c
+  }
+ deriving (Show)
+$(makeLenses ''MinState)
+
+data MinSplitState f a c = MSS
+  { _mss_newcl  :: M.Map (f c) [a] }
+ deriving (Show)
+$(makeLenses ''MinSplitState)
+
+------------------------------------------------------------------------}}}
+-- Automata instance                                                    {{{
+
+instance Automata NA where
+  autHide i0 = flip evalState (0::Int, M.empty) $ do
+    i0' <- T.traverse go i0
+    ra  <- nxt
+    ins ra i0'
+    rm  <- use _2
+    return (NA ra rm)
+   where
+    nxt = incState _1
+    ins k v = _2 . at k .= Just v
+
+    go (NA i' m') = do
+      i <- nxt
+      naImport nxt ins (MA.fromJust . (m' ^.) . at) i' i
+      return i
+
+  autShallow f = do
+    f' <- T.mapM (const mzero) f
+    return (NA () (M.fromList [((),f')]))
+  {-# INLINABLE autShallow #-}
+
+  autExpose (NA a0 m) = fmap (\a -> NA a m) $ MA.fromJust $ m ^. at a0
+  {-# INLINABLE autExpose #-}
+
+  autAtEachState q (NA a0 m0) = NA a0 (M.map q' m0)
+   where
+    q' = q (MA.fromJust . (m0 ^.) . at)
+  {-# INLINABLE autAtEachState #-}
+
+  autReduce cyc red (NA a0 m0) = evalState (q a0) M.empty
+   where
+    q a = do
+      _ <- tryMapCache id (\_ -> return cyc) miss a
+      liftM MA.fromJust $ use (at a)
+
+    miss a _ = do
+     fr <- T.mapM q (MA.fromJust $ m0 ^. at a)
+     let r = red fr
+     at a .= Just r
+  {-# INLINABLE autReduce #-}
+
+  autReduceIx cyc red (naExposeAll -> (a0,m0)) = evalState (q a0) M.empty
+   where
+    q a = do
+      _ <- tryMapCache id (return . cyc) miss a
+      liftM MA.fromJust $ use (at a)
+
+    miss a _ = do
+     fr <- T.mapM q (MA.fromJust $ m0 ^. at a)
+     let r = red a fr
+     at a .= Just r
+  {-# INLINABLE autReduceIx #-}
+
+  autBiReduce c q (NA la0 lm) (NA ra0 rm) =
+    evalState (qip la0 ra0) (S.empty, S.empty, S.empty)
+   where
+    qip l r = flip (trySetCache _1 c) (l,r) $ \_ -> do
+               let lf = MA.fromJust $ lm ^. at l
+               let rf = MA.fromJust $ rm ^. at r
+               q qopl qopr qip lf rf
+
+    qopl l r = flip (trySetCache _2 c) (l, dc r) $ \_ -> do
+                let lf = MA.fromJust $ lm ^. at l
+                q qopl qopr qip lf r
+
+    qopr l r = flip (trySetCache _3 c) (dc l, r) $ \_ -> do
+                let rf = MA.fromJust $ rm ^. at r
+                q qopl qopr qip l rf
+  {-# INLINABLE autBiReduce #-}
+
+  -- While unusual, we need this instance type declaration to introduce
+  -- scoped type variables for us to hold on to down below.  Oy!
+  autMerge :: forall c f .
+              (Ord c, Ord (f ()))
+           => (forall x y . f x -> f y -> c)
+           -> (forall x y z m .
+                  (Monad m)
+               => (c -> x        -> NonRec f -> m z)
+               -> (c -> NonRec f -> y        -> m z)
+               -> (c -> x        -> y        -> m z)
+               ->  c -> f x      -> f y      -> m (f z))
+           -> NA f -> NA f -> NA f
+  autMerge ci f (NA (la0 :: la) lm) (NA (ra0 :: ra) rm) = evalState tlq iNBS
+   where
+    f' :: NBSC m f c la ra => c -> f la -> f ra -> m (f Int)
+    f' = f lsml lsmr merge
+
+    tlq = do
+      let lf :: f la = MA.fromJust $ lm ^. at la0
+      let rf :: f ra = MA.fromJust $ rm ^. at ra0
+      let ic = ci lf rf
+      ma <- merge ic la0 ra0
+      mm <- use nbs_ctx
+      return (NA ma mm)
+
+    -- These two are here so that the skolems remain local to this
+    -- defintion.  It's a bit of a shame that the type system is
+    -- not willing to let these exist at higher scope.
+    nxt :: NBSC m f c la ra => m Int
+    nxt = incState nbs_next
+    ins k v = nbs_ctx . at k .= Just v
+
+    merge :: NBSC m f c la ra => c -> la -> ra -> m Int
+    merge c l r =
+      flip (tryMapCache nbs_cache_symm (\_ -> nxt)) (c,l,r) $ \_ a -> do
+        let lf = MA.fromJust $ lm ^. at l
+        let rf = MA.fromJust $ rm ^. at r
+        v <- f' c lf rf
+        ins a v
+
+    lsml :: NBSC m f c la ra => c -> la -> NonRec f -> m Int
+    lsml c l r =
+      flip (tryMapCache nbs_cache_lsml (\_ -> nxt)) (c,l,dc r) $ \_ a -> do
+        let lf = MA.fromJust $ lm ^. at l
+        v <- f' c lf r
+        ins a v
+
+    lsmr :: NBSC m f c la ra => c -> NonRec f -> ra -> m Int
+    lsmr c l r =
+      flip (tryMapCache nbs_cache_lsmr (\_ -> nxt)) (c,dc l,r) $ \_ a -> do
+        let rf = MA.fromJust $ rm ^. at r
+        v <- f' c l rf
+        ins a v
+  {-# INLINABLE autMerge #-}
+
+  autPMerge :: forall c e f .
+               (Ord c, Ord (f ()))
+            => (forall x y . f x -> f y -> c)
+            -> (forall x y z m .
+                   (Monad m)
+                => (c -> x        -> NonRec f -> m z)
+                -> (c -> NonRec f -> y        -> m z)
+                -> (c -> x        -> y        -> m z)
+                ->  c -> f x      -> f y      -> m (Either e (f z)))
+            -> NA f -> NA f -> Either e (NA f)
+  autPMerge ci f (NA (la0 :: la) lm) (NA (ra0 :: ra) rm) =
+    evalState (runEitherT tlq) iNBS
+   where
+    f' :: NBSC m f c la ra
+       => c -> f la -> f ra -> EitherT e m (Either e (f Int))
+    f' = f lsml lsmr merge
+
+    tlq = do
+      let lf :: f la = MA.fromJust $ lm ^. at la0
+      let rf :: f ra = MA.fromJust $ rm ^. at ra0
+      let ic = ci lf rf
+      ma <- merge ic la0 ra0
+      mm <- use nbs_ctx
+      return (NA ma mm)
+
+    -- These two are here so that the skolems remain local to this
+    -- defintion.  It's a bit of a shame that the type system is
+    -- not willing to let these exist at higher scope.
+    nxt :: NBSC m f c la ra => m Int
+    nxt = incState nbs_next
+    ins k v = nbs_ctx . at k .= Just v
+
+    merge :: NBSC m f c la ra => c -> la -> ra -> EitherT e m Int
+    merge c l r =
+      flip (tryMapCache nbs_cache_symm (\_ -> nxt)) (c,l,r) $ \_ a -> do
+        let lf = MA.fromJust $ lm ^. at l
+        let rf = MA.fromJust $ rm ^. at r
+        m <- f' c lf rf
+        v <- hoistEither m
+        ins a v
+
+    lsml :: NBSC m f c la ra => c -> la -> NonRec f -> EitherT e m Int
+    lsml c l r =
+      flip (tryMapCache nbs_cache_lsml (\_ -> nxt)) (c,l,dc r) $ \_ a -> do
+        let lf = MA.fromJust $ lm ^. at l
+        m <- f' c lf r
+        v <- hoistEither m
+        ins a v
+
+    lsmr :: NBSC m f c la ra => c -> NonRec f -> ra -> EitherT e m Int
+    lsmr c l r =
+      flip (tryMapCache nbs_cache_lsmr (\_ -> nxt)) (c,dc l,r) $ \_ a -> do
+        let rf = MA.fromJust $ rm ^. at r
+        m <- f' c l rf
+        v <- hoistEither m
+        ins a v
+  {-# INLINABLE autPMerge #-}
+
+------------------------------------------------------------------------}}}
+-- AutMinimize instance                                                 {{{
+
+instance AutMinimize NA where
+
+  autMinimize (naPrune -> NA a0 m0) =
+    let ms = classifyUntil m0 (MS M.empty M.empty)
+                              (mkClasses [M.keys m0])
+
+        msci = ms ^. ms_clinv
+
+        defn = \(a:_) -> fmap (\a' -> MA.fromJust $ msci ^. at a')
+                            $ MA.fromJust $ m0 ^. at a
+    in NA ((MA.fromJust $ msci ^. at a0) :: Int)
+          (fmap defn $ ms ^. ms_classes)
+
+   where
+
+    -- Attempt to partition a class in the equivalence relationship.  Evaluate
+    -- each contained state to see if they have become distinguished.
+    splitClasses :: forall f a c . (Ord c, Ord a, Traversable f, Ord (f c))
+                 => M.Map a (f a)
+                 -> MinState a c
+                 -> [[a]]
+    splitClasses im ms =
+        concat $ flip map (M.keys $ ms ^. ms_classes)
+      $ \c -> (M.elems . _mss_newcl) $ flip execState (MSS M.empty)
+      $ forM_ (MA.fromJust $ (ms ^. ms_classes) ^. at c)
+      $ \a -> do
+        -- Look up the definition of this state in the original automata
+        -- so that we can sort it into mss_newcl appropriately
+        let ia :: f c
+               = runIdentity
+                 $ traverse (pure . MA.fromJust . ((ms ^. ms_clinv) ^.) . at)
+                 $ (MA.fromJust $ im ^. at a :: f a)
+        mss_newcl %= mapInOrCons ia a
+
+    mkClasses :: (Ord a, Enum c, Ord c) => [[a]] -> MinState a c
+    mkClasses = uncurry MS . go (toEnum 0) M.empty M.empty
+     where
+      go _ mc mi []          = (mc,mi)
+      go c mc mi ([]:xs)     = c `seq` go (succ c) mc mi xs
+      go c mc mi ((a:as):xs) = go c (mapInOrCons c a mc) (M.insert a c mi) (as:xs)
+
+    classifyUntil :: forall a c f . (Ord a, Enum c, Ord c, Traversable f, Ord (f c))
+                  => M.Map a (f a) -> MinState a c -> MinState a c -> MinState a c
+    classifyUntil im p p' =
+      if M.size (p ^. ms_classes) == M.size (p' ^. ms_classes)
+       then p'
+       else classifyUntil im p' (mkClasses $ splitClasses im p')
+
+------------------------------------------------------------------------}}}
diff --git a/src/Dyna/Analysis/Automata/Utilities.hs b/src/Dyna/Analysis/Automata/Utilities.hs
new file mode 100644 (file)
index 0000000..28a93b3
--- /dev/null
@@ -0,0 +1,41 @@
+---------------------------------------------------------------------------
+-- | Utilities for automata library
+
+-- Header material                                                      {{{
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+module Dyna.Analysis.Automata.Utilities where
+
+import           Control.Arrow (first)
+import           Control.Lens
+import           Control.Monad.State
+import qualified Data.Foldable                   as F
+import qualified Data.Traversable                as T
+import qualified Data.Map                        as M
+import qualified Data.Maybe                      as MA
+import           Dyna.Analysis.Automata.Class
+import           Dyna.Analysis.Automata.NamedAut
+import           Dyna.XXX.PPrint
+import           Text.PrettyPrint.Free
+
+------------------------------------------------------------------------}}}
+-- Pretty Printing                                                      {{{
+
+autRender :: (T.Traversable f, Automata a)
+          => (f (Doc e) -> Doc e)
+          -> a f -> Doc e
+autRender f a =
+  let (r, defs) = autReduceIx
+                    (\(x :: Int) -> (pan x, M.empty))
+                    (\x fr -> let r  = f $ fmap fst fr
+                                  ms = M.unions $ F.toList $ fmap snd fr
+                              in  (pan x, M.insert x r ms))
+                    a
+  in r <+> "where" <+> valign (map defrow $ M.toList defs)
+ where
+  pan = angles . pretty
+  defrow (k,v) = pan k <+> equals <+> v
+
+------------------------------------------------------------------------}}}
index 807d1c8191b854a108316675a0ed0500d49f80ff..10f4468964e3ecafa69d54287af6afc8b28f4358 100644 (file)
@@ -104,10 +104,11 @@ data KR f n k =
   | KRKey  k
  deriving (Eq,Ord,Show)
 
-instance (PP.Pretty f, PP.Pretty n, PP.Pretty k)
+instance (Show f, PP.Pretty n, PP.Pretty k)
       => PP.Pretty (KR f n k) where
   pretty (KRName n)   = PP.pretty n
-  pretty (KRStruct e) = IP.compactly PP.pretty (either PP.pretty PP.pretty) e
+  pretty (KRStruct e) = IP.compactly (PP.text . show)
+                                     (either PP.pretty PP.pretty) e
   pretty (KRKey k)    = PP.pretty k
 
 type KRI f n k = InstF f (Either n k)
@@ -131,10 +132,10 @@ data VR f n k =
   | VRKey    k
  deriving (Eq,Ord,Show)
 
-instance (PP.Pretty f, PP.Pretty n, PP.Pretty k)
+instance (Show f, PP.Pretty n, PP.Pretty k)
       => PP.Pretty (VR f n k) where
   pretty (VRName n)   = PP.pretty n
-  pretty (VRStruct y) = IP.compactly PP.pretty PP.pretty y
+  pretty (VRStruct y) = IP.compactly (PP.text . show) PP.pretty y
   pretty (VRKey k)    = PP.pretty k
 
 -- | Widen from a more restrictive to less restrictive recursion type.
@@ -169,7 +170,7 @@ instance (Monad m) => MonadError UnifFail (SIMCT m f) where
 instance MonadIO m => MonadIO (SIMCT m f) where
   liftIO m = SIMCT $ lift (liftIO m)
 
-instance (PP.Pretty f) => PP.Pretty (SIMCtx f) where
+instance (Show f, Ord f) => PP.Pretty (SIMCtx f) where
   pretty (SIMCtx _ km vm) =
     PP.vcat
     [ PP.text "Unaliased variables:"
index 1d1f9cf51bd928e993505b513ae3ac6313c266cd..25f2473930129448a2b95bdc0e5453c7ec0504de 100644 (file)
@@ -72,9 +72,9 @@ data VR f n =
   | VRStruct (InstF f (VR f n))
  deriving (Eq,Ord,Show)
 
-instance (PP.Pretty f, PP.Pretty n) => PP.Pretty (VR f n) where
+instance (Show f, PP.Pretty n) => PP.Pretty (VR f n) where
   pretty (VRName n)   = PP.pretty n
-  pretty (VRStruct y) = IP.compactly PP.pretty PP.pretty y
+  pretty (VRStruct y) = IP.compactly (PP.text . show) PP.pretty y
 
 {-
 -- This is used during rule analysis to capture the state of the binding
@@ -108,7 +108,7 @@ instance (Monad m) => MonadError UnifFail (SIMCT m f) where
 instance MonadIO m => MonadIO (SIMCT m f) where
   liftIO m = SIMCT $ lift (liftIO m)
 
-instance (PP.Pretty f) => PP.Pretty (SIMCtx f) where
+instance (Show f, Ord f) => PP.Pretty (SIMCtx f) where
   pretty (SIMCtx vm) = PP.vcat
                      $ flip map (M.toAscList vm)
                      $ \(v,vr) ->        PP.pretty v
index a1a1f3c0701379ec67594034f46204e5ddce1117..a90177a32ad468091ca9bbe7f9447a8096907e6d 100644 (file)
@@ -72,17 +72,22 @@ expandV :: (ExpC m f n k, MCVT m DVar ~ VR f n k, MCR m DVar)
 expandV v = clookup v >>= \x ->
             case x of
               VRName n -> return n
-              VRStruct y -> expandY y
+              VRStruct _ -> expandX x
               VRKey k -> clookup k
-                         >>= either return (expandY . q2y)
+                         >>= either return (expandX . VRStruct . q2y)
  where
-  expandY y = nDeep rec y
+  expandX x = nDeep rec x
    where
-    rec (VRName n)    = return (Left n)
-    rec (VRStruct y') = return (Right y')
-    rec (VRKey k)     = do
-      e <- clookup k
-      either (return . Left) (return . Right . q2y) e
+    rec :: (MCR m k, MCVT m k ~ ENKRI f n k, MonadTrans t, Monad (t m))
+        => VR f n k
+        -> Int
+        -> t m (Either n (InstF f (Either a1 (VR f n k))))
+    rec (VRName n)    _ = return (Left n)
+    rec (VRStruct y') _ = return (Right $ fmap Right y')
+    rec (VRKey k)     _ = do
+      e <- lift (clookup k)
+      return $ either Left (Right . fmap Right . q2y) e
+
 
 ------------------------------------------------------------------------}}}
 -- Leq                                                                  {{{
index 30786fd5bbac17f1c622659f122aade65837e1ed..72f8ee8abb4cd1fa8b4468996fd470f2772dba92 100644 (file)
@@ -75,10 +75,14 @@ expandV :: (ExpC m f n, MCVT m DVar ~ VR f n, MCR m DVar)
         => DVar -> m n
 expandV v = clookup v >>= \x -> case x of
                                   VRName n -> return n
-                                  VRStruct y -> nDeep rec y
+                                  VRStruct _ -> nDeep rec x
  where
-  rec (VRName n)   = return (Left n)
-  rec (VRStruct y) = return (Right y)
+  rec :: (MonadTrans t, Monad (t m))
+      => VR f n
+      -> Int
+      -> t m (Either n (InstF f (Either a1 (VR f n))))
+  rec (VRName n)    _ = return (Left n)
+  rec (VRStruct y') _ = return (Right $ fmap Right y')
 
 ------------------------------------------------------------------------}}}
 -- Leq                                                                  {{{
index 694d7a4c4464b4f0b3d07bdb4ee05ee7c97ddfc0..189d9d4e8c2e999dc62e63e9c2bf99982213aa30 100644 (file)
@@ -1,9 +1,6 @@
 ---------------------------------------------------------------------------
--- | Self-contained, mu-recursive inst automata
---
--- XXX This could stand a good bit of refactoring out to being generic, but
--- I am writing it quickly in hopes of checking that it works before
--- investing too much more time.
+-- | Self-contained, mu-recursive inst automata, using the generic Automata
+-- framework.
 
 -- Header material                                                      {{{
 {-# LANGUAGE FlexibleContexts #-}
 
 module Dyna.Analysis.Mode.Execution.NamedInst (
     -- * Datatype definition
-    NIX(..), NIXM,
+    NIX(..),
     -- * Unary functions
     -- ** Well-formedness predicates
-    nWellFormedUniq, nWellFormedOC,
+    nWellFormedUniq,
     -- ** Inquiries
     nAllNotEmpty, nSomeNotEmpty, nGround,
     -- ** Construction
-    nHide, nShallow, nDeep,
+    nHide, nShallow, nDeep, nFromMap,
     -- ** Destruction
     nExpose, 
-    -- ** Internals
-    nPrune,
     -- ** Rewrites
-    nUpUniq,
+    nUpUniq, nMinimize,
+    -- ** Display
+    renderNIX,
     -- * Binary comparators
-    nCmp, nEq, nLeq, nSub,
+    nEq, nLeq, nSub,
     -- * Total binary functions
-    nTBin, nLeqGLB, nSubGLB,
+    nLeqGLB, nSubGLB,
     -- * Partial binary functions
-    nPBin, nLeqGLBRD, nLeqGLBRL, -- nSubLUB,
+    nLeqGLBRD, nLeqGLBRL, -- nSubLUB,
     -- * Mode functions
     mWellFormed,
-
-    -- * XXX
-    eml, 
 ) where
 
-import           Control.Applicative
-import qualified Control.Arrow                     as A
 import           Control.Lens
--- import           Control.Monad.Identity
+import           Control.Monad.Identity
 import           Control.Monad.State
-import           Control.Monad.Trans.Either
 import qualified Data.Foldable                     as F
-import qualified Data.HashSet                      as H
 import qualified Data.Map                          as M
-import qualified Data.Set                          as S
 import qualified Data.Traversable                  as T
--- import qualified Debug.Trace                       as XT
+import           Dyna.Analysis.Automata.Class
+import           Dyna.Analysis.Automata.NamedAut
+import           Dyna.Analysis.Automata.Utilities
 import           Dyna.Analysis.Mode.Inst
 import qualified Dyna.Analysis.Mode.InstPretty     as IP
 import           Dyna.Analysis.Mode.Mode
 import           Dyna.Analysis.Mode.Unification
 import           Dyna.Analysis.Mode.Uniq
-import           Dyna.Main.Exception
--- import           Dyna.XXX.DataUtils
-import           Dyna.XXX.MonadUtils
--- import           System.IO.Unsafe (unsafePerformIO)
-import           System.Mem.StableName (makeStableName)
 import           Text.PrettyPrint.Free
 
 ------------------------------------------------------------------------}}}
 -- Datatype definition                                                  {{{
 
--- | Each named position in a NIX automata either references another
--- closed term or an InstF ply which itself recurses as labels in this
--- automata.
---
--- Note that we are contractually obligated to keep NIX automata mutually
--- acyclic (i.e. all cycles must be confied within some NIX automata).
-type NIXM a f = M.Map a (Either (NIX f) (InstF f a))
-
--- | A closed, mu-recursive inst.
---
--- The existential captures a @Show f@ as well for use by 'panicwf' below.
--- This requires that all terms constructing NIXes afresh (e.g. 'nHide' and
--- 'nShallow') have a @Show f@ constraint, but means that we don't need to
--- annotate the whole universe.  The trade-off, of course, is that we
--- potentially carry those dictionaries around at runtime.
---
--- The accessors and constructor is exported solely for the selftests'
--- benefits and SHOULD NOT be used elsewhere in the code!
-data NIX f = forall a . (Ord a,Show a,Show f) =>
-  NIX
-  {
-    -- | The top InstF ply in this term.
-    --
-    -- Note that while we could, in theory, have used @:: a@ here,
-    -- this would complicate the out-of-phase branch of comparison
-    -- operators.  Moreover, it's probably a good idea to use a ply
-    -- at the top as it forces NIX to be productive and not just
-    -- immediately alias another NIX via its map.
-    nix_root  :: InstF f a
-  , nix_map   :: NIXM a f
-  }
+newtype NIX f = NIX { unNIX :: NA (InstF f) }
 
 -- | Semantic, not structural, equality
 instance (Ord f) => Eq (NIX f) where
  n1 == n2 = nEq n1 n2
 
--- XXX This is hideously ugly, but we can clean it up later
-instance (Show f) => Show (NIX f) where
- show (NIX r m) = "(NIX ("++ (show r) ++ ") (" ++ (show m) ++ "))"
-
 ------------------------------------------------------------------------}}}
--- Utilities                                                            {{{
-
--- | Throw exception if ever a NIX is not well formed
-panicwf :: NIX f -> a
-panicwf n = dynacPanic (text "NIX not well-formed"
-                        `above` indent 2 (renderNIX n))
-
--- | Often we want to check a set cache for membership, returning true if
--- so, or assume this case and run some action to obtain a boolean.  This is
--- used, for example, in cycle-breaking in backward chaining: we assume the
--- provability of our assumption and continue to look for a
--- counter-argument.  Note that this is rather the opposite of most circular
--- systems!
-tsc :: (Ord e, MonadState s m)
-    => Simple Lens s (S.Set e)
-    -> e
-    -> m Bool
-    -> m Bool
-tsc l e miss = (uses l $ S.member e)
-               `orM`
-               (l %= S.insert e >> miss)
-
--- | Either of Maybe of Lookup.  A common pattern found in implementation.
-eml :: (Ord k)
-    => NIX f -- ^ For debugging
-    -> (a -> c)
-    -> (b -> c)
-    -> M.Map k (Either a b)
-    -> k
-    -> c
-eml n al ar m x = either al ar (ml n m x)
-
--- | Our particular version of 'fromJust' which panics appropriately.
-ml :: (Ord k) => NIX f -> M.Map k b -> k -> b
-ml n m x = maybe (panicwf n) id (M.lookup x m)
+-- Pretty-printing                                                      {{{
 
-------------------------------------------------------------------------}}}
--- Pretty-printing core                                                 {{{
-
-renderNIX_ :: forall f e. (NIX f -> Doc e) -> NIX f -> Doc e
-renderNIX_ p (NIX r m) = align $
-   ri r <> if M.null m
-            then empty
-            else line <> (indent 2 $
-                            text "where"
-                            <+> (align $ vsep $ map rme $ M.toList m))
-  where
-   -- render index
-   rix = angles . text . show
-
-   -- render map entry
-   rme (k,v) = rix k <+> equals <+> either p ri v
-
-   ri = IP.compactly (text . show) rix
-
--- | Print out a NIX just as it is.  Notably, this is used by 'panicwf',
--- rather than the 'Pretty' instance, which, in order to be pretty, calls
--- 'nPrune'.
-renderNIX :: forall f e . NIX f -> Doc e
-renderNIX = fix renderNIX_
+-- | Render a NIX exactly as it stands (the 'Pretty' instance performs
+-- minimization first).
+renderNIX :: forall f e . (Show f) => NIX f -> Doc e
+renderNIX = autRender (IP.compactly (text.show) id) . unNIX
+
+-- | Pretty-printing minimizes automata first
+instance (Show f, Ord f) => Pretty (NIX f) where
+  pretty (nMinimize -> n) = renderNIX n
+
+instance (Show f, Ord f) => Show (NIX f) where
+  show = show . pretty
 
 ------------------------------------------------------------------------}}}
 -- Unary predicates                                                     {{{
 
+-- | Check well-formedness of an inst at a given Uniq.  All uniqueness
+-- annotations within the inst are required to be larger (i.e. less unique,
+-- more restrictive).
+
+-- XXX This formulation does not use iWellFormed_.
+-- XXX I'm not sure that it's correct, either.  It has just enough hacks
+-- that I am uncomfortable.
+nWellFormedUniq :: forall f . Uniq -> NIX f -> Bool
+nWellFormedUniq u0 (NIX n) =
+  case autReduce (Just UClobbered) go n of
+    Nothing -> False
+    Just u  -> u0 <= u
+ where
+  go fmu = let ufu = maybe UClobbered id $ iUniq fmu
+               mfu = T.sequence fmu
+           in mfu >>= \fu -> if ufu <= minimum (UClobbered : F.toList fu)
+                              then Just ufu
+                              else Nothing
+
+{-
 -- | Check well-formedness of an inst at a given Uniq.  All uniqueness
 -- annotations within the inst are required to be larger (i.e. less unique,
 -- more restrictive).
@@ -200,32 +125,8 @@ nWellFormedUniq u0 n0@(NIX i0 m) = evalState (iWellFormed_ q u0 i0)
            eml n0 (return . nWellFormedUniq u)
                   (iWellFormed_ q u)
                   m a
+-}
 
--- | Check that a named inst is acyclic.
---
--- Makes use of the 'StableName' functionality (and 'unsafePerformIO') to
--- ensure that the Haskell heap is acyclic.  This is likely useful for
--- debugging nontermination of the compiler.  There's nothing that can save
--- us from an evil NIX which generates additional NIXes on the fly, tho'.
---
-nWellFormedOC :: (Ord f) => NIX f -> IO ()
-nWellFormedOC n0 = evalStateT (go n0) H.empty
- where
-  mksp x = x `seq` makeStableName x
-
-  visit q i = F.mapM_ (F.mapM_ q) (i ^. inst_rec)
-
-  go n@(NIX i m) = do
-    sn <- liftIO $ mksp n
-    vis <- get
-    if sn `H.member` vis
-     then dynacPanicStr "Named inst occurs check!"
-     else do
-           put (H.insert sn vis)
-           evalStateT (visit q i) S.empty
-   where
-    q a = tsc id a
-            (eml n0 (lift . go) (visit q) m a >> return True)
 
 -- | Is a named inst ground?
 --
@@ -233,66 +134,42 @@ nWellFormedOC n0 = evalStateT (go n0) H.empty
 -- (see def 3.2.17, p52), which includes everything from UUnique to
 -- UClobbered.  This may not be what you mean.
 nGround :: forall f . NIX f -> Bool
-nGround n0@(NIX i0 m) = evalState (iGround_ q i0) S.empty
+nGround = autReduce True (runIdentity . iGround_ return) . unNIX
+
+nNotEmpty_ :: forall f .
+              ([Bool] -> Bool)
+           -> NIX f
+           -> Bool
+nNotEmpty_ disj = autReduce True (runIdentity . visit) . unNIX
  where
-  q a = tsc id a $ eml n0 (return . nGround) (iGround_ q) m a
+  visit IFree     = return True
+  visit (IUniv _) = return True
+  visit (IAny _)  = return True
+  visit (IBound _ m b) = return (disj (b:fmap and (F.toList m)))
+{-# INLINABLE nNotEmpty_ #-}
 
 -- | Is there some term not ruled out by this inst?
 --
 -- This is mostly useful for the test harness, not actual reasoning, at
 -- the moment, since we are not sufficiently precise (i.e. we will miss some
 -- empty unification results).
+--
+-- Note in particular for InstF, the only non-accepting states are
+-- @IBound _ mempty False@; all the others include some term.
 nSomeNotEmpty :: forall f . NIX f -> Bool
-nSomeNotEmpty = fix (nNotEmpty_core orAny)
- where orAny b bs = b `orM1` (anyM bs)
+nSomeNotEmpty = nNotEmpty_ or
+{-# INLINABLE nSomeNotEmpty #-}
 
--- | Like 'nNotEmpty' but conjunctive across choices -- that is, this
+-- | Like 'nSomeNotEmpty' but conjunctive across choices -- that is, this
 -- requires that all possible branches of an automata are non-empty, rather
--- than 'nNotEmpty', which only checks that there is some reachable state in
--- the automata.
-nAllNotEmpty :: forall f . (Show f) => NIX f -> Bool
-nAllNotEmpty = fix (nNotEmpty_core andAll)
- where andAll b bs = b `andM1` (allM bs)
-
-
-
-nNotEmpty_core :: forall f .
-                  (forall m .
-                     (Monad m)
-                  => Bool
-                  -> [m Bool]
-                  -> m Bool)
-               -> (NIX f -> Bool)
-               -> NIX f -> Bool
-nNotEmpty_core disj self n0@(NIX i0 m0) = evalState (visit i0) S.empty
- where
-  visit IFree     = return True
-  visit (IUniv _) = return True
-  visit (IAny _)  = return True
-  visit (IBound _ m b) = b `disj` (M.foldr (\fas a -> allM (map rec fas) : a) [] m)
-
-  rec idx = tsc id idx (eml n0 (return . self) visit m0 idx)
+-- than 'nSomeNotEmpty', which only checks that there is some input which
+-- is accepted by the automata.
+nAllNotEmpty :: forall f . NIX f -> Bool
+nAllNotEmpty = nNotEmpty_ and
+{-# INLINABLE nAllNotEmpty #-}
 
--- | Increase the nonuniqueness of a particular named inst to at least the
--- given level.
---
--- This would be equivalent to unification with 'IANy' at the given 'Uniq'
--- level, save that it leaves free variables untouched.
 nUpUniq :: forall f . (Ord f) => Uniq -> NIX f -> NIX f
-{-
- - XXX The beginnings of a possibly more efficient implementation
-nUpUniq u0 n0@(NIX i0 m) = uncurry NIX $ runState (T.traverse visit i0) m
- where
-  visit a = eml n0 (return . nUpUniq u0)
-                   (T.traverse visit (over inst_uniq (max u0)))
--}
-nUpUniq u0 n0@(NIX i0 m0) =
-   maybe n0 (\u' -> if u' >= u0 then n0 else NIX i0' m0') (iUniq i0)
- where
-  reuniq = over inst_uniq (max u0)
-
-  m0' = M.map (nUpUniq u0 A.+++ reuniq) m0
-  i0' = reuniq i0
+nUpUniq u0 = NIX . autAtEachState (\_ -> over inst_uniq (max u0)) . unNIX
 {-# INLINABLE nUpUniq #-}
 
 -- | Expose the root ply of a 'NIX' as an Inst which recurses as additional
@@ -300,298 +177,43 @@ nUpUniq u0 n0@(NIX i0 m0) =
 --
 -- Note that recursive use of this function may well diverge!
 nExpose :: NIX f -> InstF f (NIX f)
-nExpose n@(NIX r m) = fmap (\a -> either id (\i -> NIX i m) (ml n m a)) r
+nExpose = fmap NIX . autExpose . unNIX
 {-# INLINABLE nExpose #-}
 
--- | An inefficient \"inverse\" (up to isomorphism) of nExpose.
-nHide :: (Show f) => InstF f (NIX f) -> NIX f
-nHide i = uncurry NIX $ runState (T.mapM next i) M.empty
- where
-  next n = do
-    m <- get
-    let n' = M.size m
-    put (M.insert n' (Left n) m)
-    return n'
-{-# INLINABLE nHide #-}
-
-nShallow :: (Show f) => InstF f a -> Maybe (NIX f)
-nShallow IFree          = Just $ nHide $ IFree
-nShallow (IAny u)       = Just $ nHide $ (IAny u)
-nShallow (IUniv u)      = Just $ nHide $ (IUniv u)
-nShallow (IBound _ _ _) = Nothing
-{-# INLINABLE nShallow #-}
-
-nDeep :: (Show f, Monad m, Functor m)
-      => (r -> m (Either (NIX f) (InstF f r)))
-      -> InstF f r
-      -> m (NIX f)
-nDeep rec root = liftM (\(nr,(_,nm)) -> NIX nr nm) $
-  flip runStateT (0 :: Int, M.empty) $ inst_recps rec' root
- where
-  rec' r = do
-    a  <- _1 <<%= (+(1 :: Int))
-    rhs <- lift (rec r)
-    rhs' <- either (return . Left) (liftM Right . inst_recps rec') rhs
-    _2 %= M.insert a rhs'
-    return a
+nHide :: InstF f (NIX f) -> NIX f
+nHide = NIX . autHide . fmap unNIX
+{-# INLinABLE nHide #-}
 
+nShallow :: (Show f) => InstF f x -> Maybe (NIX f)
+nShallow = fmap NIX . autShallow
+{-# INLINABLE nShallow #-}
 
+nDeep :: (Functor m, Monad m, Enum ix, Ord ix)
+      => (forall t . (MonadTrans t, Monad (t m))
+           => r -> ix -> t m (Either (NIX f) (InstF f (Either ix r))))
+      -> r -> m (NIX f)
+nDeep f r = liftM NIX $ naUnfold (\r' ix' -> liftM (left' unNIX) $ f r' ix') r
+{-# INLINABLE nDeep #-}
+
+nFromMap :: forall f oix.
+            Ord oix
+         => M.Map oix (InstF f oix) -- ^ recursion table
+         -> oix                     -- ^ root
+         -> NIX f
+nFromMap m r = NIX (naFromMap m r)
+{-# INLINABLE nFromMap #-}
 
 ------------------------------------------------------------------------}}}
 -- Binary predicates                                                    {{{
 
-nCmp :: forall f .
-        (Ord f)
-     => (forall a b m .
-            (Monad m)
-         => (a -> (forall i_ . InstF f i_) -> m Bool)
-         -> (a -> b -> m Bool)
-         -> InstF f a -> InstF f b -> m Bool)
-     -> NIX f -> NIX f -> Bool
-nCmp q l0@(NIX li0 lm) r0@(NIX ri0 rm) =
-  evalState (q qop qip li0 ri0) (S.empty, S.empty)
- where
-  -- Q In Phase
-  qip l r  = -- XT.traceShow ("NCMP QIP",l,r) $
-             tsc _1 (l,r) $ do
-               eli <- maybe (panicwf l0) return $ M.lookup l lm
-               eri <- maybe (panicwf r0) return $ M.lookup r rm
-               case (eli,eri) of
-                 (Left  l', Left  r') -> return $ nCmp q l' r'
-                 (Left  l', Right r') -> return $ nCmp q l' (NIX r' rm)
-                 (Right l', Left  r') -> return $ nCmp q (NIX l' lm) r'
-                 (Right l', Right r') -> q qop qip l' r'
-
-  -- Q Out of Phase
-  qop l ri = -- XT.traceShow ("NCMP QOP",l,ri) $
-             tsc _2 (l,downcast ri)
-               $ eml l0 (return . flip (nCmp q) (NIX ri rm))
-                        (flip (q qop qip) ri)
-                        lm l
-
-  downcast :: (forall i_ . InstF f i_) -> InstF f ()
-  downcast i = i
-
 nEq, nLeq, nSub :: (Ord f) => NIX f -> NIX f -> Bool
-nEq  = nCmp (\_ -> iEq_)
-nLeq = nCmp iLeq_
-nSub = nCmp iSub_
+nEq (NIX l) (NIX r) = autBiReduce True (\_ _ -> iEq_) l r
+nLeq (NIX l) (NIX r) = autBiReduce True (\x _ -> iLeq_ x) l r
+nSub (NIX l) (NIX r) = autBiReduce True (\x _ -> iSub_ x) l r
 
 ------------------------------------------------------------------------}}}
 -- Binary functions                                                     {{{
 
-data NBinState a b f u = NBS { _nbs_next  :: Int
-                           , _nbs_ctx   :: NIXM Int f
-                           , _nbs_cache_symm :: M.Map (u,a,b) Int
-                           , _nbs_cache_lsml :: M.Map (u,InstF f b,a) Int
-                           , _nbs_cache_lsmr :: M.Map (u,InstF f a,b) Int
-                           }
-$(makeLenses ''NBinState)
-
-iNBS :: NBinState a b f u
-iNBS = NBS 0 M.empty M.empty M.empty M.empty
-
-
-nTBin :: forall f . (Ord f, Show f)
-      => (  forall a b c m .
-            (Monad m)
-         => (Uniq -> a -> m c)
-         -> (Uniq -> b -> m c)
-         -> (Uniq -> (forall i_ . InstF f i_) -> a -> m c)
-         -> (Uniq -> (forall i_ . InstF f i_) -> b -> m c)
-         -> (Uniq -> a -> b -> m c)
-         -> Uniq
-         -> InstF f a -> InstF f b -> m (InstF f c))
-      -> NIX f -> NIX f -> NIX f
-nTBin f l0@(NIX li0 lm) r0@(NIX ri0 rm) = evalState (tlq li0 ri0) iNBS
- where
-  tlq l r = do
-    ci <- f' S.empty UUnique l r
-    ctx <- use nbs_ctx
-    return $ nPrune $ NIX ci ctx
-
-  f' sc = f (imp l0 lm) (imp r0 rm) (lsml sc) (lsmr sc) (merge sc)
-
-  -- XXX import needs some caching, too.
-
-  -- Occasionally, we need to "import" a term from one of the two inputs;
-  -- this happens when we unify 'IBound' against 'IAny' or 'IUniv', for
-  -- example.
-  --
-  -- To import the key x from the map m into our context,
-  --   if it is a closed term, just return that
-  --   otherwise, make it a closed term whose root is x and whose map is m
-  imp n m u x = 
-     -- XT.traceShow ("NTB I",u,m,x)
-     eml n return (return . flip NIX m) m x >>= new . Left . nUpUniq u 
-
-  new  x = do
-    k <- nbs_next <<%= (+1)
-    nbs_ctx %= M.insert k x
-    return k
-
-  lsml sc u ir l = -- XT.traceShow ("NTB L",u,ir,l) $
-                   do
-    cached <- uses nbs_cache_lsml $ M.lookup (u,ir,l)
-    maybe merge' return cached
-   where
-    merge' = do
-      k <- nbs_next <<%= (+1)
-      nbs_cache_lsml %= M.insert (u,ir,l) k
-      v <- eml r0
-               (return . luu u . nTBin f (NIX ir rm))
-               (\l' -> Right . over inst_uniq (max u) <$> f' (S.insert k sc) u l' ir)
-               lm l
-      nbs_ctx %= M.insert k v
-      return k
-  
-  lsmr sc u il r = -- XT.traceShow ("NTB R",u,il,r) $
-                   do
-    cached <- uses nbs_cache_lsmr $ M.lookup (u,il,r)
-    maybe merge' return cached
-   where
-    merge' = do
-      k <- nbs_next <<%= (+1)
-      nbs_cache_lsmr %= M.insert (u,il,r) k
-      v <- eml r0
-               (return . luu u . nTBin f (NIX il lm))
-               (\r' -> Right . over inst_uniq (max u) <$> f' (S.insert k sc) u il r')
-               rm r
-      nbs_ctx %= M.insert k v
-      return k
-
-  luu u = Left . nUpUniq u
-
-  merge sc u l r = -- XT.traceShow ("NTB M",u,l,r) $
-                   do
-    cached <- uses nbs_cache_symm $ M.lookup (u,l,r)
-    maybe merge' return cached
-   where
-    merge' = do
-      k <- nbs_next <<%= (+1)
-      nbs_cache_symm %= M.insert (u,l,r) k
-      eli <- maybe (panicwf l0) return $ M.lookup l lm
-      eri <- maybe (panicwf r0) return $ M.lookup r rm
-      v <- case (eli,eri) of
-             (Left  l', Left  r') -> return $ luu u $ nTBin f l' r'
-             (Left  l', Right r') -> return $ luu u $ nTBin f l' (NIX r' rm)
-             (Right l', Left  r') -> return $ luu u $ nTBin f (NIX l' lm) r'
-             (Right l', Right r') -> Right . over inst_uniq (max u)
-                                     <$> f' (S.insert k sc) u l' r'
-      nbs_ctx %= M.insert k v
-      return k
-
--- | Total lattice functions
-nLeqGLB, nSubGLB :: forall f .
-                    (Ord f, Show f)
-                 => NIX f -> NIX f -> NIX f
-nLeqGLB = nTBin iLeqGLB_
-nSubGLB = nTBin (\_ _ fl fr fm _ -> iSubGLB_ (fl UUnique) (fr UUnique) (fm UUnique))
-
-nPBin :: forall e f .
-         (Ord f, Show f)
-      => (  forall a b c m .
-            (Monad m, Show a, Show b, Show c)
-         => (Uniq -> a -> m c)
-         -> (Uniq -> b -> m c)
-         -> (Uniq -> (forall i_ . InstF f i_) -> a -> m c)
-         -> (Uniq -> (forall i_ . InstF f i_) -> b -> m c)
-         -> (Uniq -> a -> b -> m c)
-         -> Uniq
-         -> InstF f a -> InstF f b -> m (Either e (InstF f c)))
-      -> NIX f -> NIX f -> Either e (NIX f)
-nPBin f l0@(NIX li0 lm) r0@(NIX ri0 rm) = evalState (runEitherT (tlq li0 ri0)) iNBS
- where
-  tlq l r = do
-    ci <- f' S.empty UUnique l r
-    ci' <- hoistEither ci
-    ctx <- use nbs_ctx
-    return $ nPrune $ NIX ci' ctx
-
-  f' sc = f (imp l0 lm) (imp r0 rm) (lsml sc) (lsmr sc) (merge sc)
-
-  luu u = Left . nUpUniq u
-  mluu u r = luu u <$> hoistEither r
-
-  -- Occasionally, we need to "import" a term from one of the two inputs;
-  -- this happens when we unify 'IBound' against 'IAny' or 'IUniv', for
-  -- example.
-  --
-  -- To import the key x from the map m into our context,
-  --   if it is a closed term, just return that
-  --   otherwise, make it a closed term whose root is x and whose map is m
-  imp n m u x = {- XT.traceShow ("NPB I",m,u,x) $ -}
-    new . luu u =<< eml n return (return . flip NIX m) m x
-
-  new  x = do
-    k <- nbs_next <<%= (+1)
-    nbs_ctx %= M.insert k x
-    return k
-
-  lsml sc u ir l = {- XT.traceShow ("NPB L",u,ir,l) $ -}
-                   do
-    cached <- uses nbs_cache_lsml $ M.lookup (u,ir,l)
-    maybe merge' return cached
-   where
-    merge' = do
-      k <- nbs_next <<%= (+1)
-      nbs_cache_lsml %= M.insert (u,ir,l) k
-      v <- eml r0
-               (mluu u . nPBin f (NIX ir rm))
-               (\l' -> do
-                  l'' <- f' (S.insert k sc) u l' ir
-                  (Right . over inst_uniq (max u)) <$> hoistEither l'')
-               lm l
-      nbs_ctx %= M.insert k v
-      return k
-  
-  lsmr sc u il r = {- XT.traceShow ("NPB R",u,il,r) $ -}
-                   do
-    cached <- uses nbs_cache_lsmr $ M.lookup (u,il,r)
-    maybe merge' return cached
-   where
-    merge' = do
-      k <- nbs_next <<%= (+1)
-      nbs_cache_lsmr %= M.insert (u,il,r) k
-      v <- eml r0
-               (mluu u . nPBin f (NIX il lm))
-               (\r' -> do
-                  r'' <- f' (S.insert k sc) u il r'
-                  (Right . over inst_uniq (max u)) <$> hoistEither r'')
-               rm r
-      nbs_ctx %= M.insert k v
-      return k
-
-
-  merge sc u l r = {- XT.traceShow ("NPB M",u,l,r) $ -}
-                   do
-    cached <- uses nbs_cache_symm $ M.lookup (u,l,r)
-    maybe merge' return cached
-   where
-    merge' = do
-      k <- nbs_next <<%= (+1)
-      nbs_cache_symm %= M.insert (u,l,r) k
-      eli <- maybe (panicwf l0) return $ M.lookup l lm
-      eri <- maybe (panicwf r0) return $ M.lookup r rm
-      v <- case (eli,eri) of
-             (Left  l', Left  r') -> mluu u $ nPBin f l' r'
-             (Left  l', Right r') -> mluu u $ nPBin f l' (NIX r' rm)
-             (Right l', Left  r') -> mluu u $ nPBin f (NIX l' lm) r'
-             (Right l', Right r') -> do
-                                      m' <- f' (S.insert k sc) u l' r'
-                                      (Right . over inst_uniq (max u))
-                                         <$> hoistEither m'
-      nbs_ctx %= M.insert k v
-      return k
-
--- | Partial lattice functions.  These raise unification failures if
--- the runtime would fail.
-nLeqGLBRD, nLeqGLBRL :: forall f .
-                        (Ord f, Show f)
-                     => NIX f -> NIX f -> Either UnifFail (NIX f)
-nLeqGLBRD = nPBin iLeqGLBRD_
-nLeqGLBRL = nPBin iLeqGLBRL_
-
 {-
 
 XXX BITROTTED; NOT YET -- need better understanding of the problem.  The âŠ”
@@ -599,11 +221,45 @@ function is particularly interesting and it is not yet clear how to define
 its recursion in a way that is not painfully special-cased.  At this instant
 I have other things that can demand attention.
 
-
 nSubLUB :: forall f . (Ord f, Show f) => NIX f -> NIX f -> Maybe (NIX f)
 nSubLUB = nPBin (\il ir ll lr m -> iSubLUB_ il ir (ll UClobbered) (lr UClobbered) (m UClobbered))
 -}
 
+ctxUniq :: InstF f a -> InstF f' a' -> Uniq
+ctxUniq x y = u x `max` u y
+ where
+  u = maybe UUnique id . iUniq
+
+-- XXX This may suggest that we could eliminate the "import" callbacks on
+-- iLeqGLB_ and friends now that we're not doing anything special with them.
+
+bendImpl :: forall f i i' i'' m r .
+            TyILeqGLB_ f m i i' i'' r
+         -> (Uniq -> i -> NonRec (InstF f)  -> m i'')
+         -> (Uniq -> NonRec (InstF f) -> i' -> m i'')
+         -> (Uniq -> i -> i' -> m i'')
+         -> Uniq -> InstF f i -> InstF f i' -> r
+bendImpl x lsml lsmr merge = x impl impr lsml' lsmr merge
+   where
+    lsml' :: Uniq -> NonRec (InstF f) -> i -> m i''
+    lsml' u r l = lsml u l r
+
+    impl :: Uniq -> i -> m i''
+    impl u l = lsml u l IFree
+
+    impr :: Uniq -> i' -> m i''
+    impr u r = lsmr u IFree r
+
+nLeqGLB, nSubGLB :: forall f . (Ord f) => NIX f -> NIX f -> NIX f
+nLeqGLB (NIX l) (NIX r) = NIX $ autMerge ctxUniq (bendImpl iLeqGLB_) l r
+nSubGLB (NIX l) (NIX r) = NIX $ autMerge (\_ _ -> ()) go l r
+ where
+  go lsml lsmr merge () = iSubGLB_ (flip (lsml ())) (lsmr ()) (merge ())
+
+nLeqGLBRD, nLeqGLBRL :: (Ord f) => NIX f -> NIX f -> Either UnifFail (NIX f)
+nLeqGLBRD (NIX l) (NIX r) = fmap NIX $ autPMerge ctxUniq (bendImpl iLeqGLBRD_) l r
+nLeqGLBRL (NIX l) (NIX r) = fmap NIX $ autPMerge ctxUniq (bendImpl iLeqGLBRL_) l r
+
 ------------------------------------------------------------------------}}}
 -- Mode functions                                                       {{{
 
@@ -624,41 +280,7 @@ mWellFormed (QMode ats vm@(vti,vto) _) =
 ------------------------------------------------------------------------}}}
 -- Cleanup and minimization                                             {{{
 
-nCrawl :: forall f .
-          (forall a . Uniq -> InstF f a) -- ^ Replace free variables
-       -> Uniq                             -- ^ Minimum uniqueness
-       -> NIX f
-       -> NIX f
-nCrawl fv u0 n0@(NIX i0 m) =
-  let i0' = reall u0 i0 in NIX i0' $ execState (T.traverse (evac u0) i0') M.empty
- where
-  reun = over inst_uniq (max u0)
-
-  refv u IFree = fv u
-  refv _ x     = x
-
-  reall u = reun . refv u
-
-  evac u i = gets (M.lookup i) >>= maybe (go u i) (const $ return ())
-
-  go u i = do
-    let l = ml n0 m i
-    case l of
-      Left n  -> id %= M.insert i (Left $ nCrawl fv u n)
-      Right x -> do
-                  id %= M.insert i (Right $ reall u x)
-                  F.traverse_ (evac (maybe u (max u) $ iUniq x)) x
-                  return ()
-
--- | Prune the internals of a 'NIX'.  This really ought not be needed, but
--- it's handy for test generation.
-nPrune :: forall f . NIX f -> NIX f
-nPrune = nCrawl (const IFree) UUnique
-
-------------------------------------------------------------------------}}}
--- Pretty-printing part 2                                               {{{
-
-instance Pretty (NIX f) where
- pretty (nPrune -> n) = renderNIX_ pretty n
+nMinimize :: (Ord f) => NIX f -> NIX f
+nMinimize = NIX . autMinimize . unNIX
 
 ------------------------------------------------------------------------}}}
index 52ed1aa11a09d04195e9804e42bdd571468da00f..aad7ce467698edf0d3b184f13ad51ea791cd48b5 100644 (file)
@@ -22,6 +22,7 @@
 {-# LANGUAGE DeriveFunctor #-}
 {-# LANGUAGE DeriveTraversable #-}
 {-# LANGUAGE NoMonomorphismRestriction #-}
+{-# LANGUAGE StandaloneDeriving #-}
 {-# LANGUAGE RankNTypes #-}
 {-# LANGUAGE TemplateHaskell #-}
 {-# OPTIONS_GHC -Wall #-}
@@ -35,17 +36,16 @@ module Dyna.Analysis.Mode.Inst(
 
 import           Control.Applicative (Applicative)
 import           Control.Lens
--- import           Control.Monad
+import           Control.Monad
 import qualified Data.Foldable            as F
 import qualified Data.Traversable         as T
 import qualified Data.Map                 as M
 import qualified Data.Maybe               as MA
 -- import qualified Data.Set                 as S
+import           Dyna.Analysis.Mode.Uniq
 import           Dyna.XXX.DataUtils       as XDU
 import           Dyna.XXX.MonadUtils
 
-import           Dyna.Analysis.Mode.Uniq
-
 -- import qualified Debug.Trace                       as XT
 
 ------------------------------------------------------------------------}}}
@@ -189,14 +189,11 @@ inIGamma (RA r t) (IBound u ts)  = r == uniqGamma u
 -- | Extract the uniqueness from an inst.
 --
 -- Based on definition 3.2.13, p51 but see prose, p51, \"Note that there is
--- no uniqueness annotation on the free inst\".
+-- no uniqueness annotation on the free inst\".  This is because we assume,
+-- during mode work, that we know where all the free variables are -- that
+-- is, they're either uniquely referenced in some automata or they are
+-- explicitly aliased.
 iUniq :: InstF f i -> Maybe Uniq
-{-
-iUniq IFree          = Nothing
-iUniq (IAny u)       = Just u
-iUniq (IBound u _ _) = Just u
-iUniq (IUniv u)      = Just u
--}
 iUniq = (^? inst_uniq)
 {-# INLINABLE iUniq #-}
 
@@ -366,8 +363,10 @@ type TyILeqGLB_ f m i i' i'' r =
 -- variables).
 
 iLeqGLB_ :: (Monad m, Ord f) => TyILeqGLB_ f m i i' i'' (m (InstF f i''))
-iLeqGLB_ _ r _ _ _ u IFree      x            = T.mapM (r u) x
-iLeqGLB_ l _ _ _ _ u x          IFree        = T.mapM (l u) x
+iLeqGLB_ _ r _ _ _ u IFree      x            = liftM (over inst_uniq (max u))
+                                             $ T.mapM (r u) x
+iLeqGLB_ l _ _ _ _ u x          IFree        = liftM (over inst_uniq (max u))
+                                             $ T.mapM (l u) x
 
 iLeqGLB_ _ _ _ _ _ _ (IAny u)   (IAny u')    = return $ IAny (max u u')
 
index f5b273e06359504b630bf2b28cd7918675b724e3..bcf7ddf9ea9a2378bbdeb629aef31351a7eaa7ec 100644 (file)
@@ -23,6 +23,8 @@ import qualified Data.Map                  as M
 import qualified Data.Maybe                as MA
 import qualified Data.Set                  as S
 import qualified Data.Traversable          as T
+import           Dyna.Analysis.Automata.Class
+import           Dyna.Analysis.Automata.Utilities
 import           Dyna.Analysis.Mode.Execution.NamedInst
 import           Dyna.Analysis.Mode.Inst
 import           Dyna.Analysis.Mode.Selftest.Term
@@ -50,10 +52,11 @@ nWFN' n = nWFU n && nAllNotEmpty n
 prop_arbNIX_is_WFU :: QCP.Property
 prop_arbNIX_is_WFU = QCP.forAll (arbNIX UUnique) nWFU
 
--- | Check 'nPrune' using the separate 'arbNIX' generator, rather than
--- the 'Arbitrary' instance, which calls 'nPrune' internally.
+-- | Check 'nMinimize' using the separate 'arbNIX' generator, rather than
+-- the 'Arbitrary' instance, which calls 'nMinimize' internally.
 prop_nPrune_Eq :: QCP.Property
-prop_nPrune_Eq = QCP.forAll (arbNIX UUnique) (\i -> nWFU i QCP.==> i `nEq` (nPrune i))
+prop_nPrune_Eq = QCP.forAll (arbNIX UUnique)
+                            (\i -> nWFU i QCP.==> i `nEq` (nMinimize i))
 
 -- | Check 'nMinimize'
 -- prop_nMinimize_Eq :: Bool -> QCP.Property
@@ -168,3 +171,5 @@ prop_call_test_sufficient i1 i2 = nWFN' i1 && nWFN' i2 && nSub i1 i2
 
 selftest :: TF.Test
 selftest = moreTries 5000 $(testGroupGenerator)
+
+main = TF.defaultMain [selftest]
index f315bb71ad9be2a875f933fdee6c08ffad583528..ac3700fb955168e967c052f10f02005abefe41e1 100644 (file)
@@ -6,6 +6,7 @@
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE GADTs #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE NoMonomorphismRestriction #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 
 module Dyna.Analysis.Mode.Selftest.Term where
@@ -17,12 +18,15 @@ import qualified Data.List                 as L
 import qualified Data.Map                  as M
 import qualified Data.Maybe                as MA
 import qualified Data.Set                  as S
+import           Dyna.Analysis.Automata.Class
+import           Dyna.Analysis.Automata.Utilities
 import           Dyna.Analysis.Mode.Inst
 import           Dyna.Analysis.Mode.Uniq
 import           Dyna.Analysis.Mode.Execution.NamedInst
 import           Test.QuickCheck
 import           Test.SmallCheck           as SC
 import           Test.SmallCheck.Series    as SCS
+import qualified Text.PrettyPrint.Free     as PP
 
 ------------------------------------------------------------------------}}}
 -- Misc                                                                 {{{
@@ -69,6 +73,8 @@ arbUniq u = oneof $ map return [u..]
 data TestFunct = F | G | H
  deriving (Bounded,Enum,Eq,Ord,Show)
 
+instance PP.Pretty TestFunct where pretty = PP.text . show
+
 genArgs :: (Monad m) => m i -> TestFunct -> m [i]
 genArgs _ F = return []
 genArgs g G = g >>= return . (\x -> [x])
@@ -91,6 +97,7 @@ instance Arbitrary TestFunct where arbitrary = arbitraryBoundedEnum
 instance Arbitrary (S.Set TestFunct) where arbitrary = arbitrarySetBEO
 
 -- | Generate an arbitrary, well-formed NIX at some uniqueness.
+arbNIX :: Uniq -> Gen (NIX TestFunct)
 arbNIX rootU = do
     n <- sized (\s -> choose (1::Int,max 1 s)) -- How many plies?
 
@@ -106,12 +113,12 @@ arbNIX rootU = do
     let igen u = oneof $ map (return.snd) $ filter ((== u) . fst)
                        $ (zip uniqs nl ++ spares)
 
-    plies <- mapM (flip arbNIXME igen) uniqs   -- Generate plies or recurse
+    plies <- mapM (flip arbInstPly igen) uniqs   -- Generate plies or recurse
 
     let m = M.fromList (zip nl plies            -- Make the map
-                        ++ map (\(_,i) -> (i,Right IFree)) spares)
-    root <- arbInstPly rootU igen               -- The root
-    return $ NIX root m
+                        ++ map (\(_,i) -> (i,IFree)) spares)
+    root <- choose (1,n)
+    return $ nFromMap m root
  where
   arbInstPly :: forall i . Uniq -> (Uniq -> Gen i) -> Gen (InstF TestFunct i)
   arbInstPly u n = frequency
@@ -126,21 +133,16 @@ arbNIX rootU = do
                  <*> arbitrary)
     ]
   
-  arbNIXME :: forall f i . (f ~ TestFunct)
-           => Uniq -> (Uniq -> Gen i) -> Gen (Either (NIX f) (InstF f i))
-  arbNIXME u igen = oneof [Left  <$> sized (\s -> resize (s`div`2) (arbNIX u))
-                          ,Right <$> arbInstPly u igen]
-
-
 -- | The not-necessarily-well-formed variant
+arbNIXNWF :: Gen (NIX TestFunct)
 arbNIXNWF = do
     n <- sized (\s -> choose (1::Int,max 1 s)) -- How many plies?
     let nl   = [1..n]
     let igen = choose (1::Int,n)               -- Pick a ply
-    plies <- vectorOf n (arbNIXMENWF igen)     -- Generate plies or recurse
+    plies <- vectorOf n (arbInstPlyNWF igen)   -- Generate plies or recurse
     let m = M.fromList $ zip nl plies          -- Make the map
-    root <- arbInstPlyNWF igen                 -- The root
-    return $ NIX root m
+    root <- choose (1,n)
+    return $ nFromMap m root
  where
   arbInstPlyNWF :: forall i . Gen i -> Gen (InstF TestFunct i)
   arbInstPlyNWF n = frequency
@@ -152,16 +154,11 @@ arbNIXNWF = do
                                  $ genFuncMap arbitrary n)
                 <*> arbitrary)
     ]
-  
-  arbNIXMENWF :: forall f i . (f ~ TestFunct)
-              => Gen i -> Gen (Either (NIX f) (InstF f i))
-  arbNIXMENWF igen = oneof [Left  <$> sized (\s -> resize (s`div`2) arbitrary)
-                        ,Right <$> arbInstPlyNWF igen]
-
 
 instance Arbitrary (NIX TestFunct) where
-  arbitrary = nPrune <$> arbNIX UUnique
+  arbitrary = liftM nMinimize $ arbNIX UUnique
 
+{-
   shrink n@(NIX r m) = (MA.maybeToList noRecN) ++ subautomata
    where
     -- Replace all calls out to other automata with references to the root
@@ -170,7 +167,7 @@ instance Arbitrary (NIX TestFunct) where
 
     -- Pull out all the subautomata
     subautomata = E.lefts (M.elems m)
-
+-}
 
 ------------------------------------------------------------------------}}}
 -- Term: SmallCheck Generators                                          {{{
index ed0c149e0bc63af44b8ea8a753fc87e8ae7f80a2..d632605b98d9b5cdc96aa18e3d263f3aac56f96c 100644 (file)
@@ -265,7 +265,7 @@ pdope_ _ (OPIter v vs _ Det (Just (PDBS c))) = return $ pretty (v^.mv_var)
                                      <+> c v vs
 
 pdope_ _ (OPIter v vs _ DetNon (Just (PDBS c))) = do
-      i <- incState
+      i <- incState id
       return $ "for" <+> "d" <> pretty i
                       <> comma
                       <> piterate vs
@@ -283,7 +283,7 @@ pdope_ _ (OPIter v vs f d   (Just (PDBS c))) = dynacPanic $
 
 -- XXX This works only for the special case at hand (thus the asserts)
 pdope_ bc (OPIter o m f DetSemi Nothing) | (f,length m) `S.member` bc = do
-  dookie <- incState
+  dookie <- incState id
   return $
    assert (iIsFree $ nExpose $ o^.mv_mi) $
    assert (all (not . iIsFree . nExpose . _mv_mi) m) $
@@ -304,7 +304,7 @@ pdope_ bc (OPIter o m f DetSemi Nothing) | (f,length m) `S.member` bc = do
 
 pdope_ bc (OPIter o m f _ Nothing) =
   assert (not $ (f,length m) `S.member` bc) $ do
-      i <- incState
+      i <- incState id
       return $ let mo = m ++ [o] in
           "for" <+> "d" <> pretty i <> "," <> piterate m <> comma <> (ground2underscore o)
                 <+> "in" <+> functorIndirect "chart" f m <> pslice mo <> colon
index db9aaa40bb93e7da810cff986eeab5a4d5a2a6b7..4cfdb8b407feebb6059003ad92338b3c5989dacf 100644 (file)
@@ -1,3 +1,5 @@
+{-# LANGUAGE Rank2Types #-}
+
 module Dyna.XXX.MonadUtils(
   -- * Data utilities generalizing 'Dyna.XXX.DataUtils'
   mapForallM, mapExistsM, setForallM, setExistsM,
@@ -7,6 +9,8 @@ module Dyna.XXX.MonadUtils(
   andM, andM1, orM, orM1, allM, anyM,
   -- * MonadState utilities
   bracketState, incState, readState,
+  -- * Monad cache utilities
+  trySetCache, tryMapCache,
 ) where
 
 -- import           Control.Applicative
@@ -14,27 +18,34 @@ import           Control.Lens
 import           Control.Monad.Reader
 import           Control.Monad.State
 import qualified Data.Map  as M
+import           Data.Monoid
 import qualified Data.Set  as S
 
 andM :: Monad m => m Bool -> m Bool -> m Bool
 andM x y = x >>= flip andM1 y 
+{-# INLINABLE andM #-}
 
 andM1 :: Monad m => Bool -> m Bool -> m Bool
 andM1 False _ = return False
 andM1 True  x = x
+{-# INLINABLE andM1 #-}
 
 orM :: Monad m => m Bool -> m Bool -> m Bool
 orM x y = x >>= flip orM1 y
+{-# INLINABLE orM #-}
 
 orM1 :: Monad m => Bool -> m Bool -> m Bool
 orM1 True  _ = return True
 orM1 False x = x
+{-# INLINABLE orM1 #-}
 
 allM :: Monad m => [m Bool] -> m Bool
 allM = foldM andM1 True
+{-# INLINABLE allM #-}
 
 anyM :: Monad m => [m Bool] -> m Bool
 anyM = foldM orM1 False
+{-# INLINABLE anyM #-}
 
 timesM :: Monad m => (a -> m a) -> Int -> a -> m a
 timesM f = go
@@ -42,17 +53,22 @@ timesM f = go
   go n _ | n < 0 = error "timesM negative iteration count"
   go 0 a = return a
   go n a = f a >>= go (n-1)
+{-# INLINABLE timesM #-}
 
 mapForallM, mapExistsM :: (Monad m)
                        => (k -> v -> m Bool) -> M.Map k v -> m Bool
 mapForallM p m = M.foldrWithKey (\k v -> (andM $ p k v)) (return True ) m
 mapExistsM p m = M.foldrWithKey (\k v -> (orM  $ p k v)) (return False) m
+{-# INLINABLE mapForallM #-}
+{-# INLINABLE mapExistsM #-}
 
 setForallM, setExistsM :: (Monad m)
                        => (e -> m Bool) -> S.Set e -> m Bool
 setForallM p m = S.foldr (\e -> (andM $ p e)) (return True ) m
 setExistsM p m = S.foldr (\e -> (orM  $ p e)) (return False) m
 
+{-# INLINABLE setForallM #-}
+{-# INLINABLE setExistsM #-}
 
 bracketState :: (MonadState s m) => s -> m t -> m (t, s)
 bracketState bs m = do
@@ -62,10 +78,54 @@ bracketState bs m = do
  s' <- get
  put s
  return (r, s')
+{-# INLINABLE bracketState #-}
 
-incState :: (Num a, MonadState a m) => m a
-incState = id <<%= (+1)
+-- | Increment a state field given its lens.  Often you will just want to
+-- pass "id" or something similarly simple (frequently much shorter than
+-- the type given here!)
+incState :: (Enum a, MonadState s m)
+         => Overloading (->) (->) ((,) a) s s a a
+         -> m a
+incState = (<<%= succ)
+{-# INLINABLE incState #-}
 
 readState :: (MonadState a m) => ReaderT a m b -> m b
 readState x = get >>= runReaderT x
+{-# INLINABLE readState #-}
+
+-- | Often we want to check a set cache for membership, returning 'mempty'
+-- if so, or assume this case and run some action to obtain a result.
+--
+-- This is used, for example, in cycle-breaking in backward chaining, often,
+-- using 'True' for the cycle-break value, we assume the provability of our
+-- assumption and continue to look for a counter-argument.  Note that this
+-- is rather the opposite of most circular systems, where 'False' would be
+-- the cycle-break value.
+trySetCache :: (Ord e, MonadState s m)
+             => Simple Lens s (S.Set e)
+             -> r
+             -> (e -> m r)
+             -> e
+             -> m r
+trySetCache l cyc miss e = do
+  h <- (uses l $ S.member e)
+  if h
+   then return cyc
+   else l %= S.insert e >> miss e
+{-# INLINABLE trySetCache #-}
 
+
+-- | Like 'trySetCache' but a version that actually, well, caches a value.
+-- This depends on us being able to store something into the map before
+-- actually recursing and does not update the map while recursing (the
+-- callbacks may of course do so at their own risk).
+tryMapCache :: (Ord e, MonadState s m)
+            => Simple Lens s (M.Map e a)
+            -> (e -> m a)                   -- On miss, construct @a@
+            -> (e -> a -> m ())             -- Now, given @a@, recurse
+            -> e
+            -> m a
+tryMapCache l mk rec e = use (l . at e) >>= maybe miss return
+ where
+  miss = mk e >>= \k -> (l . at e .= Just k) >> rec e k >> return k
+{-# INLINABLE tryMapCache #-}