1
Fork 0
mirror of https://github.com/Jaxan/ons-hs.git synced 2025-04-27 06:37:44 +02:00
ons-hs/app/Teacher.hs
2024-11-11 16:47:14 +01:00

115 lines
3.4 KiB
Haskell

{-# LANGUAGE ImportQualifiedPost #-}
import System.IO (hFlush, hPutStrLn, stderr, stdout)
import Data.IORef
import Data.Maybe (isJust)
import System.Exit (exitFailure)
import System.IO
import ExampleAutomata
import IO
import Nominal (Atom)
import OrbitList qualified
data Example
= Fifo Int
| DoubleWord Int
main :: IO ()
main =
let ex = DoubleWord 2
in case ex of
Fifo n -> teach "FIFO" (fifoFun n) (fifoCex n)
DoubleWord n -> teach "ATOMS" (doubleFun n) (doubleCex n)
teach :: (ToStr a, FromStr a, Show a) => String -> ([a] -> Bool) -> [[a]] -> IO ()
teach alphStr fun cexes = do
-- Set up some counters
countMQ <- newIORef (0 :: Int)
countEQ <- newIORef (0 :: Int)
cexChecks <- newIORef cexes
let
-- Helper functions for answering/logging
log str = hPutStrLn stderr str
answer str = hPutStrLn stdout str >> hFlush stdout
-- Parsing the commands from the learner
act message =
case message of
"ALPHABET" -> handleAlphabet
('M' : 'Q' : _) -> handleMQ message
('E' : 'Q' : _) -> handleEQ message
_ -> do
hPutStrLn stderr "Invalid command"
exitFailure
handleAlphabet = answer alphStr
handleMQ str = do
let (MQ word, _) = fromStr str
acc = fun word
answer $ if acc then "Y" else "N"
modifyIORef countMQ succ
n <- readIORef countMQ
log $ "MQ " <> show n <> ": " <> str <> " -> " <> show acc -- <> " (parsed as " <> show word <> ")"
handleEQ str = do
modifyIORef countEQ succ
n <- readIORef countEQ
log $ "EQ " <> show n <> ": " <> str
-- Currently, we handle equivalence queries very lazily: we simply pose
-- a possible counterexample to the learner (which it might actually do
-- correctly). There is no guarantee is will work.
possibleCexes <- readIORef cexChecks
case possibleCexes of
[] -> do
log " -> Y"
answer "Y"
(c : cs) -> do
log $ " -> N " <> toStr c
-- TODO: make this syntax the same as for MQs
answer $ "N " <> toStr c
writeIORef cexChecks cs
-- Lazily answer all queries, until the stream is closed
messages <- lines <$> getContents
mapM_ act messages
-- Then output some statistics
m <- readIORef countMQ
e <- readIORef countEQ
hPutStrLn stderr $ "Total number of MQ: " <> show m
hPutStrLn stderr $ "Total number of EQ: " <> show e
-- examples from https://gitlab.science.ru.nl/moerman/nominal-learning-ons/-/blob/master/examples.hpp
-- Note: we do not implement them as state machines. Instead we implement them
-- as functions, to really make the point that this is "black box learning".
fifoFun :: Int -> [FifoA] -> Bool
fifoFun n = acc . foldl' step (Just [])
where
step Nothing _ = Nothing
step (Just ls) (Put a)
| length ls >= n = Nothing
| otherwise = Just (ls <> [a])
step (Just []) (Get a) = Nothing
step (Just (x : xs)) (Get a)
| a == x = Just xs
| otherwise = Nothing
acc = isJust
fifoCex :: Int -> [[FifoA]]
fifoCex n = concatMap dw [1 .. n]
where
dw k = [fmap Put w <> fmap Get w | w <- OrbitList.toList (OrbitList.repeatRationals k)]
doubleFun :: Int -> [Atom] -> Bool
doubleFun 0 w = w == []
doubleFun n w = let (l, r) = splitAt n w in w /= [] && l == r
doubleCex :: Int -> [[Atom]]
doubleCex n = [w <> w | w <- OrbitList.toList (OrbitList.repeatRationals n)]