1
Fork 0
mirror of https://github.com/Jaxan/nominal-lstar.git synced 2025-04-27 06:37:45 +02:00

Two things: Optimisations (now only adds one row to fix closedness, and one column to fix inconsistencies) and IO (now also speaks the protocol used in the new paper)

This commit is contained in:
Joshua Moerman 2017-10-21 10:40:59 +02:00
parent 7b062904d8
commit 7da6db27b3
5 changed files with 107 additions and 12 deletions

View file

@ -32,3 +32,32 @@ executable NominalAngluin
NLambda >= 1.1
hs-source-dirs: src
default-language: Haskell2010
executable NominalAngluin2
ghc-options:
-O2
main-is: Main2.hs
other-modules:
AbstractLStar,
Angluin,
Bollig,
Examples,
Examples.Contrived,
Examples.ContrivedNFAs,
Examples.Fifo,
Examples.RunningExample,
Examples.Stack,
NLStar,
ObservationTable,
Teacher,
Teachers.Teacher,
Teachers.Terminal,
Teachers.Whitebox
build-depends:
base >= 4.8 && < 5,
containers,
haskeline,
mtl,
NLambda >= 1.1
hs-source-dirs: src
default-language: Haskell2010

View file

@ -54,13 +54,17 @@ learn makeComplete handleCounterExample constructHypothesis teacher s =
let h = constructHypothesis s2 in
traceShow h $
trace "3. Equivalent? " $
let eq = equivalent teacher h in
traceShow eq $
case eq of
Nothing -> h
Just ce -> do
let s3 = handleCounterExample teacher s2 ce
learn makeComplete handleCounterExample constructHypothesis teacher s3
eqloop s2 h
where
eqloop s2 h = case equivalent teacher h of
Nothing -> trace "Yes" $ h
Just ces -> trace "No" $
case isTrue . isEmpty $ realces h ces of
True -> eqloop s2 h
False ->
let s3 = handleCounterExample teacher s2 ces in
learn makeComplete handleCounterExample constructHypothesis teacher s3
realces h ces = NLambda.filter (\(ce, a) -> a `neq` accepts h ce) $ membership teacher ces
-- Initial state is always the same in our case
constructEmptyState :: LearnableAlphabet i => Teacher i -> State i

View file

@ -11,22 +11,25 @@ import NLambda
import qualified Prelude hiding ()
import Prelude (Bool(..), Maybe(..), id, ($), (.), (++), fst, show)
justOne :: (Contextual a, NominalType a) => Set a -> Set a
justOne s = mapFilter id . orbit [] . element $ s
-- We can determine its completeness with the following
-- It returns all witnesses (of the form sa) for incompleteness
closednessTest :: NominalType i => State i -> TestResult i
closednessTest :: LearnableAlphabet i => State i -> TestResult i
closednessTest State{..} = case solve (isEmpty defect) of
Just True -> Succes
Just False -> trace "Not closed" $ Failed defect empty
Just False -> trace "Not closed" $ Failed (justOne defect) empty
where
sss = map (row t) ss -- all the rows
hasEqRow = contains sss . row t -- has equivalent upper row
defect = filter (not . hasEqRow) ssa -- all rows without equivalent guy
-- We look for inconsistencies and return columns witnessing it
consistencyTestDirect :: NominalType i => State i -> TestResult i
consistencyTestDirect :: LearnableAlphabet i => State i -> TestResult i
consistencyTestDirect State{..} = case solve (isEmpty defect) of
Just True -> Succes
Just False -> trace "Not consistent" $ Failed empty defect
Just False -> trace "Not consistent" $ Failed empty (justOne defect)
where
ssRows = map (\u -> (u, row t u)) ss
candidates = pairsWithFilter (\(u1,r1) (u2,r2) -> maybeIf (u1 `neq` u2 /\ r1 `eq` r2) (u1, u2)) ssRows ssRows

View file

@ -6,6 +6,7 @@ module Teacher
, teacherWithTarget
, teacherWithTargetNonDet
, teacherWithIO
, teacherWithIO2
, teacherWithTargetAndIO
) where
@ -53,6 +54,14 @@ teacherWithIO alph = Teacher
, alphabet = alph
}
-- 2b. Same as above. But with machine readable queries (except for EQs maybe)
teacherWithIO2 :: (Show i, Read i, NominalType i, Contextual i) => Set i -> Teacher i
teacherWithIO2 alph = Teacher
{ membership = ioMembership2
, equivalent = ioEquivalent2
, alphabet = alph
}
-- 3. A teacher uses a target for the mebership queries, but you for equivalence
-- Useful as long as you don't have an equivalence check
-- used for NFAs when there was no bounded bisimulation yet

View file

@ -4,6 +4,7 @@ import NLambda
import Control.Monad
import Data.IORef
import Data.List (intersperse, concat)
import Prelude hiding (filter, map, and, sum)
import System.Console.Haskeline
import System.IO.Unsafe (unsafePerformIO)
@ -41,6 +42,34 @@ ioMembership queries = unsafePerformIO $ do
mqCache = unsafePerformIO $ newIORef empty
-- Same as above, but with a machine-readable format
ioMembership2 :: (Show i, NominalType i, Contextual i) => Set [i] -> Set ([i], Formula)
ioMembership2 queries = unsafePerformIO $ do
cache <- readIORef mqCache
let cachedAnswers = filter (\(a, f) -> a `member` queries) cache
let newQueries = simplify $ queries \\ map fst cache
let representedInputs = toList . mapFilter id . setOrbitsRepresentatives $ newQueries
answers <- forM representedInputs $ \input -> do
let str = Data.List.concat . intersperse " " . fmap show $ input
putStrLn $ "MQ \"" ++ str ++ "\""
let askit = do
x <- getInputLine ""
case x of
Just "Y" -> return True
Just "N" -> return False
_ -> error "Unable to parse, or quit. Bye!"
answer <- runInputT defaultSettings askit
return $ orbit [] (input, fromBool answer)
let answersAsSet = simplify . sum . fromList $ answers
writeIORef mqCache (simplify $ cache `union` answersAsSet)
return (simplify $ cachedAnswers `union` answersAsSet)
where
-- We use a cache, so that questions will not be repeated.
-- It is a bit hacky, as the Teacher interface does not allow state...
{-# NOINLINE mqCache #-}
mqCache = unsafePerformIO $ newIORef empty
-- Poses a query to the terminal, waiting for the user to provide a counter example
-- TODO: extend to any alphabet type (hard because of parsing)
ioEquivalent :: (Show q, NominalType q, Show i, Read i, NominalType i) => Automaton q i -> Maybe (Set [i])
@ -56,8 +85,29 @@ ioEquivalent hypothesis = unsafePerformIO $ do
outputStrLn $ "Ok, we're done"
return Nothing
Just Nothing -> do
outputStrLn $ "Unable to parse, try again"
outputStrLn $ "Unable to parse (88), try again"
loop
Just (Just f) -> return (Just f)
answer <- runInputT defaultSettings loop
return (orbit [] <$> answer)
-- Same as above but in different format.
ioEquivalent2 :: (Show q, NominalType q, Show i, Read i, NominalType i) => Automaton q i -> Maybe (Set [i])
ioEquivalent2 hypothesis = unsafePerformIO $ do
putStrLn "EQ\n\"Is the following automaton correct?"
print hypothesis
putStrLn "\""
let loop = do
x <- getInputLine ""
case x of
Just "Y" -> return Nothing
Just ('N' : ' ' : ce) -> return (Just (readCE ce))
_ -> error "Unable to parse (104), or quit. Bye!"
answer <- runInputT defaultSettings loop
return (orbit [] <$> answer)
where
readCE [] = []
readCE (' ' : xs) = readCE xs
readCE xs = case reads xs of
[(a, str)] -> a : readCE str
_ -> error "Unable to parse (113)"