1
Fork 0
mirror of https://github.com/Jaxan/nominal-lstar.git synced 2025-04-27 22:57:45 +02:00
nominal-lstar/src/Bollig.hs
2023-05-31 15:52:17 +02:00

111 lines
5.4 KiB
Haskell

{-# language FlexibleContexts #-}
{-# language PartialTypeSignatures #-}
{-# language TypeFamilies #-}
{-# OPTIONS_GHC -Wno-partial-type-signatures #-}
module Bollig where
import AbstractLStar
import qualified BooleanObservationTable as BOT
import ObservationTableClass
import qualified SimpleObservationTable as SOT
import Teacher
import Data.List (tails)
import Debug.Trace (trace, traceShow)
import NLambda hiding (alphabet)
import Prelude (Bool (..), Int, Maybe (..), Show (..), ($), (++), (.))
-- Comparing two graphs of a function is inefficient in NLambda,
-- because we do not have a map data structure. (So the only way
-- is by taking a product and filtering on equal inputs.)
-- So instead of considering a row as E -> 2, we simply take it
-- as a subset.
-- This does hinder generalisations to other nominal join semi-
-- lattices than the Booleans.
rfsaClosednessTest :: (Nominal i, _) => Set (Row table) -> table -> TestResult i
rfsaClosednessTest primesUpp t = case solve (isEmpty defect) of
Just True -> Succes
Just False -> trace "Not closed" $ Failed defect empty
Nothing -> trace "@@@ Unsolved Formula (rfsaClosednessTest) @@@" $
Failed defect empty
where
defect = filter (\ua -> row t ua `neq` sum (filter (`isSubsetOf` row t ua) primesUpp)) (rowsExt t)
rfsaConsistencyTest :: (Nominal i, _) => table -> TestResult i
rfsaConsistencyTest t = case solve (isEmpty defect) of
Just True -> Succes
Just False -> trace "Not consistent" $ Failed empty defect
Nothing -> trace "@@@ Unsolved Formula (rfsaConsistencyTest) @@@" $
Failed empty defect
where
candidates = pairsWithFilter (\u1 u2 -> maybeIf (row t u2 `isSubsetOf` row t u1) (u1, u2)) (rows t) (rows t)
defect = triplesWithFilter (\(u1, u2) a v -> maybeIf (not (tableAt2 (u1 ++ [a]) v) /\ tableAt2 (u2 ++ [a]) v) (a:v)) candidates (alph t) (cols t)
tableAt2 s e = singleton True `eq` tableAt t s e
constructHypothesisBollig :: (Nominal i, _) => Set (Row table) -> table -> Automaton (Row table) i
constructHypothesisBollig primesUpp t = automaton q (alph t) d i f
where
q = primesUpp
i = filter (`isSubsetOf` rowEps t) q
f = filter (`contains` []) q
-- TODO: compute indices of primesUpp only once
d0 = triplesWithFilter (\s a bs2 -> maybeIf (bs2 `isSubsetOf` row t (s ++ [a])) (row t s, a, bs2)) (rows t) (alph t) q
d = filter (\(q1, _, _) -> q1 `member` q) d0
-- Adds all suffixes as columns
-- TODO: do actual Rivest and Schapire
addCounterExample :: (Nominal i, _) => MQ i Bool -> Set [i] -> table -> table
addCounterExample mq ces t =
let newColumns = sum . map (fromList . tails) $ ces
newColumnsRed = newColumns \\ cols t
in addColumns mq newColumnsRed t
learnBollig :: (Nominal i, _) => Int -> Int -> Teacher i -> Automaton _ i
learnBollig k n teacher = learnBolligLoop teacher (BOT.initialBTableSize (mqToBool teacher) (alphabet teacher) k n)
-- Slow version
learnBolligOld :: (Nominal i, _) => Int -> Int -> Teacher i -> Automaton _ i
learnBolligOld k n teacher = learnBolligLoop teacher (SOT.initialBTableSize (mqToBool teacher) (alphabet teacher) k n)
learnBolligLoop :: (Nominal i, _) => Teacher i -> table -> Automaton (Row table) i
learnBolligLoop teacher t =
let
-- These simplify's do speed up
allRowsUpp = simplify $ map (row t) (rows t)
allRows = simplify $ allRowsUpp `union` map (row t) (rowsExt t)
primesUpp = simplify $ filter (\r -> isNotEmpty r /\ r `neq` sum (filter (`isSubsetOf` r) (allRows \\ orbit [] r))) allRowsUpp
-- No worry, these are computed lazily
closednessRes = rfsaClosednessTest primesUpp t
consistencyRes = rfsaConsistencyTest t
hyp = constructHypothesisBollig primesUpp t
in
trace "1. Making it rfsa closed" $
case closednessRes of
Failed newRows _ ->
let state2 = addRows (mqToBool teacher) newRows t in
trace ("newrows = " ++ show newRows) $
learnBolligLoop teacher state2
Succes ->
trace "2. Making it rfsa consistent" $
case consistencyRes of
Failed _ newColumns ->
let state2 = addColumns (mqToBool teacher) newColumns t in
trace ("newcols = " ++ show newColumns) $
learnBolligLoop teacher state2
Succes ->
traceShow hyp $
trace "3. Equivalent? " $
eqloop t hyp
where
eqloop s2 h = case equivalent teacher h of
Nothing -> trace "Yes" h
Just ces -> trace "No" $
if isTrue . isEmpty $ realces h ces
then eqloop s2 h
else
let s3 = addCounterExample (mqToBool teacher) ces s2 in
trace ("Using ce: " ++ show ces) $
learnBolligLoop teacher s3
realces h ces = NLambda.filter (\(ce, a) -> a `neq` accepts h ce) $ membership teacher ces