mirror of
https://github.com/Jaxan/ons-hs.git
synced 2025-04-27 14:47:45 +02:00
Counterexample handling
This commit is contained in:
parent
01319327af
commit
6b89d2dc5f
4 changed files with 124 additions and 50 deletions
77
app/LStar.hs
77
app/LStar.hs
|
@ -6,38 +6,23 @@
|
||||||
|
|
||||||
module Main where
|
module Main where
|
||||||
|
|
||||||
import Nominal hiding (product)
|
import OnsAutomata
|
||||||
import Support (Rat(..), Support(..), intersect)
|
import OnsQuotient
|
||||||
|
|
||||||
import OrbitList
|
import OrbitList
|
||||||
import qualified OrbitList as List
|
import qualified OrbitList as List
|
||||||
import EquivariantMap (EquivariantMap(..), lookup, (!))
|
import EquivariantMap (EquivariantMap(..), lookup, (!))
|
||||||
import qualified EquivariantMap as Map
|
import qualified EquivariantMap as Map
|
||||||
import EquivariantSet (EquivariantSet(..))
|
|
||||||
import qualified EquivariantSet as Set
|
import qualified EquivariantSet as Set
|
||||||
|
|
||||||
|
import Data.List (tails)
|
||||||
import Control.Monad.State
|
import Control.Monad.State
|
||||||
import Prelude hiding (filter, null, elem, lookup, product, Word, map, take, partition)
|
import Prelude hiding (filter, null, elem, lookup, product, Word, map, take)
|
||||||
|
|
||||||
type Word a = [a]
|
|
||||||
type Rows a = OrbitList (Word a)
|
type Rows a = OrbitList (Word a)
|
||||||
type Columns a = OrbitList (Word a)
|
type Columns a = OrbitList (Word a)
|
||||||
type Table a = EquivariantMap (Word a) Bool
|
type Table a = EquivariantMap (Word a) Bool
|
||||||
|
|
||||||
-- states, initial state, acceptance, transition
|
|
||||||
data Automaton q a = Automaton
|
|
||||||
{ states :: OrbitList q
|
|
||||||
, initialState :: q
|
|
||||||
, acceptance :: EquivariantMap q Bool
|
|
||||||
, transition :: EquivariantMap (q, a) q
|
|
||||||
}
|
|
||||||
|
|
||||||
instance (Nominal q, Nominal a, Show q, Show a) => Show (Automaton q a) where
|
|
||||||
show Automaton{..} =
|
|
||||||
"{ states = " ++ show (toList states) ++
|
|
||||||
", initialState = " ++ show initialState ++
|
|
||||||
", acceptance = " ++ show (Map.toList acceptance) ++
|
|
||||||
", transition = " ++ show (Map.toList transition) ++
|
|
||||||
"}"
|
|
||||||
|
|
||||||
|
|
||||||
-- Utility functions
|
-- Utility functions
|
||||||
|
@ -45,19 +30,19 @@ exists f = not . null . filter f
|
||||||
forAll f = null . filter (not . f)
|
forAll f = null . filter (not . f)
|
||||||
ext p a = p <> [a]
|
ext p a = p <> [a]
|
||||||
|
|
||||||
equalRows :: (Nominal a, Ord (Orbit a)) => Word a -> Word a -> Columns a -> Table a -> Bool
|
equalRows :: _ => Word a -> Word a -> Columns a -> Table a -> Bool
|
||||||
equalRows s0 t0 suffs table =
|
equalRows s0 t0 suffs table =
|
||||||
forAll (\((s, t), e) -> lookup (s ++ e) table == lookup (t ++ e) table) $ product (singleOrbit (s0, t0)) suffs
|
forAll (\((s, t), e) -> lookup (s ++ e) table == lookup (t ++ e) table) $ product (singleOrbit (s0, t0)) suffs
|
||||||
|
|
||||||
closed :: (Nominal a, Ord (Orbit a)) => Word a -> Rows a -> Columns a -> Table a -> Bool
|
closed :: _ => Word a -> Rows a -> Columns a -> Table a -> Bool
|
||||||
closed t prefs suffs table =
|
closed t prefs suffs table =
|
||||||
exists (\(t, s) -> equalRows t s suffs table) (product (singleOrbit t) prefs)
|
exists (\(t, s) -> equalRows t s suffs table) (product (singleOrbit t) prefs)
|
||||||
|
|
||||||
nonClosedness :: (Nominal a, Ord (Orbit a)) => Rows a -> Rows a -> Columns a -> Table a -> Rows a
|
nonClosedness :: _ => Rows a -> Rows a -> Columns a -> Table a -> Rows a
|
||||||
nonClosedness prefs prefsExt suffs table =
|
nonClosedness prefs prefsExt suffs table =
|
||||||
filter (\t -> not $ closed t prefs suffs table) prefsExt
|
filter (\t -> not $ closed t prefs suffs table) prefsExt
|
||||||
|
|
||||||
inconsistencies :: (Nominal a, Ord a, Ord (Orbit a)) => Rows a -> Columns a -> Table a -> OrbitList a -> OrbitList ((Word a, Word a), (a, Word a))
|
inconsistencies :: _ => Rows a -> Columns a -> Table a -> OrbitList a -> OrbitList ((Word a, Word a), (a, Word a))
|
||||||
inconsistencies prefs suffs table alph =
|
inconsistencies prefs suffs table alph =
|
||||||
filter (\((s, t), (a, e)) -> lookup (s ++ (a:e)) table /= lookup (t ++ (a:e)) table) candidatesExt
|
filter (\((s, t), (a, e)) -> lookup (s ++ (a:e)) table /= lookup (t ++ (a:e)) table) candidatesExt
|
||||||
where
|
where
|
||||||
|
@ -70,19 +55,6 @@ ask mq table (p, s) =
|
||||||
Just b -> return (w, b)
|
Just b -> return (w, b)
|
||||||
Nothing -> (w,) <$> mq w
|
Nothing -> (w,) <$> mq w
|
||||||
|
|
||||||
-- Non trivial, should be made more efficient
|
|
||||||
quotient :: _ => EquivariantSet (a, a) -> OrbitList a -> (EquivariantMap a (Int, Support), OrbitList (Int, Support))
|
|
||||||
quotient equiv ls = go 0 Map.empty OrbitList.empty (toList ls)
|
|
||||||
where
|
|
||||||
go n 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))
|
|
||||||
y2 = map (\(a1, a2) -> (a2, (n, support a1 `intersect` support a2))) (y1 <> y0)
|
|
||||||
m0 = Map.fromListWith (\(n1, s1) (n2, s2) -> (n1, s1 `intersect` s2)) . OrbitList.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)
|
|
||||||
|
|
||||||
|
|
||||||
-- invariants: * prefs and prefsExt disjoint, without dups
|
-- invariants: * prefs and prefsExt disjoint, without dups
|
||||||
-- * prefsExt ordered
|
-- * prefsExt ordered
|
||||||
|
@ -100,7 +72,7 @@ data Observations a = Observations
|
||||||
type LStar i m a = StateT (Observations i) m a
|
type LStar i m a = StateT (Observations i) m a
|
||||||
|
|
||||||
-- precondition: newPrefs is subset of prefExts
|
-- precondition: newPrefs is subset of prefExts
|
||||||
addRows :: (Nominal a, Ord (Orbit a), Monad m) => Rows a -> (Word a -> m Bool) -> LStar a m ()
|
addRows :: _ => Rows a -> (Word a -> m Bool) -> LStar a m ()
|
||||||
addRows newPrefs mq = do
|
addRows newPrefs mq = do
|
||||||
Observations{..} <- get
|
Observations{..} <- get
|
||||||
let newPrefsExt = productWith ext newPrefs alph
|
let newPrefsExt = productWith ext newPrefs alph
|
||||||
|
@ -115,7 +87,7 @@ addRows newPrefs mq = do
|
||||||
return ()
|
return ()
|
||||||
|
|
||||||
-- precondition: newSuffs disjoint from suffs
|
-- precondition: newSuffs disjoint from suffs
|
||||||
addCols :: (Nominal a, Ord (Orbit a), Monad m) => Columns a -> (Word a -> m Bool) -> LStar a m ()
|
addCols :: _ => Columns a -> (Word a -> m Bool) -> LStar a m ()
|
||||||
addCols newSuffs mq = do
|
addCols newSuffs mq = do
|
||||||
Observations{..} <- get
|
Observations{..} <- get
|
||||||
let rect = product (prefs `union` prefsExt) newSuffs
|
let rect = product (prefs `union` prefsExt) newSuffs
|
||||||
|
@ -127,7 +99,7 @@ addCols newSuffs mq = do
|
||||||
}
|
}
|
||||||
return ()
|
return ()
|
||||||
|
|
||||||
fillTable :: (Nominal a, Ord (Orbit a), Monad m) => (Word a -> m Bool) -> LStar a m ()
|
fillTable :: _ => (Word a -> m Bool) -> LStar a m ()
|
||||||
fillTable mq = do
|
fillTable mq = do
|
||||||
Observations{..} <- get
|
Observations{..} <- get
|
||||||
let rect = product (prefs `union` prefsExt) suffs
|
let rect = product (prefs `union` prefsExt) suffs
|
||||||
|
@ -143,8 +115,6 @@ learn mq = do
|
||||||
Observations{..} <- get
|
Observations{..} <- get
|
||||||
let ncl = nonClosedness prefs prefsExt suffs table
|
let ncl = nonClosedness prefs prefsExt suffs table
|
||||||
inc = inconsistencies prefs suffs table alph
|
inc = inconsistencies prefs suffs table alph
|
||||||
lift (print (toList ncl))
|
|
||||||
lift (print (toList inc))
|
|
||||||
case null ncl of
|
case null ncl of
|
||||||
False -> do
|
False -> do
|
||||||
addRows (take 1 ncl) mq
|
addRows (take 1 ncl) mq
|
||||||
|
@ -159,8 +129,7 @@ learn mq = do
|
||||||
(f, s) = quotient equiv prefs
|
(f, s) = quotient equiv prefs
|
||||||
trans = Map.fromList . toList . map (\(s, t) -> (s, f ! t)) . filter (\(s, t) -> equalRows s t suffs table) $ product prefsExt prefs
|
trans = Map.fromList . toList . map (\(s, t) -> (s, f ! t)) . filter (\(s, t) -> equalRows s t suffs table) $ product prefsExt prefs
|
||||||
trans2 pa = if pa `elem` prefsExt then trans ! pa else f ! pa
|
trans2 pa = if pa `elem` prefsExt then trans ! pa else f ! pa
|
||||||
lift (print (Map.toList trans))
|
hypothesis = Automaton
|
||||||
let hypothesis = Automaton
|
|
||||||
{ states = s
|
{ states = s
|
||||||
, initialState = f ! []
|
, initialState = f ! []
|
||||||
, acceptance = Map.fromList . toList . map (\p -> (f ! p, table ! p)) $ prefs
|
, acceptance = Map.fromList . toList . map (\p -> (f ! p, table ! p)) $ prefs
|
||||||
|
@ -169,13 +138,18 @@ learn mq = do
|
||||||
eq <- lift (askEquiv hypothesis)
|
eq <- lift (askEquiv hypothesis)
|
||||||
case eq of
|
case eq of
|
||||||
Nothing -> return hypothesis
|
Nothing -> return hypothesis
|
||||||
Just w -> error "No counterexample handling yet"
|
Just w -> do
|
||||||
|
lift (print w)
|
||||||
|
let allSuffs = Set.fromList $ tails w
|
||||||
|
newSuffs = allSuffs `Set.difference` Set.fromOrbitList suffs
|
||||||
|
addCols (Set.toOrbitList newSuffs) mq
|
||||||
|
learn mq
|
||||||
|
|
||||||
|
|
||||||
accept :: Show a => Word a -> IO Bool
|
accept :: _ => Word a -> IO Bool
|
||||||
accept w = do
|
accept w = do
|
||||||
putStr "MQ \""
|
putStr "MQ \""
|
||||||
putStr (show w)
|
putStr (toStr w)
|
||||||
putStrLn "\""
|
putStrLn "\""
|
||||||
a <- getLine
|
a <- getLine
|
||||||
case a of
|
case a of
|
||||||
|
@ -186,10 +160,13 @@ accept w = do
|
||||||
askEquiv :: _ => Automaton q a -> IO (Maybe (Word a))
|
askEquiv :: _ => Automaton q a -> IO (Maybe (Word a))
|
||||||
askEquiv aut = do
|
askEquiv aut = do
|
||||||
putStr "EQ \""
|
putStr "EQ \""
|
||||||
putStr (show aut)
|
putStr (toStr aut)
|
||||||
putStrLn "\""
|
putStrLn "\""
|
||||||
a <- getLine
|
a <- getLine
|
||||||
return Nothing
|
case a of
|
||||||
|
"Y" -> return Nothing
|
||||||
|
'N':' ':w -> return $ Just (fst $ fromStr w)
|
||||||
|
_ -> askEquiv aut
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = do
|
main = do
|
||||||
|
@ -200,6 +177,6 @@ main = do
|
||||||
table = Map.empty
|
table = Map.empty
|
||||||
init = Observations{..}
|
init = Observations{..}
|
||||||
aut <- evalStateT (fillTable accept >> learn accept) init
|
aut <- evalStateT (fillTable accept >> learn accept) init
|
||||||
print aut
|
putStrLn "Done learning :D"
|
||||||
return ()
|
return ()
|
||||||
|
|
||||||
|
|
67
app/OnsAutomata.hs
Normal file
67
app/OnsAutomata.hs
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
{-# language RecordWildCards #-}
|
||||||
|
module OnsAutomata where
|
||||||
|
|
||||||
|
import Data.Char (isSpace)
|
||||||
|
import Data.Ratio
|
||||||
|
import Data.List (intersperse)
|
||||||
|
|
||||||
|
import Nominal
|
||||||
|
import Support (Rat(..), Support(..))
|
||||||
|
import OrbitList as L (OrbitList, toList)
|
||||||
|
import EquivariantMap as M (EquivariantMap, toList)
|
||||||
|
|
||||||
|
import Prelude hiding (print)
|
||||||
|
|
||||||
|
|
||||||
|
type Word a = [a]
|
||||||
|
|
||||||
|
-- states, initial state, acceptance, transition
|
||||||
|
data Automaton q a = Automaton
|
||||||
|
{ states :: OrbitList q
|
||||||
|
, initialState :: q
|
||||||
|
, acceptance :: EquivariantMap q Bool
|
||||||
|
, transition :: EquivariantMap (q, a) q
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- I do not want to give weird Show instances for basic types, so I create my
|
||||||
|
-- own. This is not meant to be generic, but just enough for the queries of L*.
|
||||||
|
class ToStr a where toStr :: a -> String
|
||||||
|
class FromStr a where fromStr :: String -> (a, String)
|
||||||
|
|
||||||
|
-- Should always print integers, this is not a problem for the things we build
|
||||||
|
-- from getElementE (since it returns elements with support from 1 to n).
|
||||||
|
instance ToStr Rat where
|
||||||
|
toStr (Rat r) = case denominator r of
|
||||||
|
1 -> show (numerator r)
|
||||||
|
_ -> error "Can only show integers"
|
||||||
|
|
||||||
|
instance ToStr Support where
|
||||||
|
toStr (Support s) = "{" ++ toStr s ++ "}"
|
||||||
|
|
||||||
|
instance ToStr Bool where toStr b = show b
|
||||||
|
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 ++ ")"
|
||||||
|
|
||||||
|
instance (Nominal q, Nominal a, ToStr q, ToStr a) => ToStr (Automaton q a) where
|
||||||
|
toStr Automaton{..} =
|
||||||
|
"{ states = " ++ toStr (L.toList states) ++
|
||||||
|
", initialState = " ++ toStr initialState ++
|
||||||
|
", acceptance = " ++ toStr (M.toList acceptance) ++
|
||||||
|
", transition = " ++ toStr (M.toList transition) ++ " }"
|
||||||
|
|
||||||
|
instance FromStr Rat where
|
||||||
|
fromStr str = (Rat (read l % 1), r)
|
||||||
|
where (l, r) = break isSpace str
|
||||||
|
|
||||||
|
instance FromStr a => FromStr [a] where
|
||||||
|
fromStr "" = ([], "")
|
||||||
|
fromStr str = (a : l, emptyStr)
|
||||||
|
where
|
||||||
|
(a, str2) = fromStr str
|
||||||
|
(l, emptyStr) = fromStr (dropWhile isSpace str2)
|
||||||
|
|
28
app/OnsQuotient.hs
Normal file
28
app/OnsQuotient.hs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
{-# language FlexibleContexts #-}
|
||||||
|
module OnsQuotient where
|
||||||
|
|
||||||
|
import Nominal (Nominal(..))
|
||||||
|
import Support (Support, intersect)
|
||||||
|
import OrbitList
|
||||||
|
import EquivariantMap (EquivariantMap(..))
|
||||||
|
import qualified EquivariantMap as Map
|
||||||
|
import EquivariantSet (EquivariantSet(..))
|
||||||
|
import qualified EquivariantSet as Set
|
||||||
|
|
||||||
|
import Prelude (Int, Ord, (.), (<>), (+), ($), snd, fmap)
|
||||||
|
|
||||||
|
type QuotientType = (Int, Support)
|
||||||
|
type QuotientMap a = EquivariantMap a QuotientType
|
||||||
|
|
||||||
|
-- Non trivial, should be made more efficient
|
||||||
|
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)
|
||||||
|
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))
|
||||||
|
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)
|
|
@ -47,6 +47,8 @@ executable ons-hs-lstar
|
||||||
build-depends: base
|
build-depends: base
|
||||||
, mtl
|
, mtl
|
||||||
, ons-hs
|
, ons-hs
|
||||||
|
other-modules: OnsAutomata
|
||||||
|
, OnsQuotient
|
||||||
ghc-options: -threaded -rtsopts -with-rtsopts=-N -O2
|
ghc-options: -threaded -rtsopts -with-rtsopts=-N -O2
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue