1
Fork 0
mirror of https://git.cs.ou.nl/joshua.moerman/mealy-decompose.git synced 2025-04-30 02:07:44 +02:00

more formatting

This commit is contained in:
Joshua Moerman 2024-06-14 14:43:32 +02:00
parent 8223ff9d59
commit 646b915d36
15 changed files with 425 additions and 387 deletions

View file

@ -21,14 +21,14 @@ semanticsForState _ _ [] = error ""
semanticsForState MealyMachine{..} q [a] = fst (behaviour q a) semanticsForState MealyMachine{..} q [a] = fst (behaviour q a)
semanticsForState m@MealyMachine{..} q (a : w) = semanticsForState m (snd (behaviour q a)) w semanticsForState m@MealyMachine{..} q (a : w) = semanticsForState m (snd (behaviour q a)) w
main :: IO () main :: IO ()
main = do main = do
[dotFile] <- getArgs [dotFile] <- getArgs
print dotFile print dotFile
transitions <- mapMaybe (parseMaybe parseTransFull) . lines <$> readFile dotFile transitions <- mapMaybe (parseMaybe parseTransFull) . lines <$> readFile dotFile
let machine = convertToMealy transitions let
machine = convertToMealy transitions
alphabet = inputs machine alphabet = inputs machine
tInit = initialState machine tInit = initialState machine
tOut s i = fst (behaviour machine s i) tOut s i = fst (behaviour machine s i)
@ -37,7 +37,7 @@ main = do
mq0 = semanticsForState machine (initialState machine) mq0 = semanticsForState machine (initialState machine)
mq = countingMQ (\w -> when debugOutput (print w) >> return (mq0 w)) mq = countingMQ (\w -> when debugOutput (print w) >> return (mq0 w))
let loop table = do loop table = do
lift $ putStrLn "Making the table closed and consistent" lift $ putStrLn "Making the table closed and consistent"
(table2, b) <- makeClosedAndConsistentA mq table (table2, b) <- makeClosedAndConsistentA mq table
let (hInit, size, hTransMap, hOutMap) = createHypothesis table2 let (hInit, size, hTransMap, hOutMap) = createHypothesis table2

View file

@ -1,3 +1,6 @@
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
{-# HLINT ignore "Avoid reverse" #-}
module Main where module Main where
import DotParser import DotParser
@ -10,21 +13,20 @@ import Preorder
import Control.Monad (forM_) import Control.Monad (forM_)
import Data.Bifunctor import Data.Bifunctor
import Data.List (sort, sortOn, intercalate) import Data.List (intercalate, sort, sortOn)
import Data.List.Ordered (nubSort) import Data.List.Ordered (nubSort)
import Data.Map.Strict qualified as Map import Data.Map.Strict qualified as Map
import Data.Maybe (mapMaybe) import Data.Maybe (isNothing, mapMaybe)
import Data.Set qualified as Set import Data.Set qualified as Set
import Data.Tuple (swap) import Data.Tuple (swap)
import System.Environment import System.Environment
import Text.Megaparsec import Text.Megaparsec
converseRelation :: Ord b => Map.Map a b -> Map.Map b [a] converseRelation :: Ord b => Map.Map a b -> Map.Map b [a]
converseRelation m = Map.fromListWith (++) . fmap (second pure . swap) . Map.assocs $ m converseRelation = Map.fromListWith (++) . fmap (second pure . swap) . Map.assocs
myWriteFile :: FilePath -> String -> IO () myWriteFile :: FilePath -> String -> IO ()
myWriteFile filename content = writeFile ("results/" ++ filename) content myWriteFile filename = writeFile ("results/" ++ filename)
{- {-
Hacked together, you can view the result with: Hacked together, you can view the result with:
@ -39,10 +41,7 @@ main = do
-- Read dot file -- Read dot file
[dotFile] <- getArgs [dotFile] <- getArgs
print dotFile print dotFile
transitions <- mapMaybe (parseMaybe parseTransFull) . lines <$> readFile dotFile machine <- convertToMealy . mapMaybe (parseMaybe parseTransFull) . lines <$> readFile dotFile
-- convert to mealy
let machine = convertToMealy transitions
-- print some basic info -- print some basic info
putStrLn $ (show . length $ states machine) <> " states, " <> (show . length $ inputs machine) <> " inputs and " <> (show . length $ outputs machine) <> " outputs" putStrLn $ (show . length $ states machine) <> " states, " <> (show . length $ inputs machine) <> " inputs and " <> (show . length $ outputs machine) <> " outputs"
@ -69,19 +68,23 @@ main = do
-- Then compute each projection -- Then compute each projection
-- I did some manual preprocessing, these are the only interesting bits -- I did some manual preprocessing, these are the only interesting bits
let -- outs = ["10", "10-O9", "2.2", "3.0", "3.1", "3.10", "3.12", "3.13", "3.14", "3.16", "3.17", "3.18", "3.19", "3.2", "3.20", "3.21", "3.3", "3.4", "3.6", "3.7", "3.8", "3.9", "5.0", "5.1", "5.12", "5.13", "5.17", "5.2", "5.21", "5.23", "5.6", "5.7", "5.8", "5.9", "quiescence"] let
-- outs = ["10", "10-O9", "2.2", "3.0", "3.1", "3.10", "3.12", "3.13", "3.14", "3.16", "3.17", "3.18", "3.19", "3.2", "3.20", "3.21", "3.3", "3.4", "3.6", "3.7", "3.8", "3.9", "5.0", "5.1", "5.12", "5.13", "5.17", "5.2", "5.21", "5.23", "5.6", "5.7", "5.8", "5.9", "quiescence"]
outs = outputs machine outs = outputs machine
(projections0, state2idx) = allProjections machine outs (projections0, state2idx) = allProjections machine outs
projections = zip outs $ fmap refineMealy projections0 projections = zip outs $ fmap refineMealy projections0
-- Print number of states of each projection -- Print number of states of each projection
forM_ projections (\(o, partition) -> do forM_
projections
( \(o, partition) -> do
putStr $ o <> " -> " putStr $ o <> " -> "
printPartition partition printPartition partition
) )
-- First we check for equivalent partitions, so that we skip redundant work. -- First we check for equivalent partitions, so that we skip redundant work.
let preord p1 p2 = toPreorder (comparePartitions p1 p2) let
preord p1 p2 = toPreorder (comparePartitions p1 p2)
(equiv, uniqPartitions) = equivalenceClasses preord projections (equiv, uniqPartitions) = equivalenceClasses preord projections
putStrLn "" putStrLn ""
@ -90,19 +93,24 @@ main = do
putStrLn "" putStrLn ""
putStrLn "Equivalences" putStrLn "Equivalences"
forM_ (Map.assocs equiv) (\(o2, o1) -> do forM_
putStrLn $ " " <> (show o2) <> " == " <> (show o1) (Map.assocs equiv)
( \(o2, o1) -> do
putStrLn $ " " <> show o2 <> " == " <> show o1
) )
-- Then we compare each pair of partitions. We only keep the finest -- Then we compare each pair of partitions. We only keep the finest
-- partitions, since the coarse ones don't provide value to us. -- partitions, since the coarse ones don't provide value to us.
let (topMods, downSets) = maximalElements preord uniqPartitions let
(topMods, downSets) = maximalElements preord uniqPartitions
foo (a, b) = (numBlocks b, a) foo (a, b) = (numBlocks b, a)
putStrLn "" putStrLn ""
putStrLn "Top modules" putStrLn "Top modules"
forM_ (reverse . sort . fmap foo $ topMods) (\(b, o) -> do forM_
putStrLn $ " " <> (show o) <> " has size " <> (show b) (reverse . sort . fmap foo $ topMods)
( \(b, o) -> do
putStrLn $ " " <> show o <> " has size " <> show b
) )
-- Then we try to combine paritions, so that we don't end up with -- Then we try to combine paritions, so that we don't end up with
@ -114,35 +122,37 @@ main = do
projmap <- heuristicMerger topMods strategy projmap <- heuristicMerger topMods strategy
-- Now we are going to output the components we found. -- Now we are going to output the components we found.
let equivInv = converseRelation equiv let
equivInv = converseRelation equiv
projmapN = zip projmap [1 :: Int ..] projmapN = zip projmap [1 :: Int ..]
action ((os, p), componentIdx) = do
forM_ projmapN (\((os, p), i) -> do let
let name = intercalate "x" os name = intercalate "x" os
osWithRel = concat $ os : [Map.findWithDefault [] o downSets | o <- os] osWithRel = concat $ os : [Map.findWithDefault [] o downSets | o <- os]
osWithRelAndEquiv = concat $ osWithRel : [Map.findWithDefault [] o equivInv | o <- osWithRel] osWithRelAndEquiv = concat $ osWithRel : [Map.findWithDefault [] o equivInv | o <- osWithRel]
componentOutputs = Set.fromList osWithRelAndEquiv componentOutputs = Set.fromList osWithRelAndEquiv
proj = projectToComponent (flip Set.member componentOutputs) machine proj = projectToComponent (`Set.member` componentOutputs) machine
-- Sanity check: compute partition again -- Sanity check: compute partition again
partition = refineMealy . mealyMachineToEncoding $ proj partition = refineMealy . mealyMachineToEncoding $ proj
putStrLn $ "" putStrLn ""
putStrLn $ "Component " <> show os putStrLn $ "Component " <> show os
putStrLn $ "Correct? " <> show (comparePartitions p partition) putStrLn $ "Correct? " <> show (comparePartitions p partition)
putStrLn $ "Size = " <> show (numBlocks p) putStrLn $ "Size = " <> show (numBlocks p)
(do do
let filename = "partition_" <> show i <> ".dot" let
filename = "partition_" <> show componentIdx <> ".dot"
idx2State = Map.map head . converseRelation $ state2idx idx2State = Map.map head . converseRelation $ state2idx
stateBlocks = fmap (fmap (idx2State Map.!)) . Partition.toBlocks $ partition stateBlocks = fmap (fmap (idx2State Map.!)) . Partition.toBlocks $ partition
content = unlines . fmap (intercalate " ") $ stateBlocks content = unlines . fmap unwords $ stateBlocks
putStrLn $ "Output (partition) in file " <> filename putStrLn $ "Output (partition) in file " <> filename
myWriteFile filename content myWriteFile filename content
)
(do do
let MealyMachine{..} = proj let
MealyMachine{..} = proj
-- We enumerate all transitions in the full automaton -- We enumerate all transitions in the full automaton
transitions = [(s, i, o, t) | s <- states, i <- inputs, let (o, t) = behaviour s i] transitions = [(s, i, o, t) | s <- states, i <- inputs, let (o, t) = behaviour s i]
-- This is the quotient map, from state to block -- This is the quotient map, from state to block
@ -155,18 +165,18 @@ main = do
initialFirst = sortOn (\(s, _, _, _) -> s /= initialBlock) transitionsBlocks initialFirst = sortOn (\(s, _, _, _) -> s /= initialBlock) transitionsBlocks
-- Convert to a file -- Convert to a file
filename1 = "component_" <> show i <> ".dot" filename1 = "component_" <> show componentIdx <> ".dot"
content1 = toString . mealyToDot name $ initialFirst content1 = toString . mealyToDot name $ initialFirst
-- So far so good, `initialFirst` could serve as our output -- So far so good, `initialFirst` could serve as our output
-- But we do one more optimisation on the machine -- But we do one more optimisation on the machine
-- We remove inputs, on which the machine does nothing -- We remove inputs, on which the machine does nothing
deadInputs0 = Map.fromListWith (++) . fmap (\(s, i, o, t) -> (i, [(s, o, t)])) $ initialFirst deadInputs0 = Map.fromListWith (++) . fmap (\(s, i, o, t) -> (i, [(s, o, t)])) $ initialFirst
deadInputs = Map.keysSet . Map.filter (all (\(s,o,t) -> s == t && o == Nothing)) $ deadInputs0 deadInputs = Map.keysSet . Map.filter (all (\(s, o, t) -> s == t && isNothing o)) $ deadInputs0
result = filter (\(_, i, _, _) -> i `Set.notMember` deadInputs) initialFirst result = filter (\(_, i, _, _) -> i `Set.notMember` deadInputs) initialFirst
-- Convert to a file -- Convert to a file
filename2 = "component_reduced_" <> show i <> ".dot" filename2 = "component_reduced_" <> show componentIdx <> ".dot"
content2 = toString . mealyToDot name $ result content2 = toString . mealyToDot name $ result
putStrLn $ "Output (reduced machine) in file " <> filename1 putStrLn $ "Output (reduced machine) in file " <> filename1
@ -176,7 +186,5 @@ main = do
putStrLn $ "Output (reduced machine) in file " <> filename2 putStrLn $ "Output (reduced machine) in file " <> filename2
myWriteFile filename2 content2 myWriteFile filename2 content2
)
)
return () mapM_ action projmapN

View file

@ -1,5 +1,6 @@
{-# language PartialTypeSignatures #-} {-# LANGUAGE PartialTypeSignatures #-}
{-# OPTIONS_GHC -Wno-partial-type-signatures #-} {-# OPTIONS_GHC -Wno-partial-type-signatures #-}
module Main where module Main where
import SplittingTree import SplittingTree
@ -21,7 +22,7 @@ genTransitions size inputs outputs = do
randomOutput = (outputs !!) <$> liftRand (uniformR (0, numOutputs - 1)) randomOutput = (outputs !!) <$> liftRand (uniformR (0, numOutputs - 1))
randomTarget = (states !!) <$> liftRand (uniformR (0, size - 1)) randomTarget = (states !!) <$> liftRand (uniformR (0, size - 1))
randomTransition s i = (\o t -> (s, i, o, t)) <$> randomOutput <*> randomTarget randomTransition s i = (\o t -> (s, i, o, t)) <$> randomOutput <*> randomTarget
t <- sequence $ (\s i -> randomTransition s i) <$> states <*> inputs t <- sequence $ randomTransition <$> states <*> inputs
return (states, t) return (states, t)
-- numC <= 8 -- numC <= 8
@ -34,10 +35,13 @@ genComposition size numC inputs = do
allTransitions <- traverse (\c -> genTransitions size inputs (outputSets !! c)) components allTransitions <- traverse (\c -> genTransitions size inputs (outputSets !! c)) components
let let
productState = fmap (Map.fromList . zip components) (sequence (fmap fst allTransitions)) productState = fmap (Map.fromList . zip components) (mapM fst allTransitions)
allStates = (,) <$> components <*> productState allStates = (,) <$> components <*> productState
compMap = Map.fromList $ zip components (fmap (Map.fromList . fmap (\(s, i, o, t) -> ((s, i), (o, t)))) . fmap snd $ allTransitions) compMap = Map.fromList $ zip components ((Map.fromList . fmap (\(s, i, o, t) -> ((s, i), (o, t)))) . snd <$> allTransitions)
norm c = if c <= 0 then c + numC else if c > numC then c - numC else c norm c
| c <= 0 = c + numC
| c > numC = c - numC
| otherwise = c
transition (c, cs) 'L' = (norm (c - 1), cs) transition (c, cs) 'L' = (norm (c - 1), cs)
transition (c, cs) 'R' = (norm (c + 1), cs) transition (c, cs) 'R' = (norm (c + 1), cs)
transition (c, cs) x = (c, Map.adjust (\s -> snd (compMap Map.! c Map.! (s, x))) c cs) transition (c, cs) x = (c, Map.adjust (\s -> snd (compMap Map.! c Map.! (s, x))) c cs)
@ -75,7 +79,7 @@ main = do
states = Map.elems reachableMap states = Map.elems reachableMap
init = reachableMap Map.! init0 init = reachableMap Map.! init0
trans s i = reachableMap Map.! trans0 (inverseMap Map.! s) i trans s i = reachableMap Map.! trans0 (inverseMap Map.! s) i
outpf s i = outpf0 (inverseMap Map.! s) i outpf s = outpf0 (inverseMap Map.! s)
-- minimize -- minimize
let let

View file

@ -335,9 +335,7 @@ class Encoder:
# Even omzetten in een makkelijkere data structuur # Even omzetten in een makkelijkere data structuur
m = self.solver.get_model() m = self.solver.get_model()
model = {} model = {abs(l): l > 0 for l in m}
for l in m:
model[abs(l)] = l > 0
if self.args.verbose: if self.args.verbose:
for rid in self.rids: for rid in self.rids:

View file

@ -4,7 +4,6 @@ import Data.List (find)
import Data.Map.Strict qualified as Map import Data.Map.Strict qualified as Map
import Data.Sequence qualified as Seq import Data.Sequence qualified as Seq
-- Dit is niet de echte union-find datastructuur (niet erg efficient), -- Dit is niet de echte union-find datastructuur (niet erg efficient),
-- maar wel simpel en beter dan niks. -- maar wel simpel en beter dan niks.
newtype UnionFind x = MkUnionFind {unUnionFind :: Map.Map x x} newtype UnionFind x = MkUnionFind {unUnionFind :: Map.Map x x}
@ -15,10 +14,9 @@ empty = MkUnionFind Map.empty
-- Omdat we een pure interface hebben, doen we hier geen path-compression. -- Omdat we een pure interface hebben, doen we hier geen path-compression.
equivalent :: Ord x => x -> x -> UnionFind x -> Bool equivalent :: Ord x => x -> x -> UnionFind x -> Bool
equivalent x y (MkUnionFind m) = root x == root y where equivalent x y (MkUnionFind m) = root x == root y
root z = case Map.lookup z m of where
Nothing -> z root z = maybe z root (Map.lookup z m)
Just w -> root w
-- Hier kunnen we wel path-compression doen. We zouden ook nog een rank -- Hier kunnen we wel path-compression doen. We zouden ook nog een rank
-- optimalisatie kunnen (moeten?) doen. Maar dan moeten we meer onthouden. -- optimalisatie kunnen (moeten?) doen. Maar dan moeten we meer onthouden.
@ -34,8 +32,6 @@ equate x y (MkUnionFind m1) =
Nothing -> (z, m) Nothing -> (z, m)
Just w -> Map.insert z r <$> rootCP w m r Just w -> Map.insert z r <$> rootCP w m r
-- Bisimulatie in 1 machine -- Bisimulatie in 1 machine
bisimulation :: (Eq o, Ord s) => [i] -> (s -> i -> o) -> (s -> i -> s) -> s -> s -> Maybe [i] bisimulation :: (Eq o, Ord s) => [i] -> (s -> i -> o) -> (s -> i -> s) -> s -> s -> Maybe [i]
bisimulation alphabet output transition x y = go (Seq.singleton ([], x, y)) empty bisimulation alphabet output transition x y = go (Seq.singleton ([], x, y)) empty

View file

@ -49,15 +49,17 @@ parseTransFull :: Parser Trans
parseTransFull = space *> parseTrans <* eof parseTransFull = space *> parseTrans <* eof
convertToMealy :: [Trans] -> MealyMachine String String String convertToMealy :: [Trans] -> MealyMachine String String String
convertToMealy l = MealyMachine convertToMealy l =
MealyMachine
{ states = states { states = states
, inputs = ins , inputs = ins
, outputs = outs , outputs = outs
, behaviour = \s i -> base Map.! (s, i) , behaviour = curry (base Map.!)
, initialState = (\(a, _, _, _) -> a) . head $ l , initialState = (\(a, _, _, _) -> a) . head $ l
-- ^ Assumption: first transition in the file belongs to the initial state
} }
where where
-- \^ Assumption: first transition in the file belongs to the initial state
froms = OrdList.nubSort . fmap (\(a, _, _, _) -> a) $ l froms = OrdList.nubSort . fmap (\(a, _, _, _) -> a) $ l
tos = OrdList.nubSort . fmap (\(_, a, _, _) -> a) $ l tos = OrdList.nubSort . fmap (\(_, a, _, _) -> a) $ l
ins = OrdList.nubSort . fmap (\(_, _, i, _) -> i) $ l ins = OrdList.nubSort . fmap (\(_, _, i, _) -> i) $ l

View file

@ -73,7 +73,7 @@ inconsistencies table@LStarState{..} = defects
equivalentPairs = [(r1, r2) | r1 <- Set.toList rowIndices, r2 <- Set.toList rowIndices, r1 < r2, row table r1 == row table r2] equivalentPairs = [(r1, r2) | r1 <- Set.toList rowIndices, r2 <- Set.toList rowIndices, r1 < r2, row table r1 == row table r2]
defects = [(a, s) | (r1, r2) <- equivalentPairs, a <- alphabet, let d = differenceOfRows r1 r2 a, s <- Map.keys d] defects = [(a, s) | (r1, r2) <- equivalentPairs, a <- alphabet, let d = differenceOfRows r1 r2 a, s <- Map.keys d]
differenceOfRows r1 r2 a = differenceOfMaps (row table (r1 `snoc` a)) (row table (r2 `snoc` a)) differenceOfRows r1 r2 a = differenceOfMaps (row table (r1 `snoc` a)) (row table (r2 `snoc` a))
differenceOfMaps m1 m2 = MapMerge.merge MapMerge.dropMissing MapMerge.dropMissing (MapMerge.zipWithMaybeMatched (\_ x y -> if x == y then Nothing else Just ())) m1 m2 differenceOfMaps = MapMerge.merge MapMerge.dropMissing MapMerge.dropMissing (MapMerge.zipWithMaybeMatched (\_ x y -> if x == y then Nothing else Just ()))
-- Preconditie: tabel is gesloten en consistent -- Preconditie: tabel is gesloten en consistent
-- TODO: misschien checken of de automaat uniek is? De constructie van -- TODO: misschien checken of de automaat uniek is? De constructie van
@ -83,7 +83,7 @@ createHypothesis :: (Ord i, Ord o) => LStarState i o -> (Int, Int, Map.Map (Int,
createHypothesis table@LStarState{..} = (initialState, Map.size row2IntMap, transitions, outputs) createHypothesis table@LStarState{..} = (initialState, Map.size row2IntMap, transitions, outputs)
where where
rowIndicesLs = Set.toList rowIndices rowIndicesLs = Set.toList rowIndices
upperRows = map (row table) $ rowIndicesLs upperRows = map (row table) rowIndicesLs
row2IntMap = Map.fromList $ zip upperRows [0..] row2IntMap = Map.fromList $ zip upperRows [0..]
row2Int = (Map.!) row2IntMap . row table row2Int = (Map.!) row2IntMap . row table
transitions = Map.fromList [((row2Int r, a), row2Int (r `snoc` a)) | r <- rowIndicesLs, a <- alphabet] transitions = Map.fromList [((row2Int r, a), row2Int (r `snoc` a)) | r <- rowIndicesLs, a <- alphabet]
@ -97,7 +97,7 @@ createHypothesis table@LStarState{..} = (initialState, Map.size row2IntMap, tran
-- Een lege tabel heeft altijd een "epsilon-rij" en voor elk symbool een kolom. -- Een lege tabel heeft altijd een "epsilon-rij" en voor elk symbool een kolom.
-- (Omdat het Mealy machines zijn.) -- (Omdat het Mealy machines zijn.)
initialiseA :: (Applicative f, Ord i) => [i] -> MQ f i o -> f (LStarState i o) initialiseA :: (Applicative f, Ord i) => [i] -> MQ f i o -> f (LStarState i o)
initialiseA alphabet mq = initialiseWithA alphabet [mempty] (map pure alphabet) mq initialiseA alphabet = initialiseWithA alphabet [mempty] (map pure alphabet)
-- We kunnen ook een tabel maken met voorgedefinieerde rijen en kolommen. -- We kunnen ook een tabel maken met voorgedefinieerde rijen en kolommen.
initialiseWithA :: (Applicative f, Ord i) => [i] -> [Word i] -> [Word i] -> MQ f i o -> f (LStarState i o) initialiseWithA :: (Applicative f, Ord i) => [i] -> [Word i] -> [Word i] -> MQ f i o -> f (LStarState i o)
@ -106,7 +106,7 @@ initialiseWithA alphabet rowIdcs colIdcs mq = (\content -> LStarState{..}) <$> c
rowIndices = Set.fromList rowIdcs rowIndices = Set.fromList rowIdcs
colIndices = Set.fromList colIdcs colIndices = Set.fromList colIdcs
queries = [p <> m <> s | p <- rowIdcs, m <- []:fmap pure alphabet, s <- colIdcs] queries = [p <> m <> s | p <- rowIdcs, m <- []:fmap pure alphabet, s <- colIdcs]
contentA = Map.traverseWithKey (\w _ -> mq w) . Map.fromList . zip queries $ repeat () contentA = Map.traverseWithKey (\ w _ -> mq w) . Map.fromList . map (, ()) $ queries
-- preconditie: newRowIndices is disjunct van de huidige rowIndices en de -- preconditie: newRowIndices is disjunct van de huidige rowIndices en de
-- vereniging is prefix-gesloten. (Wordt niet gechecked.) -- vereniging is prefix-gesloten. (Wordt niet gechecked.)
@ -116,7 +116,7 @@ addRowsA newRowIndices mq table@LStarState{..} = (\newContent -> table
, rowIndices = rowIndices <> Set.fromList newRowIndices }) <$> contentA , rowIndices = rowIndices <> Set.fromList newRowIndices }) <$> contentA
where where
queries = [w | p <- newRowIndices, m <- []:fmap pure alphabet, s <- Set.toList colIndices, let w = p <> m <> s, w `Map.notMember` content] queries = [w | p <- newRowIndices, m <- []:fmap pure alphabet, s <- Set.toList colIndices, let w = p <> m <> s, w `Map.notMember` content]
contentA = Map.traverseWithKey (\w _ -> mq w) . Map.fromList . zip queries $ repeat () contentA = Map.traverseWithKey (\ w _ -> mq w) . Map.fromList . map (, ()) $ queries
-- preconditie: zie addRows (?) -- preconditie: zie addRows (?)
addColumnsA :: (Applicative f, Ord i) => [Word i] -> MQ f i o -> LStarState i o -> f (LStarState i o) addColumnsA :: (Applicative f, Ord i) => [Word i] -> MQ f i o -> LStarState i o -> f (LStarState i o)
@ -125,7 +125,7 @@ addColumnsA newColIndices mq table@LStarState{..} = (\newContent -> table
, colIndices = colIndices <> Set.fromList newColIndices }) <$> contentA , colIndices = colIndices <> Set.fromList newColIndices }) <$> contentA
where where
queries = [w | p <- Set.toList rowIndices, m <- []:fmap pure alphabet, s <- newColIndices, let w = p <> m <> s, w `Map.notMember` content] queries = [w | p <- Set.toList rowIndices, m <- []:fmap pure alphabet, s <- newColIndices, let w = p <> m <> s, w `Map.notMember` content]
contentA = Map.traverseWithKey (\w _ -> mq w) . Map.fromList . zip queries $ repeat () contentA = Map.traverseWithKey (\ w _ -> mq w) . Map.fromList . map (, ()) $ queries
------------------ ------------------

View file

@ -15,8 +15,9 @@ transitionFunction :: MealyMachine s i o -> s -> i -> s
transitionFunction MealyMachine{..} s i = snd (behaviour s i) transitionFunction MealyMachine{..} s i = snd (behaviour s i)
exampleMM :: MealyMachine String Char String exampleMM :: MealyMachine String Char String
exampleMM = exampleMM = MealyMachine{..}
let states = ["q0", "q1", "q2", "q3"] where
states = ["q0", "q1", "q2", "q3"]
inputs = ['a', 'b'] inputs = ['a', 'b']
outputs = ["een", "twee", "drie", "vier"] outputs = ["een", "twee", "drie", "vier"]
behaviour "q0" 'a' = ("een", "q1") behaviour "q0" 'a' = ("een", "q1")
@ -29,4 +30,3 @@ exampleMM =
behaviour "q3" 'b' = ("twee", "q1") behaviour "q3" 'b' = ("twee", "q1")
behaviour _ _ = error "undefined behaviour of exampleMM" behaviour _ _ = error "undefined behaviour of exampleMM"
initialState = "q0" initialState = "q0"
in MealyMachine{..}

View file

@ -5,8 +5,8 @@ import Partition (Partition)
import Control.Monad.ST (runST) import Control.Monad.ST (runST)
import Copar.Algorithm (refine) import Copar.Algorithm (refine)
import Copar.Functors.Polynomial (Polynomial, PolyF1(..)) import Copar.Functors.Polynomial (PolyF1 (..), Polynomial)
import Copar.RefinementInterface (Label, F1) import Copar.RefinementInterface (F1, Label)
import Data.Bool (bool) import Data.Bool (bool)
import Data.CoalgebraEncoding (Encoding (..)) import Data.CoalgebraEncoding (Encoding (..))
import Data.List.Ordered (nubSort) import Data.List.Ordered (nubSort)
@ -16,7 +16,8 @@ import Data.Vector qualified
import Data.Vector.Unboxed qualified import Data.Vector.Unboxed qualified
project :: Ord u => (o -> u) -> MealyMachine s i o -> MealyMachine s i u project :: Ord u => (o -> u) -> MealyMachine s i o -> MealyMachine s i u
project f MealyMachine{..} = MealyMachine project f MealyMachine{..} =
MealyMachine
{ outputs = nubSort $ fmap f outputs -- inefficient { outputs = nubSort $ fmap f outputs -- inefficient
, behaviour = \s i -> case behaviour s i of , behaviour = \s i -> case behaviour s i of
(out, s2) -> (f out, s2) (out, s2) -> (f out, s2)
@ -28,7 +29,8 @@ projectToBit o = project (o ==)
projectToComponent :: Ord o => (o -> Bool) -> MealyMachine s i o -> MealyMachine s i (Maybe o) projectToComponent :: Ord o => (o -> Bool) -> MealyMachine s i o -> MealyMachine s i (Maybe o)
projectToComponent oPred = project oMaybe projectToComponent oPred = project oMaybe
where oMaybe o where
oMaybe o
| oPred o = Just o | oPred o = Just o
| otherwise = Nothing | otherwise = Nothing
@ -49,15 +51,21 @@ allProjections MealyMachine{..} outs = (fmap mkEncoding outs, state2idx)
edgesLabel = Data.Vector.generate (numStates * numInputs) (snd . stateInputIndex) edgesLabel = Data.Vector.generate (numStates * numInputs) (snd . stateInputIndex)
edgesTo = Data.Vector.Unboxed.generate (numStates * numInputs) ((state2idx Map.!) . snd . (\(s, i) -> behaviour (idx2state Map.! s) (idx2input Map.! i)) . stateInputIndex) edgesTo = Data.Vector.Unboxed.generate (numStates * numInputs) ((state2idx Map.!) . snd . (\(s, i) -> behaviour (idx2state Map.! s) (idx2input Map.! i)) . stateInputIndex)
bool2Int = bool 0 1 bool2Int = bool 0 1
structure o = Data.Vector.generate numStates structure o =
(\s -> PolyF1 Data.Vector.generate
numStates
( \s ->
PolyF1
{ polyF1Summand = 0 -- There is only one summand { polyF1Summand = 0 -- There is only one summand
, polyF1Variables = numInputs -- This many transitions per state , polyF1Variables = numInputs -- This many transitions per state
, polyF1Constants = Data.Vector.Unboxed.generate numInputs , polyF1Constants =
(\i -> bool2Int . (o==) . fst $ (behaviour (idx2state Map.! s) (idx2input Map.! i))) Data.Vector.Unboxed.generate
numInputs
(\i -> bool2Int . (o ==) . fst $ behaviour (idx2state Map.! s) (idx2input Map.! i))
} }
) )
mkEncoding o = Encoding mkEncoding o =
Encoding
{ eStructure = structure o { eStructure = structure o
, eInitState = Nothing -- not needed , eInitState = Nothing -- not needed
, eEdgesFrom = edgesFrom , eEdgesFrom = edgesFrom
@ -76,12 +84,17 @@ mealyMachineToEncoding MealyMachine{..} =
idx2state = Map.fromList $ zip [0 ..] states idx2state = Map.fromList $ zip [0 ..] states
idx2input = Map.fromList $ zip [0 ..] inputs idx2input = Map.fromList $ zip [0 ..] inputs
out2idx = Map.fromList $ zip outputs [0 ..] out2idx = Map.fromList $ zip outputs [0 ..]
eStructure = Data.Vector.generate numStates eStructure =
(\s -> PolyF1 Data.Vector.generate
numStates
( \s ->
PolyF1
{ polyF1Summand = 0 -- There is only one summand { polyF1Summand = 0 -- There is only one summand
, polyF1Variables = numInputs -- This many transitions per state , polyF1Variables = numInputs -- This many transitions per state
, polyF1Constants = Data.Vector.Unboxed.generate numInputs , polyF1Constants =
(\i -> out2idx Map.! (fst (behaviour (idx2state Map.! s) (idx2input Map.! i)))) Data.Vector.Unboxed.generate
numInputs
(\i -> out2idx Map.! fst (behaviour (idx2state Map.! s) (idx2input Map.! i)))
} }
) )
eInitState = Nothing eInitState = Nothing

View file

@ -29,7 +29,8 @@ heuristicMerger components strategy = do
return $ Map.assocs projmap return $ Map.assocs projmap
where where
score ps p3 = numBlocks p3 - sum (fmap numBlocks ps) score ps p3 = numBlocks p3 - sum (fmap numBlocks ps)
combine ops = let os = fmap fst ops combine ops =
let os = fmap fst ops
ps = fmap snd ops ps = fmap snd ops
p3 = foldr1 commonRefinement ps p3 = foldr1 commonRefinement ps
in ((os, p3), score ps p3) in ((os, p3), score ps p3)

View file

@ -1,14 +1,14 @@
module Partition module Partition (
( module Partition module Partition,
, module Data.Partition module Data.Partition,
) where ) where
import Preorder import Preorder
import Control.Monad.Trans.State.Strict (runState, get, put) import Control.Monad.Trans.State.Strict (get, put, runState)
import Data.Coerce (coerce) import Data.Coerce (coerce)
import Data.Map.Strict qualified as Map import Data.Map.Strict qualified as Map
import Data.Partition (Partition(..), numStates, blockOfState, toBlocks) import Data.Partition (Partition (..), blockOfState, numStates, toBlocks)
import Data.Partition.Common (Block (..)) import Data.Partition.Common (Block (..))
import Data.Vector qualified as V import Data.Vector qualified as V
@ -62,7 +62,8 @@ toPreorder Incomparable = IC'
comparePartitions :: Partition -> Partition -> Comparison comparePartitions :: Partition -> Partition -> Comparison
comparePartitions p1 p2 comparePartitions p1 p2
| p1 == p2 = Equivalent | p1 == p2 = Equivalent
| otherwise = let glb = commonRefinement p1 p2 | otherwise =
let glb = commonRefinement p1 p2
n1 = numBlocks p1 n1 = numBlocks p1
n2 = numBlocks p2 n2 = numBlocks p2
n3 = numBlocks glb n3 = numBlocks glb

View file

@ -1,14 +1,14 @@
{-# language PatternSynonyms #-} {-# LANGUAGE PatternSynonyms #-}
module Preorder where module Preorder where
{- | -- \|
This modules includes some algorithms to deal with preorders. For our use-case -- This modules includes some algorithms to deal with preorders. For our use-case
it could be done efficiently with a single function. But this makes it a bit -- it could be done efficiently with a single function. But this makes it a bit
unwieldy. So I have separated it into two functions: -- unwieldy. So I have separated it into two functions:
1. One function takes a preorder and computes the equivalence classes. -- 1. One function takes a preorder and computes the equivalence classes.
2. The second function takes the order into account (now only on the -- 2. The second function takes the order into account (now only on the
representatives of the first function) and returns the "top" elements. -- representatives of the first function) and returns the "top" elements.
-}
import Control.Monad.Trans.Writer.Lazy (runWriter, tell) import Control.Monad.Trans.Writer.Lazy (runWriter, tell)
import Data.Bifunctor import Data.Bifunctor
@ -19,10 +19,15 @@ import Data.Set qualified as Set
type PartialOrdering = Maybe Ordering type PartialOrdering = Maybe Ordering
pattern EQ', LT', GT', IC' :: PartialOrdering pattern EQ', LT', GT', IC' :: PartialOrdering
pattern EQ' = Just EQ -- ^ Equivalent (or equal) pattern EQ' = Just EQ
pattern LT' = Just LT -- ^ Strictly less than -- \^ Equivalent (or equal)
pattern GT' = Just GT -- ^ Strictly greater than pattern LT' = Just LT
pattern IC' = Nothing -- ^ Incomparable -- \^ Strictly less than
pattern GT' = Just GT
-- \^ Strictly greater than
pattern IC' = Nothing
-- \^ Incomparable
-- | A preorder should satisfy reflexivity and transitivity. It is not assumed -- | A preorder should satisfy reflexivity and transitivity. It is not assumed
-- to be anti-symmetric. -- to be anti-symmetric.

View file

@ -1,13 +1,14 @@
{-# language PartialTypeSignatures #-} {-# LANGUAGE PartialTypeSignatures #-}
{-# OPTIONS_GHC -Wno-partial-type-signatures #-} {-# OPTIONS_GHC -Wno-partial-type-signatures #-}
module SplittingTree where module SplittingTree where
import Control.Monad.Trans.Class import Control.Monad.Trans.Class
import Control.Monad.Trans.State import Control.Monad.Trans.State
import Data.Map.Strict qualified as Map
import Data.Coerce (coerce) import Data.Coerce (coerce)
import Data.Foldable (traverse_)
import Data.List (sortOn) import Data.List (sortOn)
import Data.Map.Strict qualified as Map
newtype Block = Block Int newtype Block = Block Int
deriving (Eq, Ord, Read, Show, Enum) deriving (Eq, Ord, Read, Show, Enum)
@ -63,7 +64,8 @@ data PRState s i o = PRState
deriving (Show) deriving (Show)
updatePartition :: (Monad m, Ord s) => s -> Block -> StateT (PRState s i o) m () updatePartition :: (Monad m, Ord s) => s -> Block -> StateT (PRState s i o) m ()
updatePartition s b = modify foo where updatePartition s b = modify foo
where
foo prs = prs{partition = coerce (Map.insert s b) (partition prs)} foo prs = prs{partition = coerce (Map.insert s b) (partition prs)}
updateSize :: Monad m => Block -> Int -> StateT (PRState s i o) m Int updateSize :: Monad m => Block -> Int -> StateT (PRState s i o) m Int
@ -78,9 +80,11 @@ genNextBlockId = do
return idx return idx
updateParent :: Monad m => Either Block InnerNode -> InnerNode -> o -> StateT (PRState s i o) m () updateParent :: Monad m => Either Block InnerNode -> InnerNode -> o -> StateT (PRState s i o) m ()
updateParent (Left block) target output = modify foo where updateParent (Left block) target output = modify foo
where
foo prs = prs{splittingTree = (splittingTree prs){blockParent = Map.insert block (target, output) (blockParent (splittingTree prs))}} foo prs = prs{splittingTree = (splittingTree prs){blockParent = Map.insert block (target, output) (blockParent (splittingTree prs))}}
updateParent (Right node) target output = modify foo where updateParent (Right node) target output = modify foo
where
foo prs = prs{splittingTree = (splittingTree prs){innerParent = Map.insert node (target, output) (innerParent (splittingTree prs))}} foo prs = prs{splittingTree = (splittingTree prs){innerParent = Map.insert node (target, output) (innerParent (splittingTree prs))}}
updateLabel :: Monad m => InnerNode -> [i] -> StateT (PRState s i o) m () updateLabel :: Monad m => InnerNode -> [i] -> StateT (PRState s i o) m ()
@ -111,10 +115,10 @@ refineWithSplitter action rev Splitter{..} = do
tempChildsMaps = Map.map (Map.fromListWith (++)) . Map.fromListWith (++) $ tempChildsList tempChildsMaps = Map.map (Map.fromListWith (++)) . Map.fromListWith (++) $ tempChildsList
-- Now we need to check the 3-way split: -- Now we need to check the 3-way split:
-- * Some blocks have no states which occured, these don't appear. -- \* Some blocks have no states which occured, these don't appear.
-- * Some blocks have all states move to a single subblock, this is not a -- \* Some blocks have all states move to a single subblock, this is not a
-- proper split and should be removed. -- proper split and should be removed.
-- * Some blocks have different outputs (a proper split) or states which -- \* Some blocks have different outputs (a proper split) or states which
-- moved and states which didn't. -- moved and states which didn't.
properSplit b os properSplit b os
| Map.null os = error "Should not happen" | Map.null os = error "Should not happen"
@ -130,7 +134,7 @@ refineWithSplitter action rev Splitter{..} = do
-- Create a new sub-block -- Create a new sub-block
nBIdx <- genNextBlockId nBIdx <- genNextBlockId
-- Set all states to that id -- Set all states to that id
mapM_ (\s -> updatePartition s nBIdx) ls mapM_ (`updatePartition` nBIdx) ls
-- And update the tree -- And update the tree
updateParent (Left nBIdx) nNIdx o updateParent (Left nBIdx) nNIdx o
n <- updateSize nBIdx (length ls) n <- updateSize nBIdx (length ls)
@ -163,17 +167,20 @@ refineWithSplitter action rev Splitter{..} = do
-- sure it's worth the effort, so we only do it when we can remove a -- sure it's worth the effort, so we only do it when we can remove a
-- subblock. -- subblock.
if missingSize == 0 if missingSize == 0
then let ls = Map.toList sizesAndSubblocks then
let ls = Map.toList sizesAndSubblocks
-- TODO: sort(On) is unnecessarily expensive, we only need to -- TODO: sort(On) is unnecessarily expensive, we only need to
-- know the biggest... -- know the biggest...
((o1, _) : smallerBlocks) = sortOn (\(_, (n, _)) -> -n) ls ((o1, _) : smallerBlocks) = sortOn (\(_, (n, _)) -> -n) ls
in in return
return Splitter Splitter
{ split = Map.fromList (fmap (\(o, (_, lss)) -> (o, lss)) smallerBlocks) { split = Map.fromList (fmap (\(o, (_, lss)) -> (o, lss)) smallerBlocks)
, leftOut = o1 , leftOut = o1
, witness = newWitness , witness = newWitness
} }
else return Splitter else
return
Splitter
{ split = Map.map snd sizesAndSubblocks { split = Map.map snd sizesAndSubblocks
, leftOut = leftOut , leftOut = leftOut
, witness = newWitness , witness = newWitness
@ -200,7 +207,7 @@ refineWithOutput action out = do
-- Create a new sub-block -- Create a new sub-block
nBIdx <- genNextBlockId nBIdx <- genNextBlockId
-- Set all states to that id -- Set all states to that id
mapM_ (\s -> updatePartition s nBIdx) ss mapM_ (`updatePartition` nBIdx) ss
-- And update the tree -- And update the tree
updateParent (Left nBIdx) nNIdx o updateParent (Left nBIdx) nNIdx o
_ <- updateSize nBIdx (length ss) _ <- updateSize nBIdx (length ss)
@ -213,7 +220,7 @@ refineWithOutput action out = do
-- For the remaining blocks, we update the partition -- For the remaining blocks, we update the partition
nNIdx <- genNextNodeId nNIdx <- genNextNodeId
_ <- traverse (\(o, ss) -> updateStates nNIdx o ss) smaller traverse_ (uncurry (updateStates nNIdx)) smaller
-- If we are doing the very first split, the nNIdx node does not have a -- If we are doing the very first split, the nNIdx node does not have a
-- parent. So we don't have to do updates. Now nNIdx will be the root. -- parent. So we don't have to do updates. Now nNIdx will be the root.
@ -228,7 +235,8 @@ refineWithOutput action out = do
_ <- updateSize b (length biggest) _ <- updateSize b (length biggest)
-- Return the splitter, not that we already skipped the larger part. -- Return the splitter, not that we already skipped the larger part.
return Splitter return
Splitter
{ split = Map.fromList smaller { split = Map.fromList smaller
, leftOut = o1 , leftOut = o1
, witness = witness , witness = witness
@ -237,10 +245,12 @@ refineWithOutput action out = do
Map.elems <$> Map.traverseWithKey updateBlock tempChildsMaps2 Map.elems <$> Map.traverseWithKey updateBlock tempChildsMaps2
initialPRState :: Ord s => [s] -> PRState s i o initialPRState :: Ord s => [s] -> PRState s i o
initialPRState ls = PRState initialPRState ls =
PRState
{ partition = Partition . Map.fromList $ [(s, Block 0) | s <- ls] { partition = Partition . Map.fromList $ [(s, Block 0) | s <- ls]
, nextBlockId = Block 1 , nextBlockId = Block 1
, splittingTree = SplittingTree , splittingTree =
SplittingTree
{ label = Map.empty { label = Map.empty
, innerParent = Map.empty , innerParent = Map.empty
, blockParent = Map.empty , blockParent = Map.empty
@ -249,13 +259,13 @@ initialPRState ls = PRState
, nextNodeId = Node 0 , nextNodeId = Node 0
} }
refineWithAllOutputs :: (Monad m, Ord o, Ord s) => [(i, (s -> o))] -> StateT (PRState s i o) m [Splitter s i o] refineWithAllOutputs :: (Monad m, Ord o, Ord s) => [(i, s -> o)] -> StateT (PRState s i o) m [Splitter s i o]
refineWithAllOutputs ls = concat <$> traverse (uncurry refineWithOutput) ls refineWithAllOutputs ls = concat <$> traverse (uncurry refineWithOutput) ls
refineWithSplitterAllInputs :: (Monad m, Ord o, Ord s) => [(i, (s -> [s]))] -> Splitter s i o -> StateT (PRState s i o) m [Splitter s i o] refineWithSplitterAllInputs :: (Monad m, Ord o, Ord s) => [(i, s -> [s])] -> Splitter s i o -> StateT (PRState s i o) m [Splitter s i o]
refineWithSplitterAllInputs ls splitter = concat <$> traverse (\(i, rev) -> refineWithSplitter i rev splitter) ls refineWithSplitterAllInputs ls splitter = concat <$> traverse (\(i, rev) -> refineWithSplitter i rev splitter) ls
refine :: (Monad m, Ord o, Ord s) => ([i] -> m ()) -> [(i, (s -> o))] -> [(i, (s -> [s]))] -> StateT (PRState s i o) m () refine :: (Monad m, Ord o, Ord s) => ([i] -> m ()) -> [(i, s -> o)] -> [(i, s -> [s])] -> StateT (PRState s i o) m ()
refine ping outputs transitionsReverse = do refine ping outputs transitionsReverse = do
initialQueue <- refineWithAllOutputs outputs initialQueue <- refineWithAllOutputs outputs

View file

@ -1,14 +1,14 @@
module StateIdentifiers where module StateIdentifiers where
import SplittingTree import SplittingTree
import Trie qualified as Trie import Trie qualified
import Data.Map.Strict qualified as Map import Data.Map.Strict qualified as Map
stateIdentifierFor :: (Ord i, Ord s) => s -> Partition s -> SplittingTree s i o -> Trie.Trie i stateIdentifierFor :: (Ord i, Ord s) => s -> Partition s -> SplittingTree s i o -> Trie.Trie i
stateIdentifierFor state Partition{..} SplittingTree{..} = go firstNode where stateIdentifierFor state Partition{..} SplittingTree{..} = go firstNode
where
firstNode = fst <$> blockParent Map.!? (getPartition Map.! state) firstNode = fst <$> blockParent Map.!? (getPartition Map.! state)
getParent n = fst <$> innerParent Map.!? n getParent n = fst <$> innerParent Map.!? n
go Nothing = Trie.empty go Nothing = Trie.empty
go (Just n) = Trie.insert (label Map.! n) (go (getParent n)) go (Just n) = Trie.insert (label Map.! n) (go (getParent n))