smcdel-1.3.0: bench/diningcrypto.hs
{- |
This benchmark uses "SMCDEL.Examples.DiningCrypto".
We use it to compare the performance of SMCDEL with the temporal epistemic logic
model checker [MCMAS](https://vas.doc.ic.ac.uk/software/mcmas/) from
- "Alessio Lomuscio, Hongyang Qu, Franco Raimondi (2015):
/MCMAS: an open-source model checker for the verification of multi-agent systems/.
<https://doi.org/10.1007/s10009-015-0378-x>
There the following statement was checked:
"If cryptographer 1 did not pay the bill, then after the announcements are
made, he knows that no cryptographers paid, or that someone paid, but in
this case he does not know who did."
Wwe formalize the same statement in DEL as:
\[
\lnot p_1
\rightarrow
[!? \psi]
\left(
K_1 ( \bigwedge_{i=1}^n \lnot p_i )
\lor
\left(
K_1 ( \bigvee_{i=2}^n p_i )
\land
\bigwedge_{i=2}^n ( \lnot K_1 p_i )
\right)
\right)
\]
where \(p_i\) says that agent \(i\) paid and \(!? \psi\) is the public announcement
whether the number of agents which announced a \(1\) is odd or even, i.e.
\(\psi := \bigoplus_i \bigoplus \{ p \mid \text{Agent $i$ can observe $p$} \}\).
To run this benchmark, run @stack bench smcdel:bench:bench-diningcrypto@ in the SMCDEL folder.
The program outputs a table like the following, showing in five columns
(i) the number of cryptographers,
(ii) the number of propositions used,
(iii) the length of the knowledge structure,
(iv) the length of the formula and
(v) the time in seconds needed by SMCDEL to check it.
> n n(prps) sz(KNS) sz(frm) time
> 3 7 217 339 0.1372
> 4 11 332 477 0.0005
> 5 16 483 645 0.0008
> 10 56 1654 1847 0.0023
> 20 211 6497 6289 0.0091
> 30 466 14572 13419 0.0283
> 40 821 25747 23149 0.0619
> 50 1276 40850 36031 0.1212
> 60 1831 59890 52071 0.2229
> 70 2486 82330 70911 0.4160
> 80 3241 108170 92551 0.7038
> 90 4096 137410 116991 1.0987
MCMAS needs more than 10 seconds to check the interpreted system for 50 or
more dining cryptographers (see Lomuscio et al., Table 4), but SMCDEL can
deal with the DEL model of up to 160 agents in less time.
Note however, that the DEL model we use is less detailed than the temporal
model. In particular, we take synchronous perfect recall for granted and
merge all broadcasts done by different agents into one public announcement.
Note that the result for three agents is slower just because we compute it
first. The same happens if we start at four or five agents. The reason is
that initializing the BDD package takes some time, but is done only once.
-}
module Main (main) where
import Control.Monad (when)
import Data.Time (diffUTCTime,getCurrentTime,NominalDiffTime)
import System.Environment (getArgs)
import System.IO (hSetBuffering,BufferMode(NoBuffering),stdout)
import Text.Printf
import SMCDEL.Language
import SMCDEL.Symbolic.S5
import SMCDEL.Examples.DiningCrypto
-- | The formula to be checked.
-- Note that this formula is different from the one in "SMCDEL.Examples.DiningCrypto"
benchDcCheckForm :: Int -> Form
benchDcCheckForm n =
PubAnnounceW (Xor [genDcReveal n i | i<-[1..n] ]) $
-- pubAnnounceWhetherStack [ genDcReveal n i | i<-[1..n] ] $ -- slow!
Impl (Neg (PrpF $ P 1)) $
Disj [ K "1" (Conj [Neg $ PrpF $ P k | k <- [1..n] ])
, Conj [ K "1" (Disj [ PrpF $ P k | k <- [2..n] ])
, Conj [ Neg $ K "1" (PrpF $ P k) | k <- [2..n] ] ] ]
benchDcValid :: Int -> Bool
benchDcValid n = validViaBdd (genDcKnsInit n) (benchDcCheckForm n)
dcTimeThis :: Int -> IO NominalDiffTime
dcTimeThis n = do
start <- getCurrentTime
let mykns@(KnS props _ _) = genDcKnsInit n
putStr $ show (length props) ++ "\t"
putStr $ show (length $ show mykns) ++ "\t"
putStr $ show (length $ show $ benchDcCheckForm n) ++ "\t"
if benchDcValid n then do
end <- getCurrentTime
return (end `diffUTCTime` start)
else
error "Wrong result."
mainLoop :: [Int] -> Int -> IO ()
mainLoop [] _ = putStrLn ""
mainLoop (n:ns) limit = do
putStr $ show n ++ "\t"
result <- dcTimeThis n
printf "%.4f\n" (realToFrac result :: Double)
when (result <= fromIntegral limit) $ mainLoop ns limit
main :: IO ()
main = do
args <- getArgs
hSetBuffering stdout NoBuffering
limit <- case args of
[aInteger] | [(n,_)] <- reads aInteger -> return n
_ -> do
putStrLn "No maximum runtime given, defaulting to one second."
return 1
putStrLn $ "n" ++ "\tn(prps)"++ "\tsz(KNS)"++ "\tsz(frm)" ++ "\ttime"
mainLoop (3:4:(5 : map (10*) [1..])) limit