{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveTraversable #-}
module Data.AIG.Interface
(
IsLit(..)
, IsAIG(..)
, lazyMux
, Proxy(..)
, SomeGraph(..)
, Network(..)
, networkInputCount
, networkOutputCount
, LitView(..)
, negateLitView
, LitTree(..)
, toLitTree
, fromLitTree
, toLitForest
, fromLitForest
, foldAIG
, foldAIGs
, unfoldAIG
, unfoldAIGs
, SatResult(..)
, VerifyResult(..)
, toSatResult
, toVerifyResult
, genLitView
, genLitTree
, getMaxInput
, buildNetwork
, randomNetwork
, BasicGraph
, BasicLit
, basicProxy
, newBasicGraph
) where
import qualified Prelude as Prelude
import Control.Applicative
import Control.Monad
import Data.IORef
import Prelude.Compat hiding (not, and, or, mapM)
import Test.QuickCheck (Gen, Arbitrary(..), generate, oneof, sized, choose)
data LitView a
= And !a !a
| NotAnd !a !a
| Input !Int
| NotInput !Int
| TrueLit
| FalseLit
deriving (Eq,Show,Ord,Functor,Foldable,Traversable)
negateLitView :: LitView a -> LitView a
negateLitView l = case l of
TrueLit -> FalseLit
FalseLit -> TrueLit
Input i -> NotInput i
NotInput i -> Input i
And x y -> NotAnd x y
NotAnd x y -> And x y
newtype LitTree = LitTree { unLitTree :: LitView LitTree }
deriving (Eq,Show,Ord)
class IsLit l where
not :: l s -> l s
(===) :: l s -> l s -> Bool
data Proxy l g where
Proxy :: IsAIG l g => (forall a . a -> a) -> Proxy l g
class IsLit l => IsAIG l g | g -> l where
withNewGraph :: Proxy l g
-> (forall s . g s -> IO a)
-> IO a
withNewGraph p f = newGraph p >>= (`withSomeGraph` f)
newGraph :: Proxy l g
-> IO (SomeGraph g)
newGraph p = withNewGraph p (return . SomeGraph)
aigerNetwork :: Proxy l g
-> FilePath
-> IO (Network l g)
trueLit :: g s -> l s
falseLit :: g s -> l s
constant :: g s -> Bool -> l s
constant g True = trueLit g
constant g False = falseLit g
asConstant :: g s -> l s -> Maybe Bool
asConstant g l | l === trueLit g = Just True
| l === falseLit g = Just False
| otherwise = Nothing
newInput :: g s -> IO (l s)
and :: g s -> l s -> l s -> IO (l s)
ands :: g s -> [l s] -> IO (l s)
ands g [] = return (trueLit g)
ands g (x:r) = foldM (and g) x r
or :: g s -> l s -> l s -> IO (l s)
or g x y = not <$> and g (not x) (not y)
eq :: g s -> l s -> l s -> IO (l s)
eq g x y = not <$> xor g x y
implies :: g s -> l s -> l s -> IO (l s)
implies g x y = or g (not x) y
xor :: g s -> l s -> l s -> IO (l s)
xor g x y = do
o <- or g x y
a <- and g x y
and g o (not a)
mux :: g s -> l s -> l s -> l s -> IO (l s)
mux g c x y = do
x' <- and g c x
y' <- and g (not c) y
or g x' y'
inputCount :: g s -> IO Int
getInput :: g s -> Int -> IO (l s)
writeAiger :: FilePath -> Network l g -> IO ()
writeAigerWithLatches :: FilePath -> Network l g -> Int -> IO ()
writeCNF :: g s -> l s -> FilePath -> IO [Int]
checkSat :: g s -> l s -> IO SatResult
cec :: Network l g -> Network l g -> IO VerifyResult
evaluator :: g s
-> [Bool]
-> IO (l s -> Bool)
evaluate :: Network l g
-> [Bool]
-> IO [Bool]
evaluate (Network g outputs) inputs = do
f <- evaluator g inputs
return (f <$> outputs)
litView :: g s -> l s -> IO (LitView (l s))
abstractEvaluateAIG
:: g s
-> (LitView a -> IO a)
-> IO (l s -> IO a)
foldAIG :: IsAIG l g
=> g s
-> (LitView a -> IO a)
-> l s
-> IO a
foldAIG n view l = do
eval <- abstractEvaluateAIG n view
eval l
foldAIGs :: IsAIG l g
=> g s
-> (LitView a -> IO a)
-> [l s]
-> IO [a]
foldAIGs n view ls = do
eval <- abstractEvaluateAIG n view
mapM eval ls
unfoldAIG :: IsAIG l g
=> g s
-> (a -> IO (LitView a))
-> a -> IO (l s)
unfoldAIG n unfold = f
where f = unfold >=> g
g (And x y) = and' (f x) (f y)
g (NotAnd x y) = fmap not $ and' (f x) (f y)
g (Input i) = getInput n i
g (NotInput i) = fmap not $ getInput n i
g TrueLit = return $ trueLit n
g FalseLit = return $ falseLit n
and' mx my = do
x <- mx
y <- my
and n x y
unfoldAIGs :: IsAIG l g
=> g s
-> (a -> IO (LitView a))
-> [a] -> IO [l s]
unfoldAIGs n unfold = mapM (unfoldAIG n unfold)
toLitTree :: IsAIG l g => g s -> l s -> IO LitTree
toLitTree g = foldAIG g (return . LitTree)
fromLitTree :: IsAIG l g => g s -> LitTree -> IO (l s)
fromLitTree g = unfoldAIG g (return . unLitTree)
toLitForest :: IsAIG l g => g s -> [l s] -> IO [LitTree]
toLitForest g = foldAIGs g (return . LitTree)
fromLitForest :: IsAIG l g => g s -> [LitTree] -> IO [l s]
fromLitForest g = unfoldAIGs g (return . unLitTree)
lazyMux :: IsAIG l g => g s -> l s -> IO (l s) -> IO (l s) -> IO (l s)
lazyMux g c
| c === (trueLit g) = \x _y -> x
| c === (falseLit g) = \_x y -> y
| otherwise = \x y -> join $ pure (mux g c) <*> x <*> y
data Network l g where
Network :: IsAIG l g => g s -> [l s] -> Network l g
networkInputCount :: Network l g -> IO Int
networkInputCount (Network g _) = inputCount g
networkOutputCount :: Network l g -> Int
networkOutputCount (Network _ o) = length o
data SomeGraph g where
SomeGraph :: g s -> SomeGraph g
withSomeGraph :: SomeGraph g
-> (forall s . g s -> IO a)
-> IO a
withSomeGraph (SomeGraph g) f = f g
data SatResult
= Unsat
| Sat !([Bool])
| SatUnknown
deriving (Eq,Show)
data VerifyResult
= Valid
| Invalid [Bool]
| VerifyUnknown
deriving (Eq, Show)
toVerifyResult :: SatResult -> VerifyResult
toVerifyResult Unsat = Valid
toVerifyResult (Sat l) = Invalid l
toVerifyResult SatUnknown = VerifyUnknown
toSatResult :: VerifyResult -> SatResult
toSatResult Valid = Unsat
toSatResult (Invalid l) = Sat l
toSatResult VerifyUnknown = SatUnknown
genLitView :: Gen a -> Gen (LitView a)
genLitView gen = oneof
[ return TrueLit
, return FalseLit
, sized $ \n -> choose (0,n-1) >>= \i -> return (Input i)
, sized $ \n -> choose (0,n-1) >>= \i -> return (NotInput i)
, do x <- gen
y <- gen
return (And x y)
, do x <- gen
y <- gen
return (NotAnd x y)
]
genLitTree :: Gen LitTree
genLitTree = fmap LitTree $ genLitView genLitTree
getMaxInput :: LitTree -> Int
getMaxInput (LitTree x) =
case x of
TrueLit -> 0
FalseLit -> 0
Input i -> i
NotInput i -> i
And a b -> max (getMaxInput a) (getMaxInput b)
NotAnd a b -> max (getMaxInput a) (getMaxInput b)
instance Arbitrary LitTree where
arbitrary = genLitTree
shrink (LitTree TrueLit) = []
shrink (LitTree FalseLit) = []
shrink (LitTree (Input _)) = [LitTree TrueLit, LitTree FalseLit]
shrink (LitTree (NotInput _)) = [LitTree TrueLit, LitTree FalseLit]
shrink (LitTree (And x y)) =
[ LitTree TrueLit, LitTree FalseLit, x, y ] ++
[ LitTree (And x' y') | (x',y') <- shrink (x,y) ]
shrink (LitTree (NotAnd x y)) =
[ LitTree TrueLit, LitTree FalseLit, x, y ] ++
[ LitTree (NotAnd x' y') | (x',y') <- shrink (x,y) ]
buildNetwork :: IsAIG l g => Proxy l g -> [LitTree] -> IO (Network l g)
buildNetwork proxy litForrest = do
let maxInput = foldr max 0 $ map getMaxInput litForrest
(SomeGraph g) <- newGraph proxy
forM_ [0..maxInput] (\_ -> newInput g)
ls <- fromLitForest g litForrest
return (Network g ls)
randomNetwork :: IsAIG l g => Proxy l g -> IO (Network l g)
randomNetwork proxy = generate arbitrary >>= buildNetwork proxy
newtype BasicGraph s = BasicGraph (IORef Int)
newtype BasicLit s = BasicLit LitTree
deriving (Show)
basicProxy :: Proxy BasicLit BasicGraph
basicProxy = Proxy (\x -> x)
notTree :: LitTree -> LitTree
notTree (LitTree x) = LitTree . negateLitView $ x
andTree :: LitTree -> LitTree -> LitTree
andTree (LitTree FalseLit) _ = LitTree FalseLit
andTree _ (LitTree FalseLit) = LitTree FalseLit
andTree (LitTree TrueLit) y = y
andTree x (LitTree TrueLit) = x
andTree x y = LitTree (And x y)
newBasicGraph :: IO (BasicGraph s)
newBasicGraph =
do r <- newIORef 0
return (BasicGraph r)
instance IsLit BasicLit where
not (BasicLit x) = BasicLit . notTree $ x
(BasicLit x) === (BasicLit y) = x == y
instance IsAIG BasicLit BasicGraph where
withNewGraph _proxy k = k =<< newBasicGraph
aigerNetwork _proxy _fp =
fail "Cannot read AIGER files from the BasicGraph implementation"
trueLit _g = BasicLit . LitTree $ TrueLit
falseLit _g = BasicLit . LitTree $ FalseLit
newInput (BasicGraph r) =
atomicModifyIORef' r (\n -> (n+1, BasicLit . LitTree . Input $ n))
and _g (BasicLit x) (BasicLit y) = return . BasicLit $ andTree x y
inputCount (BasicGraph r) = readIORef r
getInput _g i = return . BasicLit . LitTree . Input $ i
writeAiger _fp _ntk =
fail "Cannot write AIGER files from the BasicGraph implementation"
writeAigerWithLatches _fp _ntk _n =
fail "Cannot write AIGER files from the BasicGraph implementation"
writeCNF _g _l _fp =
fail "Cannot write CNF files from the BasicGraph implementation"
checkSat _g _l =
fail "Cannot SAT check graphs in the BasicGraph implementation"
cec _g1 _g2 =
fail "Cannot CEC graphs in the BasicGraph implementation"
asConstant _g (BasicLit bl) = go bl
where
go (LitTree l) = go' l
go' l = case l of
TrueLit -> pure True
FalseLit -> pure False
And x y -> (&&) <$> go x <*> go y
NotAnd x y -> Prelude.not <$> go' (And x y)
Input _ -> Nothing
NotInput _ -> Nothing
evaluator _g xs = return (\(BasicLit x) -> eval x)
where
eval :: LitTree -> Bool
eval (LitTree x) = eval' x
eval' :: LitView LitTree -> Bool
eval' TrueLit = True
eval' FalseLit = False
eval' (And x y) = eval x && eval y
eval' (NotAnd x y) = Prelude.not (eval' (And x y))
eval' (Input i) = case drop i xs of
(b:_) -> b
[] -> error $ unwords ["Input value out of bounds", show i]
eval' (NotInput i) = Prelude.not (eval' (Input i))
litView _ (BasicLit (LitTree x)) = return (fmap BasicLit x)
abstractEvaluateAIG _g f = return (\(BasicLit x) -> h x)
where h (LitTree x) = f =<< traverse h x