packages feed

abcBridge-0.15: tests/Tests/Basic.hs

{-# LANGUAGE PatternGuards #-}
module Tests.Basic
  ( basic_tests
  ) where

import Control.Exception
import Control.Monad.Compat
import System.Directory
import System.IO
import Prelude ()
import Prelude.Compat

import Test.Tasty
import Test.Tasty.HUnit as HU
import Test.Tasty.QuickCheck

import qualified Data.ABC as ABC
import qualified Data.AIG.Trace as Tr

tryIO :: IO a -> IO (Either IOException a)
tryIO = try

basic_tests :: Tr.Traceable l => ABC.Proxy l g -> [TestTree]
basic_tests proxy@(ABC.Proxy f) = f $
  [ testCase "test_true" $ do
      ABC.SomeGraph g <- ABC.newGraph proxy
      let n = ABC.Network g [ABC.trueLit g]
      assertEqual "test_true" [True] =<< ABC.evaluate n []
  , testCase "test_false" $ do
      ABC.SomeGraph g <- ABC.newGraph proxy
      let n = ABC.Network g [ABC.falseLit g]
      assertEqual "test_false" [False] =<< ABC.evaluate n []
  , testProperty "test_constant"$ \b -> ioProperty $do
      ABC.SomeGraph g <- ABC.newGraph proxy
      let n = ABC.Network g [ABC.constant g b]
      (==[b]) <$> ABC.evaluate n []
  , testProperty "test_not" $ \b0 -> ioProperty $ do
      ABC.SomeGraph g <- ABC.newGraph proxy
      i0 <- ABC.newInput g
      let n = ABC.Network g [ABC.not i0]
      r <- ABC.evaluate n [b0]
      return $ r == [not b0]
  , testProperty "test_and" $ \b1 b2 -> ioProperty $ do
      ABC.SomeGraph g <- ABC.newGraph proxy
      i0 <- ABC.newInput g
      i1 <- ABC.newInput g
      x <- ABC.and g i0 i1
      let n = ABC.Network g [x]
      r <- ABC.evaluate n [b1, b2]
      return $ r == [b1 && b2]
  , testProperty "test_xor" $ \b1 b2 -> ioProperty $ do
      ABC.SomeGraph g <- ABC.newGraph proxy
      i0 <- ABC.newInput g
      i1 <- ABC.newInput g
      x <- ABC.xor g i0 i1
      let n = ABC.Network g [x]
      r <- ABC.evaluate n [b1, b2]
      return $ r == [b1 /= b2]
  , testProperty "test_mux" $ \b0 b1 b2 -> ioProperty $ do
      ABC.SomeGraph g <- ABC.newGraph proxy
      i0 <- ABC.newInput g
      i1 <- ABC.newInput g
      i2 <- ABC.newInput g
      o <- ABC.mux g i0 i1 i2

      let n = ABC.Network g [o]
      r <- ABC.evaluate n [b0, b1, b2]
      return $ r == [if b0 then b1 else b2]
  , testCase "test_cec" $ do
     r <- join $ ABC.cec <$> cecNetwork proxy <*> cecNetwork' proxy
     assertEqual "test_cec" (ABC.Invalid (toEnum <$> [0,0,0,1,0,0,0])) r
  , testCase "test_aiger" $ do
      -- XXX: cwd unfriendly
      n1 <- ABC.aigerNetwork proxy "tests/eijk.S298.S.aig"
      tmpdir <- getTemporaryDirectory
      (path, hndl) <- openTempFile tmpdir "aiger.aig"
      hClose hndl
      ABC.writeAiger path n1
      n2 <- ABC.aigerNetwork proxy path
      assertEqual "test_aiger" ABC.Valid =<< ABC.cec n1 n2
      removeFile path

  , testProperty "unfold_fold" $ \litForest -> ioProperty $ do
      let maxInput = foldr max 0 $ map ABC.getMaxInput litForest

      n1@(ABC.Network g ls) <- ABC.buildNetwork proxy litForest

      litForest' <- ABC.toLitForest g ls

      -- NB: we cannot just compare litForest and litForest' for syntactic equality
      -- due to simplifications performed when building the AIG.  Also, the following
      -- commented line does not work because references to inputs may also be removed
      -- during simpification, resulting in a different number of inputs.
      --n2 <- ABC.buildNetwork proxy litForest'

      -- so do this instead...
      (ABC.SomeGraph g') <- ABC.newGraph proxy
      forM_ [0 .. maxInput] (\_ -> ABC.newInput g')
      ls' <- ABC.fromLitForest g' litForest'
      let n2 = ABC.Network g' ls'

      result <- ABC.cec n1 n2
      return $ result == ABC.Valid

  , testCase "fold_unfold" $ do
      (ABC.Network g l) <- cecNetwork proxy
      inputs <- ABC.inputCount g
      litForest <- ABC.toLitForest g l

      (ABC.SomeGraph g') <- ABC.newGraph proxy
      forM_ [0 .. inputs-1] (\_ -> ABC.newInput g')
      l' <- ABC.fromLitForest g' litForest

      assertEqual "fold_unfold" ABC.Valid =<< ABC.cec (ABC.Network g l) (ABC.Network g' l')

  , testCase "bad_aiger" $ do
      me <- tryIO $ ABC.aigerNetwork proxy "Nonexistent AIGER!"
      case me of
        Left{} -> return ()
        Right{} -> fail "Expected error when opening AIGER"
  , testCase "test_sat" $ do
     ABC.SomeGraph g <- ABC.newGraph proxy
     rt <- ABC.checkSat g (ABC.trueLit g)
     case rt of
       ABC.Sat{} -> return ()
       ABC.Unsat{} -> fail "trueLit is unsat"
       ABC.SatUnknown{} -> fail "trueLit is unknown"
     rf <- ABC.checkSat g (ABC.falseLit g)
     case rf of
       ABC.Sat{} -> fail "falseLit is sat"
       ABC.Unsat{} -> return ()
       ABC.SatUnknown{} -> fail "falseLit is unknown"

  , testCase "aiger_twice" $ do
      ABC.SomeGraph g <- ABC.newGraph proxy

      tmpdir <- getTemporaryDirectory
      (path, hndl) <- openTempFile tmpdir "aiger.aig"
      hClose hndl

      x <- ABC.newInput g

      ABC.writeAiger (path++"1") (ABC.Network g [ABC.falseLit g, ABC.falseLit g])

      y <- ABC.newInput g
      r <- ABC.and g x y

      ABC.writeAiger (path++"2") (ABC.Network g [r])

  , testCase "aiger_eval" $ do
      ABC.SomeGraph g <- ABC.newGraph proxy

      tmpdir <- getTemporaryDirectory
      (path, hndl) <- openTempFile tmpdir "aiger.aig"
      hClose hndl

      x <- fmap ABC.bvFromList $ sequence $ replicate 32 (ABC.newInput g)
      y <- ABC.zipWithM (ABC.lAnd' g) x (ABC.bvFromInteger g 32 0x12345678)

      ABC.writeAiger path (ABC.Network g (ABC.bvToList y))

      let tobool :: Int -> Bool
          tobool i = if i == 0 then False else True

      let inputs = map tobool $ reverse $
                   [ 0,1,1,0,1,0,0,0,
                     0,0,0,0,0,0,0,0,
                     0,0,0,0,0,0,0,0,
                     0,0,0,0,0,0,0,0 ]

      let outputs = fmap tobool $ reverse $
                    [ 0,0,0,0,1,0,0,0,
                      0,0,0,0,0,0,0,0,
                      0,0,0,0,0,0,0,0,
                      0,0,0,0,0,0,0,0 ]

      z <- ABC.evaluate (ABC.Network g (ABC.bvToList y)) inputs

      assertEqual "aiger_eval" outputs z

  , testCase "lit_view" $ do
       ABC.SomeGraph g <- ABC.newGraph proxy
       i  <- ABC.newInput g
       lv <- ABC.litView g i
       x  <- ABC.newInput g
       xv <- ABC.litView g x
       o  <- ABC.and g i x
       lo <- ABC.litView g o

       case lv of
         ABC.Input 0 -> return ()
         _ -> fail "expected input 0"

       case xv of
         ABC.Input 1 -> return ()
         _ -> fail "expected input 1"

       case lo of
         ABC.And a1 a2
           | a1 ABC.=== i, a2 ABC.=== x -> return ()
         _ -> fail "expected and literal"
  ]

cecNetwork :: ABC.IsAIG l g => ABC.Proxy l g -> IO (ABC.Network l g)
cecNetwork proxy = do
  ABC.SomeGraph g <- ABC.newGraph proxy
  [n2, n3, n4, n5, n6, n7, n8] <- replicateM 7 $ ABC.newInput g

  n14 <- ABC.ands g [ ABC.not n2
                    , ABC.not n3
                    , ABC.not n4
                    , n5
                    , ABC.not n6
                    , ABC.not n7
                    , ABC.not n8
                    ]
  let r = [n14] ++ replicate 6 (ABC.falseLit g)
  return (ABC.Network g r)

cecNetwork' :: ABC.IsAIG l g => ABC.Proxy l g -> IO (ABC.Network l g)
cecNetwork' proxy = do
  ABC.SomeGraph g <- ABC.newGraph proxy
  replicateM_ 7 $ ABC.newInput g
  let r = replicate 7 $ ABC.falseLit g
  return (ABC.Network g r)