mirror of
https://github.com/Jaxan/ons-hs.git
synced 2025-04-27 22:57:44 +02:00
More on handling counterexamples
This commit is contained in:
parent
6b89d2dc5f
commit
d5a1cea46b
2 changed files with 61 additions and 48 deletions
83
app/LStar.hs
83
app/LStar.hs
|
@ -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,18 +64,26 @@ 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 ()
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
case ce of
|
||||||
Nothing -> return hypothesis
|
Nothing -> return hypothesis
|
||||||
Just w -> do
|
Just w -> do
|
||||||
lift (print w)
|
let b1 = accepts hypothesis w
|
||||||
|
(_, b2) <- ask mq (w, [])
|
||||||
|
-- Ignore false counterexamples
|
||||||
|
case b1 == b2 of
|
||||||
|
True -> askCe
|
||||||
|
False -> do
|
||||||
|
-- Add all suffixes of a counterexample
|
||||||
let allSuffs = Set.fromList $ tails w
|
let allSuffs = Set.fromList $ tails w
|
||||||
newSuffs = allSuffs `Set.difference` Set.fromOrbitList suffs
|
newSuffs = allSuffs `Set.difference` Set.fromOrbitList suffs
|
||||||
addCols (Set.toOrbitList newSuffs) mq
|
addCols (Set.toOrbitList newSuffs) mq
|
||||||
learn 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 ()
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Add table
Reference in a new issue