mirror of
https://github.com/Jaxan/nominal-lstar.git
synced 2025-04-27 22:57:45 +02:00
111 lines
5.4 KiB
Haskell
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
|