1
Fork 0
mirror of https://github.com/Jaxan/ons-hs.git synced 2025-04-27 06:37:44 +02:00

Minimisation

This commit is contained in:
Joshua Moerman 2019-01-18 14:01:19 +01:00
parent 2b6a011551
commit 1d72d3cddb
6 changed files with 208 additions and 25 deletions

140
app/Minimise.hs Normal file
View file

@ -0,0 +1,140 @@
{-# language DeriveGeneric #-}
{-# language DerivingVia #-}
{-# language FlexibleContexts #-}
{-# language PartialTypeSignatures #-}
{-# language RecordWildCards #-}
{-# language UndecidableInstances #-}
{-# OPTIONS_GHC -Wno-partial-type-signatures #-}
import Nominal hiding (product)
import Support (Rat(..), Support)
import OnsAutomata
import OnsQuotient
import OrbitList
import EquivariantMap ((!))
import qualified EquivariantMap as Map
import qualified EquivariantSet as Set
import Data.Foldable (fold)
import qualified GHC.Generics as GHC
import Prelude as P hiding (map, product, words, filter)
-- Version A: works on equivalence relations
minimiseA :: _ => Automaton q a -> OrbitList a -> Automaton _ a
minimiseA Automaton{..} alph = Automaton
{ states = states2
, initialState = phi ! initialState
, acceptance = Map.fromList . fmap (\(s, b) -> (phi ! s, b)) . Map.toList $ acceptance
, transition = Map.fromList . fmap (\((s, a), t) -> ((phi ! s, a), phi ! t)) . Map.toList $ transition
}
where
-- Are all successors of s0 t0 related?
nextAreEquiv equiv s0 t0 = OrbitList.null
. filter (\(s2, t2) -> s2 /= t2 && not ((s2, t2) `Set.member` equiv))
$ productWith (\(s, t) a -> (transition ! (s, a), transition ! (t, a))) (singleOrbit (s0, t0)) alph
-- Recursively shrink equiv to fixpoint
go equiv = let (equiv2, nonEq) = Set.partition (\(s, t) -> s == t || nextAreEquiv equiv s t) equiv
in if Set.null nonEq -- fixpoint!
then equiv
else go equiv2
-- Start with FxF + NxN
(y, n) = partition (acceptance !) states
equiv0 = Set.fromOrbitList $ y `product` y <> n `product` n
-- compute fixpoint
equivInf = go equiv0
-- get quotient
(phi, states2) = quotient equivInf states
-- Version B: works on quotient maps
minimiseB :: _ => Automaton q a -> OrbitList a -> Automaton _ a
minimiseB Automaton{..} alph = Automaton
{ states = stInf
, initialState = phiInf ! initialState
, acceptance = Map.fromList . fmap (\(s, b) -> (phiInf ! s, b)) . Map.toList $ acceptance
, transition = Map.fromList . fmap (\((s, a), t) -> ((phiInf ! s, a), phiInf ! t)) . Map.toList $ transition
}
where
-- Are all successors of s0 t0 related?
nextAreEquiv phi s0 t0 = OrbitList.null
. filter (\(s2, t2) -> s2 /= t2 && phi ! s2 /= phi ! t2)
$ productWith (\(s, t) a -> (transition ! (s, a), transition ! (t, a))) (singleOrbit (s0, t0)) alph
-- Are s0 t0 equivalent with current information?
equiv phi s0 t0 = s0 == t0 || (phi ! s0 == phi ! t0 && nextAreEquiv phi s0 t0)
-- Given a quotientmap, refine it
go phi st = let (phi2, st2) = quotientf (equiv phi) states
in if size st == size st2
then (phi, st)
else go phi2 st2
-- Start with acceptance as quotient map
(phi0, st0) = quotientf (\a b -> a == b || acceptance ! a == acceptance ! b) states
-- Compute fixpoint
(phiInf, stInf) = go phi0 st0
main :: IO ()
main = do
-- putStrLn . toStr . toList . map (\x -> (x, True)) $ words 2
-- putStrLn . toStr $ (fifoAut 3)
putStrLn . toStr $ (minimiseB (fifoAut 2) fifoAlph)
-- All example automata follow below
-- words of length <= m
words m = fold $ go (m+1) (singleOrbit []) where
go 0 acc = []
go k acc = acc : go (k-1) (productWith (:) rationals acc)
fromKeys f = Map.fromSet f . Set.fromOrbitList
data DoubleWord = Store [Rat] | Check [Rat] | Accept | Reject
deriving (Eq, Ord, GHC.Generic)
deriving Nominal via Generic DoubleWord
doubleWordAut 0 = Automaton {..} where
states = fromList [Accept, Reject]
initialState = Accept
acceptance = fromKeys (Accept ==) states
transition = fromKeys (const Reject) $ product states rationals
doubleWordAut n = Automaton {..} where
states = fromList [Accept, Reject] <> map Store (words (n-1)) <> map Check (productWith (:) rationals (words (n-1)))
initialState = Store []
acceptance = fromKeys (Accept ==) states
trans Accept _ = Reject
trans Reject _ = Reject
trans (Store l) a
| length l < n = Store (a:l)
| otherwise = Check (reverse (a:l))
trans (Check (a:as)) b
| a == b = if (P.null as) then Accept else Check as
| otherwise = Reject
transition = fromKeys (uncurry trans) $ product states rationals
data FifoS = FifoS [Rat] [Rat]
deriving (Eq, Ord, GHC.Generic)
deriving Nominal via Generic FifoS
instance ToStr FifoS where
toStr (FifoS l1 l2) = "F " ++ toStr l1 ++ " - " ++ toStr l2
fifoAlph = map Put rationals <> map Get rationals
fifoAut n = Automaton {..} where
states0 = filter (\(FifoS l1 l2) -> length l1 + length l2 <= n) $ productWith (\l1 l2 -> FifoS l1 l2) (words n) (words n)
states = fromList [Nothing] <> map Just states0
initialState = Just (FifoS [] [])
acceptance = fromKeys (Nothing /=) states
trans Nothing _ = Nothing
trans (Just (FifoS l1 l2)) (Put a)
| length l1 + length l2 >= n = Nothing
| otherwise = Just (FifoS (a:l1) l2)
trans (Just (FifoS [] [])) (Get _) = Nothing
trans (Just (FifoS l1 [])) (Get b) = trans (Just (FifoS [] (reverse l1))) (Get b)
trans (Just (FifoS l1 (a:l2))) (Get b)
| a == b = Just (FifoS l1 l2)
| otherwise = Nothing
transition = fromKeys (uncurry trans) $ product states fifoAlph

View file

@ -2,7 +2,6 @@
{-# language DerivingVia #-}
{-# language FlexibleContexts #-}
{-# language RecordWildCards #-}
{-# language StandaloneDeriving #-}
{-# language UndecidableInstances #-}
module OnsAutomata where
@ -19,7 +18,7 @@ import Prelude hiding (print, Word)
import qualified GHC.Generics as GHC
type Word a = [a]
type Word a = [a]
-- states, initial state, acceptance, transition
data Automaton q a = Automaton
@ -40,7 +39,7 @@ accepts aut l = go (initialState aut) l
-- alphetbet for the Fifo queue example
data Fifo = Put Rat | Get Rat
deriving (Eq, Ord, Show, GHC.Generic)
deriving via Generic Fifo instance Nominal Fifo
deriving Nominal via Generic Fifo
-- I do not want to give weird Show instances for basic types, so I create my
@ -67,7 +66,10 @@ instance ToStr Int where toStr i = show i
instance ToStr a => ToStr [a] where
toStr = concat . intersperse " " . fmap toStr
instance (ToStr a, ToStr b) => ToStr (a, b) where
toStr (a, b) = "(" ++ toStr a ++ ", " ++ toStr b ++ ")"
toStr (a, b) = "(" ++ toStr a ++ ", " ++ toStr b ++ ")"
instance ToStr a => ToStr (Maybe a) where
toStr Nothing = "Nothing"
toStr (Just a) = "Just " ++ toStr a
instance (Nominal q, Nominal a, ToStr q, ToStr a) => ToStr (Automaton q a) where
toStr Automaton{..} =

View file

@ -9,20 +9,26 @@ import qualified EquivariantMap as Map
import EquivariantSet (EquivariantSet(..))
import qualified EquivariantSet as Set
import Prelude (Int, Ord, (.), (<>), (+), ($), snd, fmap)
import Prelude (Bool, Int, Ord, (.), (<>), (+), ($), snd, fmap, uncurry)
type QuotientType = (Int, Support)
type QuotientMap a = EquivariantMap a QuotientType
-- Non trivial, should be made more efficient
-- Computes a quotient map given an equivalence relation
quotient :: (Nominal a, Ord (Orbit a)) => EquivariantSet (a, a) -> OrbitList a -> (QuotientMap a, OrbitList QuotientType)
quotient equiv ls = go 0 Map.empty empty (toList ls)
quotient equiv = quotientf (\a b -> (a, b) `Set.member` equiv)
-- f should be equivariant and an equivalence relation
quotientf :: (Nominal a, Ord (Orbit a)) => (a -> a -> Bool) -> OrbitList a -> (QuotientMap a, OrbitList QuotientType)
quotientf f ls = go 0 Map.empty empty (toList ls)
where
go _ phi acc [] = (phi, acc)
go n phi acc (a:as) =
let (y0, r0) = partition (\p -> p `Set.member` equiv) (product (singleOrbit a) (fromList as))
y1 = filter (\p -> p `Set.member` equiv) (product (singleOrbit a) (singleOrbit a))
let y0 = filter (uncurry f) (product (singleOrbit a) (fromList as))
y1 = filter (uncurry f) (product (singleOrbit a) (singleOrbit a))
y2 = map (\(a1, a2) -> (a2, (n, support a1 `intersect` support a2))) (y1 <> y0)
m0 = Map.fromListWith (\(n1, s1) (n2, s2) -> (n1, s1 `intersect` s2)) . toList $ y2
l0 = take 1 . fromList . fmap snd $ Map.toList m0
in go (n+1) (phi <> m0) (acc <> l0) (Set.toList . Set.fromOrbitList . map snd $ r0)
in if a `Set.member` (Map.keysSet phi)
then go n phi acc as
else go (n+1) (phi <> m0) (acc <> l0) as

View file

@ -34,7 +34,6 @@ library
executable ons-hs-solver
hs-source-dirs: app
main-is: Main.hs
ghc-options: -threaded -rtsopts -with-rtsopts=-N
build-depends: base
, ons-hs
ghc-options: -threaded -rtsopts -with-rtsopts=-N -O2
@ -43,7 +42,17 @@ executable ons-hs-solver
executable ons-hs-lstar
hs-source-dirs: app
main-is: LStar.hs
ghc-options: -threaded -rtsopts -with-rtsopts=-N
build-depends: base
, mtl
, ons-hs
other-modules: OnsAutomata
, OnsQuotient
ghc-options: -threaded -rtsopts -with-rtsopts=-N -O2
default-language: Haskell2010
executable ons-hs-minimise
hs-source-dirs: app
main-is: Minimise.hs
build-depends: base
, mtl
, ons-hs

View file

@ -21,7 +21,7 @@ import Support
-- TODO: adjust / alter / update
-- TODO: -WithKey functions
-- TODO: don't export the helper functions
-- TODO: cleanup (usage of getElelemtE is not necessary)
-- TODO: cleanup (usage of getElelemtE is not always necessary?)
-- TODO: replace [Bool] by Vec Bool if better?
@ -105,6 +105,7 @@ mapWithKey f (EqMap m) = EqMap (Map.mapWithKey f2 m)
keysSet :: EquivariantMap k v -> EquivariantSet k
keysSet (EqMap m) = EqSet (Map.keysSet m)
-- f equivariant
fromSet :: (Nominal k, Nominal v) => (k -> v) -> EquivariantSet k -> EquivariantMap k v
fromSet f (EqSet s) = EqMap (Map.fromSet f2 s)
where f2 ko = let k = getElementE ko in mapel k (f k)

View file

@ -1,5 +1,6 @@
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE UndecidableInstances #-}
@ -21,7 +22,11 @@ newtype OrbitList a = OrbitList { unOrbitList :: [Orbit a] }
deriving instance Eq (Orbit a) => Eq (OrbitList a)
deriving instance Ord (Orbit a) => Ord (OrbitList a)
deriving instance Show (Orbit a) => Show (OrbitList a)
deriving via (Trivial (OrbitList a)) instance Nominal (OrbitList a)
deriving via (Trivial (OrbitList a)) instance Nominal (OrbitList a)
-- Simply concatenation of the list
deriving instance Semigroup (OrbitList a)
deriving instance Monoid (OrbitList a)
-- Query
@ -32,6 +37,10 @@ null (OrbitList x) = L.null x
elem :: (Nominal a, Eq (Orbit a)) => a -> OrbitList a -> Bool
elem x = L.elem (toOrbit x) . unOrbitList
-- Sizes of supports of all orbits (sorted, big to small)
size :: forall a. Nominal a => OrbitList a -> [Int]
size = LO.sortOn negate . fmap (index (Proxy :: Proxy a)) . unOrbitList
-- Construction
@ -44,6 +53,10 @@ singleOrbit a = OrbitList [toOrbit a]
rationals :: OrbitList Rat
rationals = singleOrbit (Rat 0)
repeatRationals :: Int -> OrbitList [Rat]
repeatRationals 0 = singleOrbit []
repeatRationals n = productWith (:) rationals (repeatRationals (n-1))
-- Map / Filter / ...
@ -61,6 +74,10 @@ partition f (OrbitList s) = both OrbitList . L.partition (f . getElementE) $ s
take :: Int -> OrbitList a -> OrbitList a
take n = OrbitList . L.take n . unOrbitList
-- TODO: drop, span, takeWhile, ...
-- TODO: folds
-- Combinations
product :: forall a b. (Nominal a, Nominal b) => OrbitList a -> OrbitList b -> OrbitList (a, b)
@ -69,8 +86,25 @@ product (OrbitList as) (OrbitList bs) = OrbitList . concat $ (Nominal.product (P
productWith :: (Nominal a, Nominal b, Nominal c) => (a -> b -> c) -> OrbitList a -> OrbitList b -> OrbitList c
productWith f as bs = map (uncurry f) (OrbitList.product as bs)
instance Semigroup (OrbitList a) where (OrbitList as) <> (OrbitList bs) = OrbitList (as <> bs)
instance Monoid (OrbitList a) where mempty = empty
-- TODO: productWith is the same as liftA2, so we could provide an
-- Applicative instance (with singleOrbit as pure). Although, I'm not
-- sure we can do <*>, hmm.. The Alternative instance is given by
-- concatenation (i.e. the monoid structure).
-- NOTE: only works for equivariant f! In theory, the Monad instance
-- should work over all finitely supported functions, but that's harder
-- to implement.
bind :: (Nominal a, Nominal b) => OrbitList a -> (a -> OrbitList b) -> OrbitList b
bind (OrbitList as) f = OrbitList (L.concatMap (unOrbitList . f . getElementE) as)
-- Conversions
toList :: Nominal a => OrbitList a -> [a]
toList = fmap getElementE . unOrbitList
fromList :: Nominal a => [a] -> OrbitList a
fromList = OrbitList . fmap toOrbit
-- Sorted Lists
@ -98,12 +132,3 @@ projectWith f = unionAll . fmap OrbitList . groupOnFst . splitOrbs . unOrbitList
where
splitOrbs = fmap (\o -> (omap fst o, omap snd o))
groupOnFst = fmap (fmap snd) . L.groupBy (\x y -> fst x == fst y)
-- Conversions
toList :: Nominal a => OrbitList a -> [a]
toList = fmap getElementE . unOrbitList
fromList :: Nominal a => [a] -> OrbitList a
fromList = OrbitList . fmap toOrbit