smcdel-1.3.0: src/SMCDEL/Internal/MyHaskCUDD.hs
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MonoLocalBinds #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
module SMCDEL.Internal.MyHaskCUDD (
-- * Types
Dd, B, Z, O1, O0, I1, I0, Manager,
-- * conversion functions
toB, toZ, toO1, toO0, toI1, toI0,
Convert, convertToZDD,
-- * Building and manipulating Dds
top, bot, var, --base elements
neg, swapChildNodes, --outer/output and inner/input negation
con, dis, restrict, gfp,
imp, equ, xor, ifthenelse,
exists_, forall_, forallSet, existsSet,
conSet, disSet, xorSet, restrictSet,
restrictLaw, ddSwapVars, relabelFun, relabelWith, substitSimul,
-- * BDDs only
allSats, anySat, allSatsWith,
-- * ZDD only
sub0, sub1,
makeManagerZ, swap,
-- * other supporting functions
makeManager, ite,
sparsity, getSupport, getDependentVars, size,
writeToDot, printDdInfo, returnDot,
-- * function classes
DdO, DdT, DdI, DdTOI, DdTO, DdCtx
) where
import qualified Cudd.Cudd
import System.IO.Temp (withSystemTempDirectory)
import Control.Arrow ((***))
import Data.List
import Data.Maybe (fromJust)
import Data.Bifunctor (bimap)
-- | A Binary Decision diagram with any of the five node eliminations.
newtype Dd x y z = ToDd Cudd.Cudd.DdNode deriving stock (Eq,Show)
-- | The first field indicates whether don't care nodes (B, traditional BDD rule) are removed
-- or those where "Then" leads to the 0 leaf node (Z, traditional ZDD rule).
data B
data Z
-- | The second field represents whether the 1 set (O1) of a given boolean function is represented or its complement, the 0 set (O0).
-- Because the traditional BDD rule is symetric, complementing it does not change the size of the structure.
-- Because the traditional ZDD rule is asymmetric towards the leaf nodes (and the edge types), complementing it can change the structure drastically.
-- Applying (output) negation on a traditional ZDD structure corresponds to changing the elimination rule from Then 0 to Then 1.
-- When representing a model (not) complementing the stored information is similar to either storing all possible states or all impossible states.
data O1
data O0
-- | The third field represents whether each variable is interpreted as its complement (I0) or not (I1).
-- Conversion between I1 and I0 is done by swapping the children of all nodes.
-- This changes the result of any Input assignment to the result of the complement Input assignment, and vice versa.
-- Because the traditional BDD rule is symetric, flipping its child edges does not change the size of the structure.
-- Because the traditional ZDD rule is asymmetric towards the edge types (and the leaf nodes), complementing it can change the structure drastically.
-- Applying input negation on a traditional ZDD structure corresponds to changing the elimination Then 0 to Else 0.
-- Applying both output and input negation results in the dual of the original stored boolean function.
data I1
data I0
-- | Collection of all contraints, in order to easily apply them when needed.
type DdCtx a b c = (DdTOI a b c, DdTO a b, DdT a, DdO a b, DdI a b c)
type Manager = Cudd.Cudd.DdManager
-- | Manager construction. When using ZDDs, CUDD requires initialisation of the variables beforehand.
makeManagerZ :: Int -> IO Cudd.Cudd.DdManager
makeManagerZ vocabCap = Cudd.Cudd.cuddInitZ (vocabCap +1)
-- | Manager construction when only using BDDs.
makeManager :: IO Cudd.Cudd.DdManager
makeManager = Cudd.Cudd.cuddInit
-- | functions that require the dd to be completely specified
class DdTOI a b c where
bot :: Cudd.Cudd.DdManager -> Dd a b c
top :: Cudd.Cudd.DdManager -> Dd a b c
var :: Cudd.Cudd.DdManager -> Int -> Dd a b c
restrict :: Cudd.Cudd.DdManager -> Dd a b c -> (Int, Bool) -> Dd a b c
instance DdTOI B O1 I1 where
bot mgr = ToDd (Cudd.Cudd.cuddReadLogicZero mgr)
top mgr = ToDd (Cudd.Cudd.cuddReadOne mgr)
var mgr n = ToDd (Cudd.Cudd.cuddBddIthVar mgr n)
restrict mgr bz@(ToDd zb) (n,bit) = ToDd $ Cudd.Cudd.cuddBddLICompaction mgr zb res where
ToDd res = if bit then var mgr n `asTypeOf` bz else neg mgr (var mgr n `asTypeOf` bz)
instance DdTOI B O1 I0 where
bot mgr = ToDd (Cudd.Cudd.cuddReadLogicZero mgr)
top mgr = ToDd (Cudd.Cudd.cuddReadOne mgr)
var mgr n = neg mgr $ ToDd (Cudd.Cudd.cuddBddIthVar mgr n)
restrict mgr bz@(ToDd zb) (n,bit) = ToDd $ Cudd.Cudd.cuddBddLICompaction mgr zb res where
ToDd res = if bit then var mgr n `asTypeOf` bz else neg mgr (var mgr n `asTypeOf` bz) --double negation to make the restrict S independent
instance DdTOI B O0 I1 where
bot mgr = ToDd (Cudd.Cudd.cuddReadOne mgr)
top mgr = ToDd (Cudd.Cudd.cuddReadLogicZero mgr)
var mgr n = neg mgr $ ToDd (Cudd.Cudd.cuddBddIthVar mgr n)
restrict mgr bz@(ToDd zb) (n,bit) = ToDd $ Cudd.Cudd.cuddBddLICompaction mgr zb res where
ToDd res = if bit then neg mgr (var mgr n `asTypeOf` bz) else var mgr n `asTypeOf` bz
instance DdTOI B O0 I0 where
bot mgr = ToDd (Cudd.Cudd.cuddReadOne mgr)
top mgr = ToDd (Cudd.Cudd.cuddReadLogicZero mgr)
var mgr n = neg mgr $ ToDd (Cudd.Cudd.cuddBddIthVar mgr n)
restrict mgr bz@(ToDd zb) (n,bit) = ToDd $ Cudd.Cudd.cuddBddLICompaction mgr zb res where
ToDd res = if bit then neg mgr (var mgr n `asTypeOf` bz) else var mgr n `asTypeOf` bz
instance DdTOI Z O1 I1 where
bot mgr = ToDd (Cudd.Cudd.cuddZddReadZero mgr)
top mgr = ToDd (Cudd.Cudd.cuddZddReadOne mgr)
var mgr n = ToDd (Cudd.Cudd.cuddZddIthVar mgr n)
restrict mgr zdd (n,bit) = if bit then dis mgr (sub1 mgr zdd n)(swap mgr (sub1 mgr zdd n) n) else dis mgr (sub0 mgr zdd n)(swap mgr (sub0 mgr zdd n) n)
instance DdTOI Z O1 I0 where
bot mgr = ToDd (Cudd.Cudd.cuddZddReadZero mgr)
top mgr = ToDd (Cudd.Cudd.cuddZddReadOne mgr)
var mgr n = neg mgr $ ToDd (Cudd.Cudd.cuddZddIthVar mgr n)
restrict mgr zdd (n,bit) = if bit then dis mgr (sub0 mgr zdd n)(swap mgr (sub0 mgr zdd n) n) else dis mgr (sub1 mgr zdd n)(swap mgr (sub1 mgr zdd n) n)
instance DdTOI Z O0 I1 where
bot mgr = ToDd (Cudd.Cudd.cuddZddReadOne mgr)
top mgr = ToDd (Cudd.Cudd.cuddZddReadZero mgr)
var mgr n = neg mgr $ ToDd (Cudd.Cudd.cuddZddIthVar mgr n)
restrict mgr zdd (n,bit) = if bit then con mgr (sub1 mgr zdd n)(swap mgr (sub1 mgr zdd n) n) else con mgr (sub0 mgr zdd n)(swap mgr (sub0 mgr zdd n) n)
instance DdTOI Z O0 I0 where
bot mgr = ToDd (Cudd.Cudd.cuddZddReadOne mgr)
top mgr = ToDd (Cudd.Cudd.cuddZddReadZero mgr)
var mgr n = ToDd (Cudd.Cudd.cuddZddIthVar mgr n)
restrict mgr zdd (n,bit) = if bit then con mgr (sub0 mgr zdd n)(swap mgr (sub0 mgr zdd n) n) else con mgr (sub1 mgr zdd n)(swap mgr (sub1 mgr zdd n) n)
-- | functions where B/Z needs to be specified
class DdT a where
neg :: DdCtx a b c => Cudd.Cudd.DdManager -> Dd a b c -> Dd a b c
ite :: DdCtx a b c => Cudd.Cudd.DdManager -> Dd a b c -> Dd a b c -> Dd a b c -> Dd a b c
equ :: DdCtx a b c => Cudd.Cudd.DdManager -> Dd a b c -> Dd a b c -> Dd a b c
swapChildNodes :: DdCtx a b c => Cudd.Cudd.DdManager -> Dd a b c -> [Int] -> Dd a b c
gfp :: DdCtx a b c => Cudd.Cudd.DdManager -> (Dd a b c -> Dd a b c) -> Dd a b c
toZ :: (DdCtx a b c, DdTOI Z b c) => Cudd.Cudd.DdManager -> Dd a b c -> Dd Z b c
toB :: (DdCtx a b c, DdTOI B b c) => Cudd.Cudd.DdManager -> Dd a b c -> Dd B b c
ddSwapVars :: (DdCtx a b c) => Cudd.Cudd.DdManager -> Dd a b c -> [Int] -> [Int] -> Dd a b c -- assumes no overlapping in lists
writeToDot :: DdCtx a b c => Cudd.Cudd.DdManager -> Dd a b c -> String -> IO ()
instance DdT B where
neg mgr (ToDd b) = ToDd $ Cudd.Cudd.cuddNot mgr b
ite mgr (ToDd b1) (ToDd b2) (ToDd b3) = ToDd $ Cudd.Cudd.cuddBddIte mgr b1 b2 b3 -- context independent version of ifthenelse
equ mgr a b = con mgr (imp mgr a b) (imp mgr b a)
swapChildNodes mgr b [n] = con mgr (imp mgr (neg mgr (var mgr n)) (restrict mgr b (n,True) `asTypeOf` b)) (imp mgr (var mgr n) (restrict mgr b (n,False) `asTypeOf` b))
swapChildNodes mgr b (n:ns) = con mgr (con mgr (imp mgr (neg mgr (var mgr n)) (restrict mgr b (n,True))) (con mgr (var mgr n) (restrict mgr b (n,False)))) (swapChildNodes mgr b ns)
swapChildNodes _ b [] = b
gfp mgr operator = gfpLoop (top mgr) where
gfpLoop current =
if current == operator current
then current
else gfpLoop (operator current)
toZ mgr (ToDd b :: Dd a b c)
| b == Cudd.Cudd.cuddReadLogicZero mgr = bot mgr :: Dd Z b c
| otherwise = ToDd (Cudd.Cudd.cuddZddPortFromBdd mgr b)
toB _ b = b
ddSwapVars mgr (ToDd bdd) list1 list2 = ToDd $ Cudd.Cudd.cuddBddSwapVariables mgr bdd [Cudd.Cudd.cuddBddIthVar mgr x | x <- list1] [Cudd.Cudd.cuddBddIthVar mgr y | y <- list2]
writeToDot mgr (ToDd zdd) = Cudd.Cudd.cuddBddToDot mgr zdd
instance DdT Z where
neg mgr z = ite mgr z (ToDd (Cudd.Cudd.cuddZddReadZero mgr) `asTypeOf` z) (ToDd (Cudd.Cudd.cuddZddReadOne mgr) `asTypeOf` z)
equ mgr a b = con mgr (imp mgr a b) (imp mgr b a)
ite mgr (ToDd z1) (ToDd z2) (ToDd z3) = ToDd (Cudd.Cudd.cuddZddITE mgr z1 z2 z3) -- context independent version of ifthenelse
swapChildNodes _ z [] = z
swapChildNodes mgr (ToDd z) [n] = ToDd $ Cudd.Cudd.cuddZddChange mgr z n
swapChildNodes mgr (ToDd z) (n:ns) = swapChildNodes mgr (ToDd $ Cudd.Cudd.cuddZddChange mgr z n) ns
gfp mgr operator = gfpLoop (top mgr) where
gfpLoop current =
if current == operator current
then current
else gfpLoop (operator current)
toZ _ z = z
toB mgr (ToDd b :: Dd a b c)
| b == Cudd.Cudd.cuddReadLogicZero mgr = bot mgr :: Dd B b c
| otherwise = ToDd (Cudd.Cudd.cuddZddPortToBdd mgr b)
ddSwapVars mgr z [n1] [n2] = ifthenelse mgr (var mgr n2) (ifthenelse mgr (var mgr n1) a11 a10) (ifthenelse mgr (var mgr n1) a01 a00)
where
a11 = restrict mgr (restrict mgr z (n1, True)) (n2, True)
a10 = restrict mgr (restrict mgr z (n1, True)) (n2, False)
a01 = restrict mgr (restrict mgr z (n1, False)) (n2, True)
a00 = restrict mgr (restrict mgr z (n1, False)) (n2, False)
ddSwapVars mgr z (n1:ns1) (n2:ns2) = ddSwapVars mgr (ifthenelse mgr (var mgr n2) (ifthenelse mgr (var mgr n1) a11 a10) (ifthenelse mgr (var mgr n1) a01 a00)) ns1 ns2
where
a11 = restrict mgr (restrict mgr z (n1, True)) (n2, True)
a10 = restrict mgr (restrict mgr z (n1, True)) (n2, False)
a01 = restrict mgr (restrict mgr z (n1, False)) (n2, True)
a00 = restrict mgr (restrict mgr z (n1, False)) (n2, False)
ddSwapVars _ _ _ _ = error "lists for ZddSwapVar are not of equal length"
writeToDot mgr (ToDd zdd) = Cudd.Cudd.cuddZddToDot mgr zdd
-- | functions where B/Z and O1/0 need to be specified
class DdTO a b where
con :: DdCtx a b c => Cudd.Cudd.DdManager -> Dd a b c -> Dd a b c -> Dd a b c
dis :: DdCtx a b c => Cudd.Cudd.DdManager -> Dd a b c -> Dd a b c -> Dd a b c
xor :: DdCtx a b c => Cudd.Cudd.DdManager -> Dd a b c -> Dd a b c -> Dd a b c
xor mgr z1 z2 = dis mgr a b where
a = con mgr (neg mgr z1) z2
b = con mgr z1 (neg mgr z2)
exists_ :: DdCtx a b c => Cudd.Cudd.DdManager -> Int -> Dd a b c -> Dd a b c
forall_ :: DdCtx a b c => Cudd.Cudd.DdManager -> Int -> Dd a b c -> Dd a b c
existsSet :: DdCtx a b c => Cudd.Cudd.DdManager -> [Int] -> Dd a b c -> Dd a b c
forallSet :: DdCtx a b c => Cudd.Cudd.DdManager -> [Int] -> Dd a b c -> Dd a b c
instance DdTO Z O1 where
con mgr (ToDd z1) (ToDd z2) = ToDd (Cudd.Cudd.cuddZddIntersect mgr z1 z2)
dis mgr (ToDd z1) (ToDd z2) = ToDd (Cudd.Cudd.cuddZddUnion mgr z1 z2)
exists_ mgr n z = dis mgr (restrict mgr z (n, False)) (restrict mgr z (n, True))
forall_ mgr n z = con mgr (restrict mgr z (n, False)) (restrict mgr z (n, True))
existsSet _ [] z = z
existsSet mgr [n] z = exists_ mgr n z
existsSet mgr (n:ns) z = dis mgr (restrict mgr (existsSet mgr ns z) (n, False)) (restrict mgr (existsSet mgr ns z) (n, True))
forallSet _ [] z = z
forallSet mgr [n] z = forall_ mgr n z
forallSet mgr (n:ns) z = con mgr (restrict mgr (forallSet mgr ns z) (n, False)) (restrict mgr (forallSet mgr ns z) (n, True))
instance DdTO Z O0 where
con mgr (ToDd z1) (ToDd z2) = ToDd (Cudd.Cudd.cuddZddUnion mgr z1 z2)
dis mgr (ToDd z1) (ToDd z2) = ToDd (Cudd.Cudd.cuddZddIntersect mgr z1 z2)
exists_ mgr n z = dis mgr (restrict mgr z (n, False)) (restrict mgr z (n, True))
forall_ mgr n z = con mgr (restrict mgr z (n, False)) (restrict mgr z (n, True))
existsSet mgr [n] z = exists_ mgr n z
existsSet mgr (n:ns) z = dis mgr (restrict mgr (existsSet mgr ns z) (n, False)) (restrict mgr (existsSet mgr ns z) (n, True))
existsSet _ [] z = z
forallSet mgr [n] z = forall_ mgr n z
forallSet mgr (n:ns) z = con mgr (restrict mgr (forallSet mgr ns z) (n, False)) (restrict mgr (forallSet mgr ns z) (n, True))
forallSet _ [] z = z
instance DdTO B O1 where
con mgr (ToDd b1) (ToDd b2) = ToDd $ Cudd.Cudd.cuddBddAnd mgr b1 b2
dis mgr (ToDd b1) (ToDd b2) = ToDd $ Cudd.Cudd.cuddBddOr mgr b1 b2
xor mgr (ToDd b1) (ToDd b2) = ToDd $ Cudd.Cudd.cuddBddXor mgr b1 b2
exists_ mgr n (ToDd b) = ToDd $ Cudd.Cudd.cuddBddExistAbstract mgr b ( Cudd.Cudd.cuddIndicesToCube mgr [n])
forall_ mgr n (ToDd b) = ToDd $ Cudd.Cudd.cuddBddUnivAbstract mgr b ( Cudd.Cudd.cuddIndicesToCube mgr [n])
existsSet mgr n (ToDd b) = ToDd $ Cudd.Cudd.cuddBddExistAbstract mgr b ( Cudd.Cudd.cuddIndicesToCube mgr n)
forallSet mgr n (ToDd b) = ToDd $ Cudd.Cudd.cuddBddUnivAbstract mgr b ( Cudd.Cudd.cuddIndicesToCube mgr n)
instance DdTO B O0 where
con mgr (ToDd b1) (ToDd b2) = ToDd $ Cudd.Cudd.cuddBddOr mgr b1 b2
dis mgr (ToDd b1) (ToDd b2) = ToDd $ Cudd.Cudd.cuddBddAnd mgr b1 b2
xor mgr (ToDd b1) (ToDd b2) = neg mgr $ ToDd $ Cudd.Cudd.cuddBddXor mgr b1 b2
exists_ mgr n (ToDd b) = ToDd $ Cudd.Cudd.cuddBddUnivAbstract mgr b ( Cudd.Cudd.cuddIndicesToCube mgr [n])
forall_ mgr n (ToDd b) = ToDd $ Cudd.Cudd.cuddBddExistAbstract mgr b ( Cudd.Cudd.cuddIndicesToCube mgr [n])
existsSet mgr n (ToDd b) = ToDd $ Cudd.Cudd.cuddBddUnivAbstract mgr b ( Cudd.Cudd.cuddIndicesToCube mgr n)
forallSet mgr n (ToDd b) = ToDd $ Cudd.Cudd.cuddBddExistAbstract mgr b ( Cudd.Cudd.cuddIndicesToCube mgr n)
-- | functions where only O1/0 needs to specified
class DdO a b where
toO0 :: (DdCtx a O0 c) => Cudd.Cudd.DdManager -> Dd a b c -> Dd a O0 c
toO1 :: (DdCtx a O1 c) => Cudd.Cudd.DdManager -> Dd a b c -> Dd a O1 c
ifthenelse :: DdCtx a b c => Cudd.Cudd.DdManager -> Dd a b c -> Dd a b c -> Dd a b c -> Dd a b c
instance DdO a O1 where
toO0 mgr (ToDd d :: Dd a b c) = neg mgr (ToDd d :: Dd a O0 c)
toO1 _ d = d
ifthenelse = ite -- context dependent version of ite
instance DdO a O0 where
toO0 _ d = d
toO1 mgr (ToDd d :: Dd a b c) = neg mgr (ToDd d :: Dd a O1 c)
ifthenelse mgr d1 d2 d3 = ite mgr d1 d3 d2 -- context dependent version of ite
-- | functions where only I1/0 needs to specified
class DdI a b c where
toI0 :: (DdCtx a b I0) => Cudd.Cudd.DdManager -> [Int] -> Dd a b c -> Dd a b I0
toI1 :: (DdCtx a b I1) => Cudd.Cudd.DdManager -> [Int] -> Dd a b c -> Dd a b I1
instance DdI a b I1 where
toI0 mgr domain (ToDd d :: Dd a b c) = swapChildNodes mgr (ToDd d :: Dd a b I0) (map fromEnum domain)
toI1 _ _ d = d
instance DdI a b I0 where
toI0 _ _ d = d
toI1 mgr domain (ToDd d :: Dd a b c) = swapChildNodes mgr (ToDd d :: Dd a b I1) (map fromEnum domain)
-- | Convert a BDD to one of the four ZDD kinds.
class (Convert b c) where
convertToZDD :: Cudd.Cudd.DdManager -> [Int] -> Dd B O1 I1 -> Dd Z b c
instance Convert O1 I1 where convertToZDD mgr _ = toZ mgr
instance Convert O1 I0 where convertToZDD mgr domain = toI0 mgr domain . toZ mgr
instance Convert O0 I1 where convertToZDD mgr _ = toO0 mgr . toZ mgr
instance Convert O0 I0 where convertToZDD mgr domain = toI0 mgr domain . toO0 mgr . toZ mgr
-- | functions where no specification is needed.
-- todo add these under the general class, instead of them being standalone functions?
imp :: DdCtx a b c => Cudd.Cudd.DdManager -> Dd a b c -> Dd a b c -> Dd a b c
imp mgr b1 = dis mgr (neg mgr b1)
conSet :: DdCtx a b c => Cudd.Cudd.DdManager -> [Dd a b c] -> Dd a b c
conSet mgr [] = top mgr
conSet mgr (b:bs) = foldl' (con mgr) b bs
disSet :: DdCtx a b c => Cudd.Cudd.DdManager -> [Dd a b c] -> Dd a b c
disSet mgr [] = bot mgr
disSet mgr (b:bs) = foldl' (dis mgr) b bs
xorSet :: DdCtx a b c => Cudd.Cudd.DdManager -> [Dd a b c] -> Dd a b c
xorSet mgr [] = top mgr
xorSet mgr (b:bs) = foldl' (xor mgr) b bs
restrictSet :: DdCtx a b c => Cudd.Cudd.DdManager -> Dd a b c -> [(Int, Bool)] -> Dd a b c
restrictSet mgr = foldl' (restrict mgr)
-- | Restrict with a law. Note that the law is the second parameter!
restrictLaw :: (DdCtx a b c) => Cudd.Cudd.DdManager -> [Int] -> Dd a b c -> Dd a b c -> Dd a b c
restrictLaw mgr v d = loop (getDependentVars mgr v d) d where
loop (n:ns) bdd law
| law == top mgr = bdd -- the law completely covers the bdd thus all nodes are restricted out
| (bdd == top mgr) || (bdd == bot mgr) = bdd -- the bdd is already top/bot, restricting it further does not change anything
| law == bot mgr = top mgr
-- otherwise do the recursive restriction until terminal cases are met
| otherwise = (var mgr n `conj` loop ns (restr bdd (n, True)) (restr law (n, True) )) `disj` (neg mgr (var mgr n) `conj` loop ns (restr bdd (n, False)) (restr law (n, False))) where
disj = dis mgr
conj = con mgr
restr = restrict mgr
loop [] bdd law
| law == top mgr = bdd -- the law completely covers the bdd thus all nodes are restricted out
| (bdd == top mgr) || (bdd == bot mgr) = bdd -- the bdd is already top/bot, restricting it further does not change anything
| law == bot mgr = top mgr
| otherwise = error "impossible? something went wrong in restrictLaw."
-- todo add faster restrictlaw for bdds?
{-}
restrictLawBDD :: Cudd.Cudd.DdManager -> Dd B b c -> Dd B b c -> Dd B b c
restrictLawBDD mgr (ToDd b) (ToDd law) = ToDd $ Cudd.Cudd.cuddBddRestrict mgr b law
-}
-- | Relabel a DD with a function. Assumption: no int is mapped to itself by rF.
relabelFun :: (DdCtx a b c) => Cudd.Cudd.DdManager -> [Int] -> (Int -> Int) -> Dd a b c -> Dd a b c
relabelFun mgr v rF dd = loop dd disjointListOfLists
where
--get indexes of "overlapping" integers positions (integers in l2 that also occur in L1)
--and use that to return the corresponding elements in a tuple
getOverlap l1 l2 = (map ((l1 !!) . fromJust) (indexesOverlap l1 l2), l1 `intersect` l2)
indexesOverlap l1 l2 = map (`elemIndex` l2) (l1 `intersect` l2)
indexesNotOverlap l1 l2 = map (`elemIndex` l1) (l1 `intersect` l2)
-- swap the (overlapping l1 ints with the corresponding l1 ints)
-- in the not overlapping l1 values we look for the overlapping l2 values
newOverlap l1 l2 = (fst $ getOverlap l1 l2, map ((l1 !!) . fromJust) (indexesNotOverlap l1 l2))
-- get a list of tuples containing 2 equal length, disjointed lists of vars to be swapped
-- by removing the "overlapping" variables and performing a recursive call with them
splitCompare [] [] = []
splitCompare [] _ = error "varlists used for relabeling do not have equal length."
splitCompare _ [] = error "varlists used for relabeling do not have equal length."
splitCompare l1 l2 = bimap (l1 \\) (l2 \\) (getOverlap l1 l2) : uncurry splitCompare (newOverlap l1 l2)
-- apply the function above to the support variable integers and the resulting integers from applying rf (the Remapping Function)
-- and turn the integers into DD nodes (as ddSwapVars requires this)
disjointListOfLists = splitCompare support (map rF support)
support = getDependentVars mgr v dd
-- loop and uncurry ddSwapVars so that it can be applied to the disjointListOfNodeLists
loop b (n:ns) = loop (uncurry (ddSwapVars mgr b) n) ns
loop b [] = b
-- | Relabel a DD with a list of pairs.
relabelWith :: (DdCtx a b c) => Cudd.Cudd.DdManager -> [(Int,Int)] -> Dd a b c -> Dd a b c
relabelWith mgr r dd = loop dd disjointListOfLists where
--get indexes of "overlapping" integers positions (integers in l2 that also occur in L1)
--and use that to return the corresponding elements in a tuple
getOverlap l1 l2 = (map ((l1 !!) . fromJust) (indexes l1 l2), l1 `intersect` l2)
indexes l1 l2 = map (`elemIndex` l2) (l1 `intersect` l2)
indexesNotOverlap l1 l2 = map (`elemIndex` l1) (l1 `intersect` l2)
-- swap the (overlapping l1 ints with the corresponding l1 ints)
-- in the non overlapping l1 values we look for the overlapping l2 values
newOverlap l1 l2 = (fst $ getOverlap l1 l2, map ((l1 !!) . fromJust) (indexesNotOverlap l1 l2))
-- get a list of tuples containing 2 equal length, disjointed lists of vars to be swapped
-- by removing the "overlapping" variables and performing a recursive call with them
splitCompare [] [] = []
splitCompare [] _ = error "varlists used for relabeling do not have equal length."
splitCompare _ [] = error "varlists used for relabeling do not have equal length."
splitCompare l1 l2 = bimap (l1 \\) (l2 \\) (getOverlap l1 l2) : uncurry splitCompare (newOverlap l1 l2)
-- apply the function above to the support variable integers and the resulting integers from applying rf (the Remapping Function)
-- and turn the integers into BDD nodes (as ddSwapVars requires this)
disjointListOfLists = splitCompare listVars1 listVars2
loop b (n:ns) = loop (uncurry (ddSwapVars mgr b) n) ns
loop b [] = b
listIntPairs = sort $ map (fromEnum *** fromEnum) $ filter (uncurry (/=)) r
(listVars1,listVars2) = unzip listIntPairs
-- | Simultaneous substitution.
-- Implemented via `ifte` and `restrict`.
substitSimul :: (DdCtx a b c) => Cudd.Cudd.DdManager -> [(Int, Dd a b c)] -> Dd a b c -> Dd a b c
substitSimul _ [] dd = dd
substitSimul mgr ((n, psi) : ns) dd = ifte psi ( recurs (restr dd (n, True))) (recurs (restr dd (n, False)))
where
recurs = substitSimul mgr ns
ifte = ifthenelse mgr
restr = restrict mgr
-- * supporting functions
returnDot :: DdCtx a b c => Cudd.Cudd.DdManager -> Dd a b c -> IO String
returnDot mgr d = withSystemTempDirectory "smcdel" $ \tmpdir -> do
writeToDot mgr d (tmpdir ++ "/temp.dot")
readFile (tmpdir ++ "/temp.dot")
printDdInfo :: Cudd.Cudd.DdManager -> Dd a b c -> String -> IO ()
printDdInfo mgr (ToDd zdd) str = do
putStrLn str
Cudd.Cudd.cuddPrintDdInfo mgr zdd
return ()
size :: Cudd.Cudd.DdManager -> Dd a b c -> Int
size mgr (ToDd dd)
| dd == Cudd.Cudd.cuddZddReadZero mgr = 0
| otherwise = Cudd.Cudd.cuddDagSize dd
-- | Given a DD and the maximum variable, calculate the sparsity of the encoded set of state.
sparsity :: Cudd.Cudd.DdManager -> Dd a b c -> Int -> Double
sparsity mgr (ToDd b) n = Cudd.Cudd.cuddCountMinterm mgr b n / (2 ** fromIntegral n)
getSupport :: Cudd.Cudd.DdManager -> Dd a b c -> [Int]
getSupport mgr (ToDd dd) = [ n | (n,True) <- zip [0..] $ Cudd.Cudd.cuddSupportIndex mgr dd ]
-- | Given the full vocabulary and a DD, return the list of variables that the DD depends on.
-- Note that for ZDDs the support is not the same as the set of dependent variables.
getDependentVars :: (DdCtx a b c) => Cudd.Cudd.DdManager -> [Int] -> Dd a b c -> [Int]
getDependentVars mgr v dd = filter (\n -> restrict mgr dd (n, True) /= restrict mgr dd (n, False)) v
-- * ZDD only functions
-- restricts, but thus also removes variable from domain
sub0 :: Cudd.Cudd.DdManager -> Dd Z a b -> Int -> Dd Z a b
sub0 mgr (ToDd z) n = ToDd $ Cudd.Cudd.cuddZddSub0 mgr z n
sub1 :: Cudd.Cudd.DdManager -> Dd Z a b -> Int -> Dd Z a b
sub1 mgr (ToDd z) n = ToDd $ Cudd.Cudd.cuddZddSub1 mgr z n
swap :: (DdCtx Z b c) => Cudd.Cudd.DdManager -> Dd Z b c -> Int -> Dd Z b c
swap mgr (ToDd z) n = ToDd $ Cudd.Cudd.cuddZddChange mgr z n
-- * BDD only functions
-- | An assignment of boolean values to variables/integers.
type Assignment = [(Int,Bool)]
bitsToAss :: [Cudd.Cudd.SatBit] -> [Assignment]
bitsToAss = loop 0 where
loop _ [] = [ [] ]
loop n (Cudd.Cudd.DontCare:rest) = loop (n+1) rest
loop n (Cudd.Cudd.Zero :rest) = [ (n,False) : restA | restA <- loop (n+1) rest ]
loop n (Cudd.Cudd.One :rest) = [ (n,True ) : restA | restA <- loop (n+1) rest ]
-- | Get all satisfying assignments. These will be partial, i.e. only
-- contain (a subset of) the variables that actually occur in the BDD.
allSats :: Cudd.Cudd.DdManager -> Dd B O1 I1 -> [Assignment]
allSats mgr (ToDd b) = concatMap bitsToAss $ Cudd.Cudd.cuddAllSat mgr b
-- | Get the lexicographically smallest satisfying assignment, if there is any.
anySat :: Cudd.Cudd.DdManager -> Dd B O1 I1 -> Maybe Assignment
anySat mgr (ToDd b) = head . bitsToAss <$> Cudd.Cudd.cuddOneSat mgr b
-- | Given a set of all variables, complete an assignment.
completeAss :: [Int] -> Assignment -> [Assignment]
completeAss allvars ass =
if null (addvars ass)
then [ass]
else concatMap (completeAss allvars) (extend ass (head (addvars ass)))
where
addvars s = allvars \\ sort (map fst s)
extend s v = [ (v,False):s, (v,True):s ]
-- | Get all complete satisfying assignments, given a set of all variables.
-- In particular this will include variables not in the BDD.
allSatsWith :: Cudd.Cudd.DdManager -> [Int] -> Dd B O1 I1 -> [Assignment]
allSatsWith mgr allvars b = concatMap (completeAss allvars) (allSats mgr b)