packages feed

obdd 0.3.3 → 0.4.0

raw patch · 7 files changed

+381/−16 lines, 7 filesdep +processdep ~basedep ~containersPVP ok

version bump matches the API change (PVP)

Dependencies added: process

Dependency ranges changed: base, containers

API changes (from Hackage documentation)

- OBDD.Data: instance (GHC.Classes.Eq v, GHC.Classes.Eq i) => GHC.Classes.Eq (OBDD.Data.Node v i)
- OBDD.Data: instance (GHC.Classes.Ord v, GHC.Classes.Ord i) => GHC.Classes.Ord (OBDD.Data.Node v i)
+ OBDD: display :: Show v => OBDD v -> IO ()
+ OBDD.Data: display :: Show v => OBDD v -> IO ()
+ OBDD.Data: instance (GHC.Classes.Eq i, GHC.Classes.Eq v) => GHC.Classes.Eq (OBDD.Data.Node v i)
+ OBDD.Data: instance (GHC.Classes.Ord i, GHC.Classes.Ord v) => GHC.Classes.Ord (OBDD.Data.Node v i)
+ OBDD.Make: false :: Ord v => OBDD v
+ OBDD.Make: true :: Ord v => OBDD v
+ OBDD.Make: variable :: Ord v => v -> OBDD v
+ OBDD.Operation: bool :: Ord v => OBDD v -> OBDD v -> OBDD v -> OBDD v
+ OBDD.Operation: equiv :: Ord v => OBDD v -> OBDD v -> OBDD v
+ OBDD.Operation: implies :: Ord v => OBDD v -> OBDD v -> OBDD v
+ OBDD.Operation: infixr 2 ||
+ OBDD.Operation: infixr 3 &&
+ OBDD.Operation: instance GHC.Show.Show OBDD.Operation.Symmetricity
+ OBDD.Operation: xor :: Ord v => OBDD v -> OBDD v -> OBDD v
- OBDD.Data: size :: OBDD v -> Int
+ OBDD.Data: size :: OBDD v -> Index
- OBDD.Property: size :: OBDD v -> Int
+ OBDD.Property: size :: OBDD v -> Index

Files

+ examples/Queens2.hs view
@@ -0,0 +1,69 @@+{-+the N Queens problem (alternative implementation).+The propositional variables+correspond to the positions on the board.+It shows how to construct an OBDD+and how to check some of its properties.+It also shows that the implementation is not terribly efficient.+It computes the number of solutions for board size 8+(the answer is: 92) in approx. 1.6 seconds on my machine.++BUILD:  ghc -O2 Queens+RUN  :  ./Queens 8+-}++import Prelude hiding ((&&),(||),not,and,or)+import OBDD ++import Control.Monad ( guard )+import System.Environment ( getArgs )+import qualified Data.Set +import qualified Data.Map.Strict as M++type Position = (Int,Int)++positions :: Int -> [ Position ]+positions n = do +    a <- [ 1 .. n ]+    b <- [ 1 .. n ]+    return (a,b)++board :: Int -> OBDD Position+board n = and +    [ handle exactlyone (\(x,y) -> x) n+    , handle atmostone  (\(x,y) -> y) n+    , handle atmostone  (\(x,y) -> x+y) n+    , handle atmostone  (\(x,y) -> x-y) n+    ]++atmostone xs =+  let go (n,o) [] = n || o+      go (n,o) (x:xs) = go (bool n false x, bool o n x) xs+  in  go (true,false) xs++exactlyone xs =+  let go (n,o) [] = o+      go (n,o) (x:xs) = go (bool n false x, bool o n x) xs+  in  go (true,false) xs++handle check f n = OBDD.and $ do+    (k,v) <- M.toList $ M.fromListWith (++)+          $ map (\p -> (f p, [variable p])) $ positions n+    return $ check v++main = do+    args <- getArgs+    case map read args :: [Int] of+        [] -> mainf 8+        [arg] -> mainf arg++mainf n = do+    let d :: OBDD Position+        d = board n+    putStrLn $ unwords [ "board size", show n ]+    putStrLn $ unwords [ "BDD size", show $ OBDD.size d ]+    putStrLn $ unwords [ "number of models"+                       , show $ OBDD.number_of_models +                         ( Data.Set.fromList $ positions n )+                         d+                       ]
+ examples/Sort.hs view
@@ -0,0 +1,234 @@+{-# language LambdaCase #-}++import Prelude hiding ((&&),(||),not,and,or,Num)+import qualified Prelude+import qualified Data.Bool +import OBDD hiding (size)+import qualified OBDD as O++import Control.Monad ( guard, forM_, when, void, mzero, msum )+import System.Environment ( getArgs )+import System.IO (hFlush, stdout)+import qualified Data.Set as S+import qualified Data.Map.Strict as M+import Data.List (sort, sortOn, tails, transpose)+import qualified Data.Tree as T+import Data.Maybe (isJust)++import Debug.Trace++-- | we will talk about permutation matrices,+-- so we need to index their elements.+type Bit = OBDD (Int,Int)++ispermutation :: [[Bit]] -> Bit+ispermutation xss =+     ( and $ map exactlyone xss )+  && ( and $ map exactlyone $ transpose xss )++exactlyone :: [Bit] -> Bit+exactlyone xs =+  let go (n,o) [] = o+      go (n,o) (x:xs) = go (bool n false x, bool o n x) xs+  in  go (true,false) xs++-- | (weakly) increasing sequence of bits+type Num = [Bit] ++-- | produce a number from a sequence that has exactly one bit set.+number :: [Bit] -> [Bit]+number (x:xs) = scanl (||) x xs++lt :: Num -> Num -> Bit+lt xs ys = or $ zipWith (\x y -> not x && y) xs ys++leq :: Num -> Num -> Bit+leq xs ys = and $ zipWith implies xs ys++type Comp = (Int,Int)++comparators :: Int -> [Comp]+comparators w =+  [0 .. w-2] >>= \ x -> [x+1..w-1] >>= \ y -> [(x,y)]++compat :: [Num] -> Comp -> Bit+compat ns (lo,hi) = leq (ns !! lo) (ns !! hi)++input w = do+  i <- [1..w]+  return $ map (\j -> variable (i,j))[1..w]++vars w = S.fromList $ (,) <$> [1..w] <*> [1..w]++-- * poset enumeration++data State =+     State { comps:: ! [Comp]+           , poset :: ! Poset+           , args :: ! [Num]+           , form :: ! Bit+           , size :: ! Integer+           }++start w =+  let i = input w+      f = ispermutation i+  in State { comps = []+           , poset = mkposet []+           , args = map number i+           , form = f+           , size = number_of_models (vars w) f+           }++next :: Int -> State -> Comp -> State+next w s c =+  let cs' = c : comps s+      f = compat (args s) c && form s+  in  s { comps = cs'+        , poset = -- mkposet cs'+	    transitive_closure $ S.insert c $ poset s+        , form = f+        , size = number_of_models (vars w) f+        }++run w d = do+  putStrLn $ unwords [ "sort", show w, "items", "with", show d, "comparisons" ]+  (r, cache) <- work w d (start w) M.empty+  putStrLn ""+  putStrLn $ unwords [ "sort", show w, "items", "with", show d, "comparisons", "is"+     , Data.Bool.bool "IMPOSSIBLE" "POSSIBLE" r ]+  putStrLn $ unwords [ "cache", "with", show (M.size cache), "entries" ]+  when False $ forM_ (M.toList cache) $ \(k,v) -> do+    putStrLn $ unwords [ show k, "=>", show v ]+    -- forM_ (M.toList m) print+  return r++work w d s known = do+  -- print (d,comps s,size s)+  if size s == 1+    then return (True,known)+    else if size s > 2^d+         then return (False,known)+         else do+	   let verbose = False+           case M.lookup (canonical $ poset s) known of+             Just (r,prev) -> do+               if verbose+	         then putStrLn $ unwords [ show d, show $ size s, show (comps s)+                                  , show r, "iso", show prev ]+		 else putStr "!"+               return (r,known)+             Nothing -> do+               let go [] known = return (False, known)+                   go (c@(x,y):cs) known = do+		     let [s1,s2] = reverse+		                 $ sortOn size+		                 $ map (next w s) [ (x,y), (y,x) ]+                     (a1,k1) <- work w (d-1) s1 known+                     if a1+                       then do+                         (a2,k2) <- work w (d-1) s2 k1+                         if a2+                           then return (True, k2)+                           else go cs k2+                       else do+                         go cs k1+               let candidates =+                     filter (\ (x,y) -> Prelude.not $ S.member (x,y) $ poset s)+                     $ filter (\ (x,y) -> Prelude.not $ S.member (y,x) $ poset s)+                      $ comparators w+               (r,known) <- go candidates known+               if verbose+	         then putStrLn $ unwords [ show d, show $ size s, show (comps s)+                                  , show r ]+                 else putStr "." +  	       hFlush stdout	 +               return+                 (r, M.insert (canonical $ poset s) (r, comps s) known)++-- * main++main = getArgs >>= \ case+  [ ] -> void $ run 4 5+  [ w ] -> let b = ceiling+                 $ logBase 2 $ fromIntegral+                 $ factorial $ read w+           in -- search (read w) b+	       void $ run (read w) b+  [ w , d ] -> void $ run (read w) (read d)+++search w d = run w d >>= \ case+  True -> return ()+  False -> search w (d+1)+  +factorial n = product [1 .. n]++-- * posets and their isomorphisms++type Poset = S.Set Comp++mkposet comps = transitive_closure $ S.fromList comps++dot :: Poset -> Poset -> Poset+dot p q = S.fromList $ do+  (x,y1) <- S.toList p+  (y2,z) <- S.toList q+  guard $ y1 == y2+  return (x,z)++transitive_closure :: Poset -> Poset+transitive_closure p =+  let q = S.union p $ dot p p+  in  if p == q then p else transitive_closure q+      +inputs  p x = map fst $ filter ((== x) . snd) $ S.toList p+outputs p x = map snd $ filter ((== x) . fst) $ S.toList p++elements p = S.union ( S.map fst p ) (S.map snd p )++-- | the Int is the length, and it is used to speed up+-- the derived Eq and Ord instance.+data List a = List !Int ![a] deriving (Eq, Ord, Show)++nil :: List a+nil = List 0 []++cons :: a -> List a -> List a+cons x (List n xs) = List (n+1) (x:xs)++list :: [a] -> List a+list xs = List (length xs) xs++instance Functor List where+  fmap f (List n xs) = List n (map f xs)++data Type = Dot | Type (List Type) (List Type) deriving (Eq, Ord, Show)++types :: Poset -> M.Map Int Type+types p = M.fromList $ zip (S.toList $ elements p) $ repeat Dot++refine :: Poset ->   M.Map Int Type -> M.Map Int Type+refine p t = M.fromList $ do+  x <- S.toList $ elements p+  return (x, Type ( list $ sort $ map (t M.!) $ inputs  p x )+                  ( list $ sort $ map (t M.!) $ outputs p x ) )++classes :: M.Map Int Type -> M.Map Type (S.Set Int)+classes m = M.fromListWith S.union $ do+  (k,v) <- M.toList m+  return (v, S.singleton k)++-- | compare with keys+essence t = M.toAscList $ M.map S.size $ classes t++canonical po =+  let go t p =+        let t' = refine po t+            c' = classes t+            p' = sort $ M.elems c'+        in  if p == p' then M.map S.size c' else go t' p'+  in  go (types po) []+++               
obdd.cabal view
@@ -1,5 +1,5 @@ Name:                obdd-Version:             0.3.3+Version:             0.4.0 Cabal-Version:       >= 1.8 Build-type: Simple Synopsis:            Ordered Reduced Binary Decision Diagrams@@ -17,7 +17,7 @@     Location: git://github.com/jwaldmann/haskell-obdd.git  Library-    Build-Depends:       base==4.*, random, mtl, containers>=0.5, array+    Build-Depends:       base==4.*, random, mtl, containers>=0.5, array, process     Hs-Source-Dirs:	     src     Exposed-Modules:     OBDD OBDD.Data OBDD.Make OBDD.Operation OBDD.Property     Other-Modules:	     OBDD.IntIntMap, OBDD.VarIntIntMap@@ -35,4 +35,16 @@     Main-Is: Queens.hs     Build-Depends: base, containers, obdd +test-suite obdd-queens2+    Hs-Source-Dirs : examples+    Type: exitcode-stdio-1.0+    Main-Is: Queens2.hs+    Build-Depends: base, containers, obdd+    +test-suite obdd-sort+    Hs-Source-Dirs : examples+    Type: exitcode-stdio-1.0+    Main-Is: Sort.hs+    Build-Depends: base, containers, obdd+    Ghc-Options: -rtsopts     
src/OBDD.hs view
@@ -1,16 +1,17 @@--- | reduced ordered binary decision diagrams+-- | Reduced ordered binary decision diagrams,+-- pure Haskell implementation. -- (c) Johannes Waldmann, 2008 ----- this module is intended to be imported qualified+-- This module is intended to be imported qualified -- because it overloads some Prelude names. ----- for a similar, but much more elaborate project, see+-- For a similar, but much more elaborate project, see -- <http://www.informatik.uni-kiel.de/~mh/lehre/diplomarbeiten/christiansen.pdf> -- but I'm not sure where that source code would be available.  module OBDD  -( OBDD +( OBDD, display , module OBDD.Property , module OBDD.Operation , module OBDD.Make@@ -18,7 +19,7 @@  where -import OBDD.Data ( OBDD )+import OBDD.Data ( OBDD, display ) import OBDD.Property import OBDD.Operation import OBDD.Make
src/OBDD/Data.hs view
@@ -15,7 +15,7 @@ , number_of_models , some_model, all_models , fold, foldM-, toDot+, toDot, display -- * for internal use , Node (..) , make@@ -49,9 +49,10 @@    (State, runState, evalState, get, put, gets, modify) import qualified System.Random import Control.Monad.Fix-import Control.Monad ( forM, guard )+import Control.Monad ( forM, guard, void ) import qualified Control.Monad ( foldM )-+import System.Process+import Data.List (isPrefixOf, isSuffixOf)  import Prelude hiding ( null ) import qualified Prelude@@ -277,6 +278,11 @@       register n     _ -> register n +-- | Calls the @dot@ executable (must be in @$PATH@) to draw a diagram+-- in an X11 window. Will block until this window is closed.+-- Window can be closed gracefully by typing  'q'  when it has focus.+display :: Show v => OBDD v -> IO ()+display d = void $ readProcess "dot" [ "-Tx11" ] $ toDot d  -- | toDot outputs a string in format suitable for input to the "dot" program -- from the graphviz suite.@@ -296,7 +302,10 @@                           _ -> idmap IM.! i         in id &&& getNode -    mkLabel lbl = "[label=\"" ++ lbl ++ "\"];"+    unquote s = if isPrefixOf "\"" s && isSuffixOf "\"" s+                then init $ tail s+                else s+    mkLabel lbl = "[label=\"" ++ unquote lbl ++ "\"];"      helper (thisId, Leaf b) = return $         -- switch to rectangle nodes for the leaf, before going back to ovals.
src/OBDD/Make.hs view
@@ -2,7 +2,7 @@  module OBDD.Make  -( constant, unit )+( constant, unit, variable, false, true )  where @@ -15,6 +15,12 @@ constant b = make $ do     register $ Leaf b +false :: Ord v => OBDD v+false = constant False++true :: Ord v => OBDD v+true  = constant True+ -- | Variable with given parity unit :: Ord v => v -> Bool -> OBDD v unit v p = make $ do@@ -22,3 +28,5 @@     r <- register $ Leaf $     p     register $ Branch v l r +variable :: Ord v => v -> OBDD v+variable v = unit v True
src/OBDD/Operation.hs view
@@ -1,8 +1,10 @@ {-# language ScopedTypeVariables #-}+{-# language PatternGuards #-}  module OBDD.Operation   ( (&&), (||), not, and, or+, bool, implies, equiv, xor , unary, binary , instantiate , exists, exists_many@@ -23,17 +25,31 @@ -- import Data.List ( foldl' ) -- don't use, see below -import Prelude hiding ( (&&), (||), and, or, not )+import Prelude hiding ( (&&), (||), and, or, not, bool ) import qualified Prelude +infixr 3 &&+ ( && ) :: Ord v => OBDD v -> OBDD v -> OBDD v-( && ) = binary ( Prelude.&& )+( && ) = symmetric_binary ( Prelude.&& ) +infixr 2 ||+ ( || ) :: Ord v => OBDD v -> OBDD v -> OBDD v-( || ) = binary ( Prelude.|| )+( || ) = symmetric_binary ( Prelude.|| ) +bool :: Ord v => OBDD v -> OBDD v -> OBDD v -> OBDD v+bool f t p = (f && not p) || (t && p) +equiv :: Ord v => OBDD v -> OBDD v -> OBDD v+equiv = symmetric_binary (==) +xor :: Ord v => OBDD v -> OBDD v -> OBDD v+xor   = symmetric_binary (/=)++implies :: Ord v => OBDD v -> OBDD v -> OBDD v+implies = binary ( <= )+ and :: Ord v => [ OBDD v ] -> OBDD v and = fold_by_size (constant True) (&&) -- and = foldr ( && ) ( constant True ) @@ -75,11 +91,27 @@     handle x  +data Symmetricity = Asymmetric | Symmetric deriving Show+ binary :: Ord v       => ( Bool -> Bool -> Bool )       -> OBDD v -> OBDD v -> OBDD v-binary op x y = make $ do+binary = binary_ Asymmetric+      +symmetric_binary :: Ord v+      => ( Bool -> Bool -> Bool )+      -> OBDD v -> OBDD v -> OBDD v+symmetric_binary = binary_ Symmetric+++binary_ :: Ord v+      => Symmetricity+      -> ( Bool -> Bool -> Bool )+      -> OBDD v -> OBDD v -> OBDD v++binary_ sym op x y = make $ do     let -- register = checked_register -- for testing+        -- handle x y | Symmetric <- sym, top x > top y = handle y x         handle x y = cached (top x, top y) $ case ( access x , access y ) of                     ( Leaf p , Leaf q ) -> register $ Leaf $ op p q                     ( ax, ay ) -> case comp ax ay of