packages feed

smcdel-1.3.0: src/SMCDEL/Symbolic/Ki_CUDD.hs

{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, ScopedTypeVariables, FlexibleContexts  #-}
{-# LANGUAGE TupleSections #-}

module SMCDEL.Symbolic.Ki_CUDD where

import Data.Bifunctor
import Data.Tagged

import Control.Arrow (Arrow ((&&&)))
-- import Data.Dynamic (fromDynamic)

import SMCDEL.Internal.MyHaskCUDD
import Data.List (sort,intersect,(\\), intercalate)
import qualified Data.Map.Strict as M
import Data.Map.Strict ((!))
import SMCDEL.Explicit.K
import SMCDEL.Internal.Help (apply,lfp,powerset)
import SMCDEL.Language
import SMCDEL.Symbolic.S5_CUDD (KnState,boolDdOf, boolDDoutof, ddEval, ddToForm, evalAssDD, texDd, texDdFun)
import Cudd.Cudd ( DdManager )
import SMCDEL.Internal.TexDisplay

mvP, cpP :: Int -> Prp -> Prp
mvP m (P n) = P  ((2*n) + m)           -- represent p  in the double vocabulary
cpP m (P n) = P  ((2*n) + 1 + m)       -- represent p' in the double vocabulary

unmvcpP :: Int -> Prp -> Prp
unmvcpP m (P n) | even (n-m) = P $ (n-m) `div` 2
                | otherwise  = P $ (n-1-m) `div` 2

mv, cp :: Int -> [Prp] -> [Prp]
mv m = map (mvP m)
cp m = map (cpP m)

unmv, uncp :: Int -> [Prp] -> [Prp]
-- | Go from p in double vocabulary to p in single vocabulary.
unmv m = map f where
  f (P n) | odd (n-m)    = error "unmv failed: Number is odd!"
          | otherwise = P $ (n-m) `div` 2
-- | Go from p' in double vocabulary to p in single vocabulary.
uncp m = map f where
  f (P n) | even (n-m)    = error "uncp failed: Number is even!"
          | otherwise = P $ (n-m-1) `div` 2

data Dubbel
type RelDD a b c = Tagged Dubbel (Dd a b c)

totalRelDd, emptyRelDd :: (DdCtx a b c) => Cudd.Cudd.DdManager -> RelDD a b c
totalRelDd mgr = pure $ boolDdOf mgr Top
emptyRelDd mgr = pure $ boolDdOf mgr Bot

allsamedd :: (DdCtx a b c) => Cudd.Cudd.DdManager -> Int -> [Prp] -> RelDD a b c
allsamedd mgr m ps = pure $ conSet mgr [boolDdOf mgr $ PrpF p `Equi` PrpF p' | (p,p') <- zip (mv m ps) (cp m ps)]

class TagDd t a b c where
  tagDdEval :: (DdCtx a b c) => Cudd.Cudd.DdManager -> [Prp] -> Tagged t (Dd a b c) -> Bool
  tagDdEval mgr truths querydd = evalAssDD mgr (untag querydd) (\n -> P n `elem` truths)

instance TagDd Dubbel a b c

-- TODO use relabelFun instead for cpDd, mvDd, unmvDd below?

cpDd :: (DdCtx a b c) => Cudd.Cudd.DdManager -> Int -> [Prp] -> Dd a b c -> RelDD a b c
cpDd mgr m vocab b = Tagged $ relabelWith mgr (zipWith (curry (bimap fromEnum fromEnum)) vocab (map (cpP m) vocab)) b

mvDd :: (DdCtx a b c) => Cudd.Cudd.DdManager -> Int -> [Prp] -> Dd a b c -> RelDD a b c
mvDd mgr m vocab b = Tagged $ relabelWith mgr (zipWith (curry (bimap fromEnum fromEnum)) vocab (map (mvP m) vocab)) b

unmvDd :: (DdCtx a b c) => Cudd.Cudd.DdManager -> Int -> [Prp] -> RelDD a b c -> Dd a b c
unmvDd mgr m vocab (Tagged b) = relabelWith mgr (zipWith (curry (bimap fromEnum fromEnum)) (map (mvP m) vocab) vocab) b

propRel2dd :: (DdCtx a b c) => Cudd.Cudd.DdManager -> Int -> [Prp] -> M.Map KnState [KnState] -> RelDD a b c
propRel2dd mgr m props relation = pure $ disSet mgr (M.elems $ M.mapWithKey linkdd relation) where
  linkdd here theres =
    con mgr (boolDDoutof mgr (mv m here) (mv m props))
        (disSet mgr [ boolDDoutof mgr (cp m there) (cp m props) | there <- theres ] )

relDdOfIn :: (DdCtx a b c) => Cudd.Cudd.DdManager -> Int -> Agent -> KripkeModel -> RelDD a b c
relDdOfIn mgr ag i (KrM m)
  | not (distinctVal (KrM m)) = error "m does not have distinct valuations."
  | otherwise = pure $ disSet mgr (M.elems $ M.map linkdd m) where
    linkdd (mapPropBool,mapAgentReach)  =
      con mgr
        (boolDDoutof mgr (mv ag here) (mv ag props))
        (disSet mgr [ boolDDoutof mgr (cp ag there) (cp ag props) | there<-theres ] )
      where
        props = M.keys mapPropBool
        here = M.keys (M.filter id mapPropBool)
        theres = map (truthsInAt (KrM m)) (mapAgentReach ! i)

data BelStruct a b c = BlS Cudd.Cudd.DdManager      -- Cudd manager for removing/reseting variables
                     [Prp]                          -- vocabulary
                     (Dd a b c)                     -- state law
                     (M.Map Agent Int,RelDD a b c)  -- observation laws
                  deriving (Eq,Show)

instance Pointed (BelStruct a b c) KnState
type BelScene a b c = (BelStruct a b c,KnState)

instance Pointed (BelStruct a b c) (Dd a b c)
type MultipointedBelScene a b c = (BelStruct a b c, Dd a b c)

instance HasVocab (BelStruct a b c) where
  vocabOf (BlS _ voc _ _) = voc

instance HasAgents (BelStruct a b c) where
  agentsOf (BlS _ _ _ (ag, _)) = M.keys ag

ddOf :: (DdCtx a b c) => BelStruct a b c -> Form -> Dd a b c
ddOf (BlS mgr _ _ _)   Top           = top mgr
ddOf (BlS mgr _ _ _)   Bot           = bot mgr
ddOf (BlS mgr _ _ _)   (PrpF (P n))  = var mgr n
ddOf bls@(BlS mgr _ _ _) (Neg form)    = neg mgr $ ddOf bls form
ddOf bls@(BlS mgr _ _ _) (Conj forms)  = conSet mgr $ map (ddOf bls) forms
ddOf bls@(BlS mgr _ _ _) (Disj forms)  = disSet mgr $ map (ddOf bls) forms
ddOf bls@(BlS mgr _ _ _) (Xor  forms)  = xorSet mgr $ map (ddOf bls) forms
ddOf bls@(BlS mgr _ _ _) (Impl f g)    = imp mgr (ddOf bls f) (ddOf bls g)
ddOf bls@(BlS mgr _ _ _) (Equi f g)    = equ mgr (ddOf bls f) (ddOf bls g)
ddOf bls@(BlS mgr _ _ _) (Forall ps f) = forallSet mgr (map fromEnum ps) (ddOf bls f)
ddOf bls@(BlS mgr _ _ _) (Exists ps f) = existsSet mgr (map fromEnum ps) (ddOf bls f)

ddOf bls@(BlS mgr allprops lawdd (ag, odds)) (K i form) = unmvDd mgr (M.size ag) allprops result
  where
  result = forallSet mgr ps' <$> (imp mgr <$> cpDd mgr (M.size ag) allprops lawdd <*> (imp mgr <$> omegai <*> cpDd mgr (M.size ag) allprops (ddOf bls form)))
  ps'    = map fromEnum $ cp (M.size ag) allprops
  omegai = Tagged $ restrictSet mgr (untag odds) ((ag ! i, True) : map (, False) agNotI) where
    agNotI =  M.elems $ M.delete i ag

ddOf bls@(BlS mgr allprops lawdd (ag, odds)) (Kw i form) = unmvDd mgr (M.size ag) allprops result
  where
  result = dis mgr <$> part form <*> part (Neg form)
  part f = forallSet mgr ps' <$> (imp mgr <$> cpDd mgr (M.size ag) allprops lawdd <*> (imp mgr <$> omegai <*> cpDd mgr (M.size ag) allprops (ddOf bls f)))
  ps'    = map fromEnum $ cp (M.size ag) allprops
  omegai = Tagged $ restrictSet mgr (untag odds) ((ag ! i, True) : map (, False) agNotI) where
    agNotI =  M.elems $ M.delete i ag

ddOf bls@(BlS mgr voc (lawdd :: Dd a b c) (ag, odds)) (Ck ags form) = lfp lambda (top mgr)  where
  ps' = map fromEnum $ cp (M.size ag) voc
  lambda :: Dd a b c -> Dd a b c
  lambda z = unmvDd mgr (M.size ag) voc $
    forallSet mgr ps' <$>
      (imp mgr <$> cpDd mgr (M.size ag) voc lawdd <*>
        ((imp mgr . disSet mgr <$> sequence [omegai i | i <- ags]) <*>
          cpDd mgr (M.size ag) voc (con mgr (ddOf bls form) z))) where
              omegai i = Tagged $ restrictSet mgr (untag odds) ((ag ! i, True) : map (, False) (agNotI i))
              agNotI i =  M.elems $ M.delete i ag

ddOf bls@(BlS mgr _ _ _) (Ckw ags form) = dis mgr (ddOf bls (Ck ags form)) (ddOf bls (Ck ags (Neg form)))

ddOf bls@(BlS mgr allprops lawdd (ag, odds)) (Dk ags form) = unmvDd mgr (M.size ag) allprops result
  where
  result = forallSet mgr ps' <$> (imp mgr <$> cpDd mgr (M.size ag) allprops lawdd <*> (imp mgr <$> omegai <*> cpDd mgr (M.size ag) allprops (ddOf bls form)))
  ps'    = map fromEnum $ cp (M.size ag) allprops
  omegai = Tagged $ restrictSet mgr (untag odds) $ map (\a -> (ag ! a, a `elem` ags)) $ M.keys ag

ddOf bls@(BlS mgr allprops lawdd (ag, odds)) (Dkw ags form) = unmvDd mgr (M.size ag) allprops result
  where
  result = dis mgr <$> part form <*> part (Neg form)
  part f = forallSet mgr ps' <$> (imp mgr <$> cpDd mgr (M.size ag) allprops lawdd <*> (imp mgr <$> omegai <*> cpDd mgr (M.size ag) allprops (ddOf bls f)))
  ps'    = map fromEnum $ cp (M.size ag) allprops
  omegai = Tagged $ restrictSet mgr (untag odds) $ map (\a -> (ag ! a, a `elem` ags)) $ M.keys ag

ddOf bls@(BlS mgr _ _ _) (PubAnnounce f g) =
  imp mgr (ddOf bls f) (ddOf (bls `update` f) g)
ddOf bls@(BlS mgr _ _ _) (PubAnnounceW f g) =
  ifthenelse mgr (ddOf bls f)
    (ddOf  (bls `update` f    ) g)
    (ddOf  (bls `update` Neg f) g)

ddOf bls@(BlS mgr props _ _) (Announce ags f g) =
  imp mgr (ddOf bls f) (restrict mgr dd2 (k,True)) where
    dd2  = ddOf (announce bls ags f) g
    (P k) = freshp props

ddOf bls@(BlS mgr props _ _) (AnnounceW ags f g) =
  ifthenelse mgr (ddOf bls f) dd2a dd2b where
    dd2a = restrict mgr (ddOf  (announce bls ags f      ) g) (k,True)
    dd2b = restrict mgr (ddOf  (announce bls ags (Neg f)) g) (k,True)
    (P k) = freshp props

ddOf _ (Dia (Dyn _ _) _) = error "Dia Dyn operator is not implemented in Ki_CUDD"

validViaDd :: (DdCtx a b c) => BelStruct a b c -> Form -> Bool
validViaDd bls@(BlS mgr _ lawdd _) f = top mgr == imp mgr lawdd (ddOf bls f)

evalViaDd :: (DdCtx a b c) => BelScene a b c -> Form -> Bool
evalViaDd (bls@(BlS mgr allprops _ _),s) f = let
    dd  = ddOf bls f
    b    = restrictSet mgr dd list
    list = [ (n, P n `elem` s) | (P n) <- allprops ]
  in
    case (b==top mgr,b==bot mgr) of
      (True,_) -> True
      (_,True) -> False
      _        -> error $ "evalViaDd failed: Composite DD leftover!\n"
        ++ "  bls:  " ++ show bls ++ "\n"
        ++ "  s:    " ++ show s ++ "\n"
        ++ "  form: " ++ show f ++ "\n"
        ++ "  dd:  " ++ show dd ++ "\n"
        ++ "  list: " ++ show list ++ "\n"
        ++ "  b:    " ++ show b ++ "\n"

instance (DdCtx a b c) => Semantics (BelStruct a b c) where
  isTrue = validViaDd

instance (DdCtx a b c) => Semantics (BelScene a b c) where
  isTrue = evalViaDd

instance (DdCtx a b c) => Semantics (MultipointedBelScene a b c) where
  isTrue (kns@(BlS mgr _ lawBdd _), statesBdd) f =
    let a = imp mgr lawBdd (imp mgr statesBdd (ddOf kns f))
     in a == top mgr

instance (DdCtx a b c) => Update (BelStruct a b c) Form where
  checks = [ ] -- unpointed structures can be updated with anything
  unsafeUpdate bls@(BlS mgr allprops lawdd obs) f =
    BlS mgr allprops (con mgr lawdd (ddOf bls f)) obs

instance (DdCtx a b c) => Update (BelScene a b c) Form where
  unsafeUpdate (kns,s) psi = (unsafeUpdate kns psi,s)

announce :: (DdCtx a b c) => BelStruct a b c -> [Agent] -> Form -> BelStruct a b c
announce bls@(BlS mgr props lawdd (ag, odds)) ags psi = BlS mgr newprops newlawdd (ag, newodds) where
  (P k)     = freshp props
  newprops  = sort $ P k : props
  newlawdd = con mgr lawdd (imp mgr (var mgr k) (ddOf  bls psi))
  newodds  = foldl (\x y -> con mgr <$> x <*> y) (Tagged $ top mgr) [newOfor i $ Tagged (restrict mgr (untag odds) (ag ! i, True)) | i <- M.keys ag]
  newOfor i oi | i `elem` ags = con mgr <$> oi <*> (equ mgr <$> mvDd mgr (M.size ag) newprops (var mgr k) <*> cpDd mgr (M.size ag) newprops (var mgr k))
               | otherwise    = con mgr <$> oi <*> (neg mgr <$> cpDd mgr (M.size ag) newprops (var mgr k)) -- p_psi'

whereViaDd :: DdCtx a b c => BelStruct a b c -> Form -> [KnState]
whereViaDd kns f = statesOf (kns `update` f)

--Somewhat fast statesOf, faster woud be to use primitive construction of all Satifying Assignments (e.i. explicitly looping through the dd instead of using restrict).
statesOf :: DdCtx a b c => BelStruct a b c -> [KnState]
statesOf (BlS mgr allprops lawdd _) = loop allprops lawdd where
  loop [] _ = []
  loop v d = r v d True ++ r v d False
  r ((P n):ns) d b
    | restrict mgr d (n,b) == bot mgr = []
    | restrict mgr d (n,b) == top mgr = if b then map ([P n] ++) (powerset ns) else powerset ns
    | otherwise =
      if b then map ([P n] ++) $ loop ns (restrict mgr d (n,b)) else loop ns (restrict mgr d (n,b))
  r [] _ _ = error "impossible?"

texRelDD :: DdCtx a b c => Cudd.Cudd.DdManager -> Int -> RelDD a b c -> String
texRelDD mgr agNr (Tagged b) = texDdFun mgr b texRelProp where
  texRelProp n
    | n < agNr  = "agent " ++ show (n+1)
    | even n    = show $ ((n - agNr) `div` 2)+1
    | otherwise = show (((n - agNr - 1) `div` 2)+1) ++ "'"

ddprefix, ddsuffix :: String
ddprefix = "\\begin{array}{l} \\scalebox{0.3}{"
ddsuffix = "} \\end{array} \n"

instance DdCtx a b c => TexAble (BelStruct a b c) where
  tex (BlS mgr props lawdd (ags,odds)) = concat
    [ " \\left( \n"
    , tex props, ", "
    , ddprefix, texDd mgr lawdd, ddsuffix
    , ", "
    , intercalate ", " ([(\ x -> ddstring (i, texDd mgr $ untag x)) (omegai i) | i <- M.keys ags ])
    , " \\right) \n"
    ] where
        omegai i = Tagged $ restrictSet mgr (untag odds) ((ags ! i, True) : map (, False) (agNotI i))
        agNotI j =  M.elems $ M.delete j ags
        ddstring (i,os) = "\\Omega_{\\text{" ++ i ++ "}} = " ++ ddprefix ++ os ++ ddsuffix

instance DdCtx a b c => TexAble (BelScene a b c) where
  tex (bls, state) = concat
    [ " \\left( \n", tex bls, ", ", tex state, " \\right) \n" ]

instance DdCtx a b c => TexAble (MultipointedBelScene a b c) where
  tex (bls@(BlS mgr _ _ _), statesDd) = concat
    [ " \\left( \n"
    , tex bls ++ ", "
    , " \\begin{array}{l} \\scalebox{0.4}{"
    , texDd mgr statesDd
    , "} \\end{array}\n "
    , " \\right)" ]

-- todo test this, although likely to work.
cleanupObsLaw :: DdCtx a b c => BelScene a b c -> BelScene a b c
cleanupObsLaw (BlS mgr vocab law (ag, obs), s) = (BlS mgr vocab law (ag, clean (M.size ag) obs), s) where
  clean shift reldd = restrictLaw mgr (map fromEnum vocab) <$> reldd <*> (con mgr <$> cpDd mgr shift vocab law <*> mvDd mgr shift vocab law)

determinedVocabOf :: (DdCtx a b c) => BelStruct a b c -> [Prp]
determinedVocabOf strct = filter (\p -> validViaDd strct (PrpF p) || validViaDd strct (Neg $ PrpF p)) (vocabOf strct)

agentBelief :: (DdCtx a b c) => Cudd.Cudd.DdManager -> RelDD a b c -> Int -> RelDD a b c
agentBelief mgr (Tagged b) n = Tagged $ restrict mgr b (n, True)

data Transformer a b c = Trf
  Cudd.Cudd.DdManager -- Cudd manager needed for reseting/removing variables
  [Prp] -- addprops
  Form  -- event law
  (M.Map Prp (Dd a b c)) -- changelaw
  (M.Map Agent Int, RelDD a b c) -- eventObs
  deriving (Eq,Show)

instance HasAgents (Transformer a b c) where
  agentsOf (Trf _ _ _ _ (ag, _)) = M.keys ag

instance HasPrecondition (Transformer a b c) where
  preOf _ = Top

instance Pointed (Transformer a b c) KnState
type Event a b c = (Transformer a b c,KnState)

instance HasPrecondition (Event a b c) where
  preOf (Trf _ addprops addlaw _ _, x) = simplify $ substitOutOf x addprops addlaw

instance Pointed (Transformer a b c) (Dd a b c)
type MultipointedEvent a b c = (Transformer a b c, Dd a b c)

-- todo see K_CUDD version
instance (DdCtx a b c) => HasPrecondition (MultipointedEvent a b c) where
  preOf (Trf mgr addprops addlaw _ _, xsDd) =
    simplify $ Exists addprops (Conj [ ddToForm mgr addprops xsDd, addlaw ])

instance DdCtx a b c => TexAble (Transformer a b c) where
  tex (Trf mgr addprops addlaw changelaw (ags, eventObs)) = concat
    [ " \\left( \n"
    , tex addprops, ", "
    , tex addlaw, ", "
    , tex changeprops, ", "
    , intercalate ", " $ map snd . M.toList $ M.mapWithKey texChange changelaw, ", "
    , intercalate ", " eoddstrings
    , " \\right) \n"
    ] where
        changeprops = M.keys changelaw
        texChange prop changedd = tex prop ++ " := " ++ tex (ddToForm mgr addprops changedd)
        eoddstrings = [ (ddstring . (fst &&& (texRelDD mgr (maximum $ M.elems ags) . snd))) (i,  omegai i) | i <- M.keys ags ]
        ddstring (i,os) = "\\Omega^+_{\\text{" ++ i ++ "}} = " ++ ddprefix ++ os ++ ddsuffix
        omegai i = Tagged $ restrictSet mgr (untag eventObs) ((ags ! i, True) : map (, False) (agNotI i))
        agNotI j =  M.elems $ M.delete j ags

instance DdCtx a b c => TexAble (Event a b c) where
  tex (trf, eventFacts) = concat
    [ " \\left( \n", tex trf, ", ", tex eventFacts, " \\right) \n" ]

instance DdCtx a b c => TexAble (MultipointedEvent a b c) where
  tex (trf@(Trf mgr _ _ _ _), eventStates) = concat
    [ " \\left( \n"
    , tex trf ++ ", \\ "
    , " \\begin{array}{l} \\scalebox{0.4}{"
    , texDd mgr eventStates
    , "} \\end{array}\n "
    , " \\right)" ]

-- | Shift addprops to ensure that props and newprops are disjoint.
shiftPrepare :: (DdCtx a b c) => BelStruct a b c -> Transformer a b c -> (Transformer a b c, [(Prp,Prp)])
shiftPrepare (BlS mgr props _ _) (Trf _ addprops addlaw changelaw (ag, eventObs)) =
  (Trf mgr shiftaddprops addlawShifted changelawShifted (ag, eventObsShifted), shiftrel) where
    shiftrel = sort $ zip addprops [(freshp props)..]
    shiftaddprops = map snd shiftrel
    -- apply the shifting to addlaw, changelaw and eventObs:
    addlawShifted    = replPsInF shiftrel addlaw
    changelawShifted = M.map (relabelWith mgr (map (bimap fromEnum fromEnum) shiftrel)) changelaw
    -- to shift addObs we need shiftrel in the double vocabulary:
    shiftrelMVCP = map (bimap fromEnum fromEnum) $
                   sort $ zip (mv (M.size ag) addprops) (mv (M.size ag) shiftaddprops)
                       ++ zip (cp (M.size ag) addprops) (cp (M.size ag) shiftaddprops)
    eventObsShifted  = foldl (\x y -> con mgr <$> x <*> y) (Tagged $ top mgr) [Tagged $ relabelWith mgr shiftrelMVCP (restrict mgr (untag eventObs) (ag ! i, True)) | i <- M.keys ag]

instance (DdCtx a b c) => Update (BelScene a b c) (Event a b c) where
  checks = [haveSameAgents, sameManager, preCheck] where
    -- Check that BelScene and Event use the same manager:
    sameManager (BlS mgr _ _ _, _) (Trf mgr' _ _ _ _ , _) = mgr == mgr'
  unsafeUpdate (bls@(BlS mgr props law (_, odds)),s) (trf, eventFactsUnshifted) = (BlS mgr newprops newlaw (ag,newobs), news) where
    -- PART 1: SHIFTING addprops to ensure props and newprops are disjoint
    (Trf _ addprops addlaw changelaw (ag,addObs), shiftrel) = shiftPrepare bls trf
    -- the actual event:
    eventFacts = map (apply shiftrel) eventFactsUnshifted
    -- PART 2: COPYING the modified propositions
    changeprops = M.keys changelaw
    copyrel = zip changeprops [(freshp $ props ++ addprops)..]
    copychangeprops = map snd copyrel
    copyrelMVCP = map (bimap fromEnum fromEnum) $
                  sort $ zip (mv (M.size ag) changeprops) (mv (M.size ag) copychangeprops)
                      ++ zip (cp (M.size ag) changeprops) (cp (M.size ag) copychangeprops)
    -- PART 3: actual transformation
    newprops = sort $ props ++ addprops ++ copychangeprops
    newlaw = conSet mgr $ relabelWith mgr (map (bimap fromEnum fromEnum) copyrel) (con mgr law (ddOf bls addlaw))
                    : [equ mgr (var mgr (fromEnum q)) (relabelWith mgr (map (bimap fromEnum fromEnum) copyrel) (changelaw ! q)) | q <- changeprops]
    newobs = foldl (\x y -> con mgr <$> x <*> y) (Tagged $ top mgr) newodds
    newodds = [(con mgr . relabelWith mgr copyrelMVCP <$> Tagged (restrict mgr (untag odds) (ag ! i, True))) <*> Tagged (restrict mgr (untag addObs) (ag ! i, True)) | i <- M.keys ag]
      --previously: M.mapWithKey (\i oldobs -> con mgr <$> (relabelWith mgr copyrelMVCP <$> oldobs) <*> (addObs ! i)) odds
    news = sort $ concat
            [ s \\ changeprops
            , map (apply copyrel) $ s `intersect` changeprops
            , eventFacts
            , filter (\ p -> ddEval mgr (s ++ eventFacts) (changelaw ! p)) changeprops ]

instance (DdCtx a b c) => Update (BelStruct a b c) (Transformer a b c) where
  checks = [haveSameAgents]
  unsafeUpdate bls ctrf = BlS mgr newprops newlaw newobs where
    (BlS mgr newprops newlaw newobs, _) = unsafeUpdate (bls,undefined::KnState) (ctrf,undefined::KnState) -- using laziness!

instance (DdCtx a b c, DdTOI a O1 I1, DdTO a O1, DdTOI a b I1) => Update (BelScene a b c) (MultipointedEvent a b c) where
  checks = [haveSameAgents, sameManager, preCheck] where
    -- Check that BelScene and MultipointedEvent use the same manager:
    sameManager (BlS mgr _ _ _, _) (Trf mgr' _ _ _ _ , _) = mgr == mgr'
  unsafeUpdate ((bls,s) :: BelScene a b c) (trfUnshifted, eventFactsDdUnshifted) =
    update (bls,s) (trf,selectedEventState) where
      (trf@(Trf mgr addprops addlaw _ _), shiftRel) = shiftPrepare bls trfUnshifted
      eventFactsDd = relabelWith mgr (map (bimap fromEnum fromEnum) shiftRel) eventFactsDdUnshifted
      selectedEventsDD = con mgr eventFactsDd (restrictSet mgr (ddOf  bls addlaw) [ (k, P k `elem` s) | P k <- vocabOf bls ])
      eventVoc = map fromEnum addprops
      -- FIXME: avoid the conversion to BDD here - needs allSatsWith for ZDDs
      selectedEvents = allSatsWith mgr eventVoc (toB mgr (toO1 mgr (toI1 mgr eventVoc selectedEventsDD)))
      selectedEventState :: KnState
      selectedEventState =
        case selectedEvents of
          []     -> error "no selected event"
          [this] -> map (P . fst) $ filter snd this
          more   -> error $ "too many selected events: " ++ show more

-- TODO: instance Update (MultipointedBelScene a b c) (MultipointedEvent a b c)

trfPost :: (DdCtx a b c) => Event a b c -> Prp -> Dd a b c
trfPost (Trf mgr addprops _ changelaw _, x) p
  | p `elem` M.keys changelaw = restrictLaw mgr (map fromEnum addprops) (changelaw ! p) (boolDDoutof mgr x addprops)
  | otherwise                 = boolDdOf mgr $ PrpF p

reduce :: (DdCtx a b c) => Event a b c -> Form -> Maybe Form
reduce _ Top          = Just Top
reduce e Bot          = Just $ Neg $ preOf e
reduce e@(Trf mgr v _ _ _, _) (PrpF p)     = Impl (preOf e) <$> Just (ddToForm mgr v $ trfPost e p)
reduce e (Neg f)      = Impl (preOf e) . Neg <$> reduce e f
reduce e (Conj fs)    = Conj <$> mapM (reduce e) fs
reduce e (Disj fs)    = Disj <$> mapM (reduce e) fs
reduce e (Xor fs)     = Impl (preOf e) . Xor <$> mapM (reduce e) fs
reduce e (Impl f1 f2) = Impl <$> reduce e f1 <*> reduce e f2
reduce e (Equi f1 f2) = Equi <$> reduce e f1 <*> reduce e f2
reduce _ (Forall _ _) = Nothing
reduce _ (Exists _ _) = Nothing
reduce (e@(t@(Trf mgr addprops _ _ (ag, eventObs)), x) :: Event a b c) (K a f) =
  Impl (preOf e) . Conj <$> sequence
    [ K a <$> reduce (t,y) f | y <- powerset addprops -- FIXME is this a bit much?
                             , tagDdEval mgr (mv (M.size ag) x ++ cp (M.size ag) y) (Tagged $ restrict mgr (untag eventObs) (ag ! a, True) :: Tagged Dubbel (Dd a b c))
    ]
reduce e (Kw a f)     = reduce e (Disj [K a f, K a (Neg f)])
reduce _ Ck  {}       = Nothing
reduce _ Ckw {}       = Nothing
reduce (e@(t@(Trf mgr addprops _ _ (ag, eventObs)), x) :: Event a b c) (Dk ags f) =
  Impl (preOf e) . Conj <$> sequence
    [Dk ags <$> reduce (t, y) f |
       let omegai
             = Tagged
                 $ restrictSet mgr (untag eventObs)
                     $ map (\ a -> (ag ! a, a `elem` ags)) $ M.keys ag ::
                 Tagged Dubbel (Dd a b c),
       y <- powerset addprops,
       tagDdEval mgr (mv (M.size ag) x ++ cp (M.size ag) y) omegai]
reduce e (Dkw ags f)     = reduce e (Disj [Dk ags f, Dk ags (Neg f)])
reduce _ PubAnnounce  {} = Nothing
reduce _ PubAnnounceW {} = Nothing
reduce _ Announce     {} = Nothing
reduce _ AnnounceW    {} = Nothing
reduce _ Dia          {} = Nothing

ddReduce :: (DdCtx a b c) => BelScene a b c -> Event a b c -> Form -> Dd a b c
ddReduce scn@(oldBls,_) event@(Trf mgr addprops _ changelaw _, eventFacts) f =
  let
    changeprops = M.keys changelaw
    -- same as in 'transform', to ensure props and addprops are disjoint
    shiftaddprops = [(freshp $ vocabOf scn)..]
    shiftrel      = sort $ zip addprops shiftaddprops
    -- apply the shifting to addlaw and changelaw:
    changelawShifted = M.map (relabelWith mgr (map (bimap fromEnum fromEnum) shiftrel)) changelaw
    (newBlS,_) = update scn event
    -- the actual event, shifted
    actualAss  = [ (shifted, P orig `elem` eventFacts) | (P orig, P shifted) <- shiftrel ]
    postconrel = [ (n, changelawShifted ! P n) | (P n) <- changeprops ]
    -- reversing V^o to V
    copychangeprops = [(freshp $ vocabOf scn ++ map snd shiftrel)..]
    copyrelInverse  = zip copychangeprops changeprops
    restrSet = restrictSet mgr
  in
    imp mgr (ddOf  oldBls (preOf event)) $ -- 0. check if precondition holds
      relabelWith mgr (map (bimap fromEnum fromEnum) copyrelInverse) $ -- 4. changepropscopies -> original changeprops
        (`restrSet` actualAss) $    -- 3. restrict to actual event x outof V+
          substitSimul mgr postconrel $    -- 2. replace changeprops with postconditions
            ddOf  newBlS f             -- 1. boolean equivalent wrt new structure

evalViaDdReduce :: (DdCtx a b c) => BelScene a b c -> Event a b c -> Form -> Bool
evalViaDdReduce (bls@(BlS mgr _ _ _),s) event f = evalAssDD mgr (ddReduce (bls,s) event f) (\n -> P n `elem` s)