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

Adds a class for tables

This commit is contained in:
Joshua Moerman 2021-02-22 13:28:42 +01:00
parent 9a75d5f296
commit c2b2907555
4 changed files with 142 additions and 76 deletions

View file

@ -11,9 +11,7 @@ common stuff
ghc-options: -O2 -Wall ghc-options: -O2 -Wall
build-depends: build-depends:
base >= 4.8 && < 5, base >= 4.8 && < 5,
containers,
haskeline, haskeline,
mtl,
NLambda >= 1.1 NLambda >= 1.1
library library
@ -32,6 +30,7 @@ library
Examples.RunningExample, Examples.RunningExample,
Examples.Stack, Examples.Stack,
ObservationTable, ObservationTable,
ObservationTableClass,
SimpleObservationTable, SimpleObservationTable,
Teacher, Teacher,
Teachers.Teacher, Teachers.Teacher,

View file

@ -1,9 +1,9 @@
{-# language PartialTypeSignatures #-} {-# language PartialTypeSignatures #-}
{-# language RecordWildCards #-}
{-# OPTIONS_GHC -Wno-partial-type-signatures #-} {-# OPTIONS_GHC -Wno-partial-type-signatures #-}
module Bollig where module Bollig where
import AbstractLStar import AbstractLStar
import ObservationTableClass
import SimpleObservationTable import SimpleObservationTable
import Teacher import Teacher
@ -31,55 +31,53 @@ mqToBool teacher words = answer
answer = map (setB True) inw `union` map (setB False) outw answer = map (setB True) inw `union` map (setB False) outw
setB b (w, _) = (w, b) setB b (w, _) = (w, b)
tableAt :: NominalType i => BTable i -> [i] -> [i] -> Formula
tableAt t s e = singleton True `eq` mapFilter (\(i, o) -> maybeIf ((s ++ e) `eq` i) o) (content t)
rfsaClosednessTest :: NominalType i => Set (BRow i) -> BTable i -> TestResult i rfsaClosednessTest :: NominalType i => Set (BRow i) -> BTable i -> TestResult i
rfsaClosednessTest primesUpp t@Table{..} = case solve (isEmpty defect) of rfsaClosednessTest primesUpp t = case solve (isEmpty defect) of
Just True -> Succes Just True -> Succes
Just False -> trace "Not closed" $ Failed defect empty Just False -> trace "Not closed" $ Failed defect empty
Nothing -> trace "@@@ Unsolved Formula (rfsaClosednessTest) @@@" $ Nothing -> trace "@@@ Unsolved Formula (rfsaClosednessTest) @@@" $
Failed defect empty Failed defect empty
where where
defect = filter (\ua -> brow t ua `neq` sum (filter (`isSubsetOf` brow t ua) primesUpp)) (rowsExt t) defect = filter (\ua -> row t ua `neq` sum (filter (`isSubsetOf` row t ua) primesUpp)) (rowsExt t)
rfsaConsistencyTest :: NominalType i => BTable i -> TestResult i rfsaConsistencyTest :: NominalType i => BTable i -> TestResult i
rfsaConsistencyTest t@Table{..} = case solve (isEmpty defect) of rfsaConsistencyTest t = case solve (isEmpty defect) of
Just True -> Succes Just True -> Succes
Just False -> trace "Not consistent" $ Failed empty defect Just False -> trace "Not consistent" $ Failed empty defect
Nothing -> trace "@@@ Unsolved Formula (rfsaConsistencyTest) @@@" $ Nothing -> trace "@@@ Unsolved Formula (rfsaConsistencyTest) @@@" $
Failed empty defect Failed empty defect
where where
candidates = pairsWithFilter (\u1 u2 -> maybeIf (brow t u2 `isSubsetOf` brow t u1) (u1, u2)) rows rows 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 (tableAt t (u1 ++ [a]) v) /\ tableAt t (u2 ++ [a]) v) (a:v)) candidates alph columns 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 :: NominalType i => Set (BRow i) -> BTable i -> Automaton (BRow i) i constructHypothesisBollig :: NominalType i => Set (BRow i) -> BTable i -> Automaton (BRow i) i
constructHypothesisBollig primesUpp t@Table{..} = automaton q alph d i f constructHypothesisBollig primesUpp t = automaton q (alph t) d i f
where where
q = primesUpp q = primesUpp
i = filter (`isSubsetOf` brow t []) q i = filter (`isSubsetOf` rowEps t) q
f = filter (`contains` []) q f = filter (`contains` []) q
-- TODO: compute indices of primesUpp only once -- TODO: compute indices of primesUpp only once
d0 = triplesWithFilter (\s a bs2 -> maybeIf (bs2 `isSubsetOf` brow t (s ++ [a])) (brow t s, a, bs2)) rows alph q 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 d = filter (\(q1, _, _) -> q1 `member` q) d0
-- Adds all suffixes as columns -- Adds all suffixes as columns
-- TODO: do actual Rivest and Schapire -- TODO: do actual Rivest and Schapire
addCounterExample :: NominalType i => MQ i Bool -> Set [i] -> BTable i -> BTable i addCounterExample :: NominalType i => MQ i Bool -> Set [i] -> BTable i -> BTable i
addCounterExample mq ces t@Table{..} = addCounterExample mq ces t =
let newColumns = sum . map (fromList . tails) $ ces let newColumns = sum . map (fromList . tails) $ ces
newColumnsRed = newColumns \\ columns newColumnsRed = newColumns \\ cols t
in addColumns mq newColumnsRed t in addColumns mq newColumnsRed t
learnBollig :: (NominalType i, _) => Int -> Int -> Teacher i -> Automaton (BRow i) i learnBollig :: (NominalType i, _) => Int -> Int -> Teacher i -> Automaton (BRow i) i
learnBollig k n teacher = learnBolligLoop teacher (initialTableSize (mqToBool teacher) (alphabet teacher) k n) learnBollig k n teacher = learnBolligLoop teacher (initialBTableSize (mqToBool teacher) (alphabet teacher) k n)
learnBolligLoop :: (NominalType i, _) => Teacher i -> BTable i -> Automaton (BRow i) i learnBolligLoop :: (NominalType i, _) => Teacher i -> BTable i -> Automaton (BRow i) i
learnBolligLoop teacher t@Table{..} = learnBolligLoop teacher t =
let let
-- These simplify's do speed up -- These simplify's do speed up
allRowsUpp = simplify $ map (brow t) rows allRowsUpp = simplify $ map (row t) (rows t)
allRows = simplify $ allRowsUpp `union` map (brow t) (rowsExt 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 primesUpp = simplify $ filter (\r -> isNotEmpty r /\ r `neq` sum (filter (`isSubsetOf` r) (allRows \\ orbit [] r))) allRowsUpp
-- No worry, these are computed lazily -- No worry, these are computed lazily

View file

@ -0,0 +1,43 @@
{-# language TypeFamilies #-}
{-# language FunctionalDependencies #-}
module ObservationTableClass where
import NLambda
import Prelude ((++))
-- Words are indices to our table
type RowIndex i = [i]
type ColumnIndex i = [i]
-- Membership queries (TODO: move to Teacher)
type MQ i o = Set [i] -> Set ([i], o)
-- This is a fat class, so that instances could give more efficient implementations
class (NominalType table, NominalType i, NominalType o) => ObservationTable table i o | table -> i o where
-- The type of data in a row is determined by the table
type Row table :: *
-- getters
rows :: table -> Set (RowIndex i)
cols :: table -> Set (ColumnIndex i)
alph :: table -> Set i
row :: table -> RowIndex i -> Row table
-- perhaps not needed
tableAt :: table -> RowIndex i -> ColumnIndex i -> Set o
-- compound getters
rowsExt :: table -> Set (RowIndex i)
colsExt :: table -> Set (ColumnIndex i)
rowEps :: table -> Row table
-- updaters
addRows :: MQ i o -> Set (RowIndex i) -> table -> table
addColumns :: MQ i o -> Set (ColumnIndex i) -> table -> table
-- default implementations
rowsExt t = pairsWith (\r a -> r ++ [a]) (rows t) (alph t)
colsExt t = pairsWith (:) (alph t) (cols t)
rowEps t = row t []

View file

@ -1,14 +1,22 @@
{-# language DeriveAnyClass #-} {-# language DeriveAnyClass #-}
{-# language DeriveGeneric #-} {-# language DeriveGeneric #-}
{-# language RecordWildCards #-} {-# language RecordWildCards #-}
{-# language FlexibleInstances #-}
{-# language MultiParamTypeClasses #-}
{-# language TypeFamilies #-}
{-# language PartialTypeSignatures #-}
{-# OPTIONS_GHC -Wno-partial-type-signatures #-}
module SimpleObservationTable where module SimpleObservationTable where
import ObservationTableClass
import NLambda hiding (fromJust) import NLambda hiding (fromJust)
import GHC.Generics (Generic) import GHC.Generics (Generic)
import Prelude (Bool (..), Eq, Int, Ord, Show (..), fst, (++)) import Prelude (Bool (..), Eq, Int, Ord, Show (..), fst, (++), (.))
import qualified Prelude () import qualified Prelude ()
import Data.Coerce (coerce)
-- We represent functions as their graphs -- We represent functions as their graphs
@ -18,55 +26,88 @@ type Fun i o = Set (i, o)
dom :: (NominalType i, NominalType o) => Fun i o -> Set i dom :: (NominalType i, NominalType o) => Fun i o -> Set i
dom = map fst dom = map fst
-- Words are indices to our table
type RowIndex i = [i]
type ColumnIndex i = [i]
-- A table is nothing more than a part of the language. -- A table is nothing more than a part of the language.
-- Invariant: content is always defined for elements in -- Invariant: content is always defined for elements in
-- `rows * columns` and `rows * alph * columns`. -- `rows * columns` and `rows * alph * columns`.
data Table i o = Table data Table i o = Table
{ content :: Fun [i] o { content :: Fun [i] o
, rows :: Set (RowIndex i) , rowIndices :: Set (RowIndex i)
, columns :: Set (ColumnIndex i) , colIndices :: Set (ColumnIndex i)
, alph :: Set i , aa :: Set i
} }
deriving (Show, Ord, Eq, Generic, NominalType, Conditional, Contextual) deriving (Show, Ord, Eq, Generic, NominalType, Conditional, Contextual)
rowsExt :: (NominalType i, NominalType o) => Table i o -> Set (RowIndex i) instance (NominalType i, NominalType o) => ObservationTable (Table i o) i o where
rowsExt Table{..} = pairsWith (\r a -> r ++ [a]) rows alph type Row (Table i o) = Fun [i] o
rows = rowIndices
cols = colIndices
alph = aa
row Table{..} r = pairsWithFilter (\e (a, b) -> maybeIf (a `eq` (r ++ e)) (e, b)) colIndices content
tableAt Table{..} r c = mapFilter (\(i, o) -> maybeIf ((r ++ c) `eq` i) o) content
columnsExt :: (NominalType i, NominalType o) => Table i o -> Set (RowIndex i) -- Assumption: newRows is disjoint from rows (for efficiency)
columnsExt Table{..} = pairsWith (:) alph columns addRows mq newRows t@Table{..} =
t { content = content `union` newContent
, rowIndices = rowIndices `union` newRows
}
where
newRowsExt = pairsWith (\r a -> r ++ [a]) newRows aa
newPart = pairsWith (++) (newRows `union` newRowsExt) colIndices
newPartRed = newPart \\ dom content
newContent = mq newPartRed
-- I could make a more specific implementation for booleans -- Assumption: newColumns is disjoint from columns (for efficiency)
-- But for now we reuse the above. addColumns mq newColumns t@Table{..} =
type BTable i = Table i Bool t { content = content `union` newContent
, colIndices = colIndices `union` newColumns
-- A row is the data in a table, i.e. a function from columns to the output }
type Row i o = Fun [i] o where
newColumnsExt = pairsWith (:) aa newColumns
row :: (NominalType i, NominalType o) => Table i o -> RowIndex i -> Row i o newPart = pairsWith (++) rowIndices (newColumns `union` newColumnsExt)
row Table{..} r = pairsWithFilter (\e (a, b) -> maybeIf (a `eq` (r ++ e)) (e, b)) columns content newPartRed = newPart \\ dom content
newContent = mq newPartRed
-- Special case of a boolean: functions to Booleans are subsets
type BRow i = Set [i]
-- TODO: slightly inefficient
brow :: NominalType i => BTable i -> RowIndex i -> BRow i
brow Table{..} r = let lang = mapFilter (\(i, o) -> maybeIf (fromBool o) i) content
in filter (\a -> lang `contains` (r ++ a)) columns
-- Membership queries (TODO: move to Teacher) -- I could make a more specific implementation for booleans.
type MQ i o = Set [i] -> Set ([i], o) -- But for now we reuse the above, and do minor optimisations
newtype Boolean table = B { unB :: table }
deriving (Show, Ord, Eq, Generic, NominalType, Conditional, Contextual)
type BTable i = Boolean (Table i Bool)
instance (NominalType i) => ObservationTable (BTable i) i Bool where
-- Special case of a boolean: functions to Booleans are subsets
type Row (BTable i) = Set [i]
rows = coerce (rows :: _ => Table i Bool -> _)
cols = coerce (cols :: _ => Table i Bool -> _)
rowsExt = coerce (rowsExt :: _ => Table i Bool -> _)
colsExt = coerce (colsExt :: _ => Table i Bool -> _)
alph = coerce (alph :: _ => Table i Bool -> _)
tableAt = coerce (tableAt :: _ => Table i Bool -> _)
--rows = rowIndices . unB
--cols = colIndices . unB
--alph = aa . unB
--tableAt = tableAt . unB
-- TODO: slightly inefficient
row (B Table{..}) r = let lang = mapFilter (\(i, o) -> maybeIf (fromBool o) i) content
in filter (\a -> lang `contains` (r ++ a)) colIndices
rowEps (B Table{..}) = mapFilter (\(i, o) -> maybeIf (fromBool o /\ i `member` colIndices) i) content
--addRows mq newRows = B . addRows mq newRows . unB
addRows = coerce (addRows :: _ => _ -> _ -> Table i Bool -> Table i Bool)
--addColumns mq newColumns = B . addColumns mq newColumns . unB
addColumns = coerce (addColumns :: _ => _ -> _ -> Table i Bool -> Table i Bool)
type BRow i = Row (BTable i)
initialTableWith :: (NominalType i, NominalType o) => MQ i o -> Set i -> Set (RowIndex i) -> Set (ColumnIndex i) -> Table i o initialTableWith :: (NominalType i, NominalType o) => MQ i o -> Set i -> Set (RowIndex i) -> Set (ColumnIndex i) -> Table i o
initialTableWith mq alphabet newRows newColumns = Table initialTableWith mq alphabet newRows newColumns = Table
{ content = content { content = content
, rows = newRows , rowIndices = newRows
, columns = newColumns , colIndices = newColumns
, alph = alphabet , aa = alphabet
} }
where where
newColumnsExt = pairsWith (:) alphabet newColumns newColumnsExt = pairsWith (:) alphabet newColumns
@ -79,26 +120,11 @@ initialTable mq alphabet = initialTableWith mq alphabet (singleton []) (singleto
initialTableSize :: (NominalType i, NominalType o) => MQ i o -> Set i -> Int -> Int -> Table i o initialTableSize :: (NominalType i, NominalType o) => MQ i o -> Set i -> Int -> Int -> Table i o
initialTableSize mq alphabet rs cs = initialTableWith mq alphabet (replicateSetUntil rs alphabet) (replicateSetUntil cs alphabet) initialTableSize mq alphabet rs cs = initialTableWith mq alphabet (replicateSetUntil rs alphabet) (replicateSetUntil cs alphabet)
-- Assumption: newRows is disjoint from rows (for efficiency) initialBTableWith :: NominalType i => MQ i Bool -> Set i -> Set (RowIndex i) -> Set (ColumnIndex i) -> BTable i
addRows :: (NominalType i, NominalType o) => MQ i o -> Set (RowIndex i) -> Table i o -> Table i o initialBTableWith = coerce initialTableWith
addRows mq newRows t@Table{..} =
t { content = content `union` newContent
, rows = rows `union` newRows
}
where
newRowsExt = pairsWith (\r a -> r ++ [a]) newRows alph
newPart = pairsWith (++) (newRows `union` newRowsExt) columns
newPartRed = newPart \\ dom content
newContent = mq newPartRed
-- Assumption: newColumns is disjoint from columns (for efficiency) initialBTable :: NominalType i => MQ i Bool -> Set i -> BTable i
addColumns :: (NominalType i, NominalType o) => MQ i o -> Set (ColumnIndex i) -> Table i o -> Table i o initialBTable = coerce initialTable
addColumns mq newColumns t@Table{..} =
t { content = content `union` newContent initialBTableSize :: NominalType i => MQ i Bool -> Set i -> Int -> Int -> BTable i
, columns = columns `union` newColumns initialBTableSize = coerce initialTableSize
}
where
newColumnsExt = pairsWith (:) alph newColumns
newPart = pairsWith (++) rows (newColumns `union` newColumnsExt)
newPartRed = newPart \\ dom content
newContent = mq newPartRed