1
Fork 0
mirror of https://github.com/Jaxan/ons-hs.git synced 2025-04-27 14:47:45 +02:00

More on handling counterexamples

This commit is contained in:
Joshua Moerman 2019-01-11 16:40:38 +01:00
parent 6b89d2dc5f
commit d5a1cea46b
2 changed files with 61 additions and 48 deletions

View file

@ -1,7 +1,6 @@
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE PartialTypeSignatures #-} {-# LANGUAGE PartialTypeSignatures #-}
{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TupleSections #-}
{-# OPTIONS_GHC -Wno-partial-type-signatures #-} {-# OPTIONS_GHC -Wno-partial-type-signatures #-}
module Main where module Main where
@ -10,7 +9,6 @@ import OnsAutomata
import OnsQuotient import OnsQuotient
import OrbitList import OrbitList
import qualified OrbitList as List
import EquivariantMap (EquivariantMap(..), lookup, (!)) import EquivariantMap (EquivariantMap(..), lookup, (!))
import qualified EquivariantMap as Map import qualified EquivariantMap as Map
import qualified EquivariantSet as Set import qualified EquivariantSet as Set
@ -19,12 +17,12 @@ import Data.List (tails)
import Control.Monad.State import Control.Monad.State
import Prelude hiding (filter, null, elem, lookup, product, Word, map, take) import Prelude hiding (filter, null, elem, lookup, product, Word, map, take)
-- We use Lists, as they provide a bit more laziness
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
-- Utility functions -- Utility functions
exists f = not . null . filter f exists f = not . null . filter f
forAll f = null . filter (not . f) forAll f = null . filter (not . f)
@ -49,13 +47,8 @@ inconsistencies prefs suffs table alph =
candidates = filter (\(s, t) -> s < t && equalRows s t suffs table) (product prefs prefs) candidates = filter (\(s, t) -> s < t && equalRows s t suffs table) (product prefs prefs)
candidatesExt = product candidates (product alph suffs) candidatesExt = product candidates (product alph suffs)
-- First lookup, then membership query
ask mq table (p, s) =
let w = p ++ s in case lookup w table of
Just b -> return (w, b)
Nothing -> (w,) <$> mq w
-- Main state of the L* algorithm
-- invariants: * prefs and prefsExt disjoint, without dups -- invariants: * prefs and prefsExt disjoint, without dups
-- * prefsExt ordered -- * prefsExt ordered
-- * prefs and (prefs `union` prefsExt) prefix-closed -- * prefs and (prefs `union` prefsExt) prefix-closed
@ -71,19 +64,27 @@ data Observations a = Observations
-- input alphabet, inner monad, return value -- input alphabet, inner monad, return value
type LStar i m a = StateT (Observations i) m a type LStar i m a = StateT (Observations i) m a
-- First lookup, then membership query, also update the table
ask mq (p, s) = do
Observations{..} <- get
let w = p ++ s
case lookup w table of
Just b -> return (w, b)
Nothing -> do
b <- lift (mq w)
modify $ \o -> o { table = Map.insert w b table }
return (w, b)
-- precondition: newPrefs is subset of prefExts -- precondition: newPrefs is subset of prefExts
addRows :: _ => 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
rect = product newPrefsExt suffs rect = product newPrefsExt suffs
ans <- lift $ mapM (ask mq table) (List.toList rect) _ <- mapM (ask mq) (OrbitList.toList rect)
put $ Observations modify $ \o -> o { prefs = prefs <> newPrefs
{ prefs = prefs <> newPrefs , prefsExt = (prefsExt `minus` newPrefs) `union` newPrefsExt
, prefsExt = (prefsExt `minus` newPrefs) `union` newPrefsExt }
, table = table <> Map.fromList ans
, ..
}
return () return ()
-- precondition: newSuffs disjoint from suffs -- precondition: newSuffs disjoint from suffs
@ -91,40 +92,37 @@ 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
ans <- lift $ mapM (ask mq table) (List.toList rect) _ <- mapM (ask mq) (OrbitList.toList rect)
put $ Observations modify $ \o -> o { suffs = suffs <> newSuffs }
{ suffs = suffs <> newSuffs
, table = table <> Map.fromList ans
, ..
}
return () return ()
fillTable :: _ => (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
ans <- lift $ mapM (ask mq table) (List.toList rect) _ <- mapM (ask mq) (OrbitList.toList rect)
put $ Observations
{ table = Map.fromList ans
, ..
}
return () return ()
learn :: _ => (Word a -> IO Bool) -> LStar a IO (Automaton _ _) -- This could be cleaned up
learn mq = do learn :: _ => (Word a -> m Bool) -> (Automaton _ a -> m (Maybe (Word a))) -> LStar a m (Automaton _ a)
learn mq eq = 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
case null ncl of case null ncl of
False -> do False -> do
-- If not closed, then add 1 orbit of rows. Then start from top
addRows (take 1 ncl) mq addRows (take 1 ncl) mq
learn mq learn mq eq
True -> do True -> do
-- Closed! Now we check consistency
case null inc of case null inc of
False -> do False -> do
-- If not consistent, then add 1 orbit of columns. Then start from top
addCols (take 1 (map (uncurry (:) . snd) inc)) mq addCols (take 1 (map (uncurry (:) . snd) inc)) mq
learn mq learn mq eq
True -> do True -> do
-- Also consistent! Let's build a minimal automaton!
let equiv = Set.fromOrbitList . filter (\(s, t) -> equalRows s t suffs table) $ product prefs prefs let equiv = Set.fromOrbitList . filter (\(s, t) -> equalRows s t suffs table) $ product prefs prefs
(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
@ -135,19 +133,28 @@ learn mq = do
, acceptance = Map.fromList . toList . map (\p -> (f ! p, table ! p)) $ prefs , acceptance = Map.fromList . toList . map (\p -> (f ! p, table ! p)) $ prefs
, transition = Map.fromList . toList . map (\(p, a) -> ((f ! p, a), trans2 (ext p a))) $ product prefs alph , transition = Map.fromList . toList . map (\(p, a) -> ((f ! p, a), trans2 (ext p a))) $ product prefs alph
} }
eq <- lift (askEquiv hypothesis) askCe = do
case eq of ce <- lift (eq hypothesis)
Nothing -> return hypothesis case ce of
Just w -> do Nothing -> return hypothesis
lift (print w) Just w -> do
let allSuffs = Set.fromList $ tails w let b1 = accepts hypothesis w
newSuffs = allSuffs `Set.difference` Set.fromOrbitList suffs (_, b2) <- ask mq (w, [])
addCols (Set.toOrbitList newSuffs) mq -- Ignore false counterexamples
learn mq case b1 == b2 of
True -> askCe
False -> do
-- Add all suffixes of a counterexample
let allSuffs = Set.fromList $ tails w
newSuffs = allSuffs `Set.difference` Set.fromOrbitList suffs
addCols (Set.toOrbitList newSuffs) mq
learn mq eq
askCe
accept :: _ => Word a -> IO Bool -- Here is the teacher: just pose the queries in the terminal
accept w = do askMember :: _ => Word a -> IO Bool
askMember w = do
putStr "MQ \"" putStr "MQ \""
putStr (toStr w) putStr (toStr w)
putStrLn "\"" putStrLn "\""
@ -155,7 +162,7 @@ accept w = do
case a of case a of
"Y" -> return True "Y" -> return True
"N" -> return False "N" -> return False
_ -> accept w _ -> askMember w
askEquiv :: _ => Automaton q a -> IO (Maybe (Word a)) askEquiv :: _ => Automaton q a -> IO (Maybe (Word a))
askEquiv aut = do askEquiv aut = do
@ -176,7 +183,5 @@ main = do
suffs = singleOrbit [] suffs = singleOrbit []
table = Map.empty table = Map.empty
init = Observations{..} init = Observations{..}
aut <- evalStateT (fillTable accept >> learn accept) init aut <- evalStateT (fillTable askMember >> learn askMember askEquiv) init
putStrLn "Done learning :D"
return () return ()

View file

@ -1,3 +1,4 @@
{-# language FlexibleContexts #-}
{-# language RecordWildCards #-} {-# language RecordWildCards #-}
module OnsAutomata where module OnsAutomata where
@ -8,9 +9,9 @@ import Data.List (intersperse)
import Nominal import Nominal
import Support (Rat(..), Support(..)) import Support (Rat(..), Support(..))
import OrbitList as L (OrbitList, toList) import OrbitList as L (OrbitList, toList)
import EquivariantMap as M (EquivariantMap, toList) import EquivariantMap as M (EquivariantMap, toList, (!))
import Prelude hiding (print) import Prelude hiding (print, Word)
type Word a = [a] type Word a = [a]
@ -23,6 +24,13 @@ data Automaton q a = Automaton
, transition :: EquivariantMap (q, a) q , transition :: EquivariantMap (q, a) q
} }
accepts :: (Nominal q, Ord (Orbit q), Nominal a, Ord (Orbit a))
=> Automaton q a -> Word a -> Bool
accepts aut l = go (initialState aut) l
where
go s [] = acceptance aut ! s
go s (a:w) = go (transition aut ! (s, a)) w
-- I do not want to give weird Show instances for basic types, so I create my -- I do not want to give weird Show instances for basic types, so I create my