diff --git a/optimality-theory/Learning.hs b/optimality-theory/Learning.hs new file mode 100644 index 0000000..d3c2927 --- /dev/null +++ b/optimality-theory/Learning.hs @@ -0,0 +1,91 @@ +{-# LANGUAGE OverloadedLists #-} +{-# LANGUAGE OverloadedStrings #-} + +{- This implementation is based on the description of the Recursive Ranking learning algorithm for OT layed out in § 7.3 of +- Kager, René. 2010. Optimality Theory. 10th printing. Cambridge Textbooks in Linguistics. Cambridge: Cambridge Univ. Press. +-} + +module Learning where + +import Data.List (findIndex, intercalate, minimumBy) +import Data.Maybe (fromJust) +import Data.Ord (comparing) +import Data.Set (Set, delete, empty, fromList, insert, member, singleton, toAscList, union) +import Data.String (IsString (..)) +import Data.Text (Text, pack, unpack) + +newtype Grammar = Grammar {strata :: [Set Constraint]} + deriving (Eq) + +instance Show Grammar where + show grammar = intercalate " ≫ " $ map prettySet $ strata grammar + where + prettySet xs = "{" ++ intercalate ", " (map show (toAscList xs)) ++ "}" + +c `rankIn` grammar = findIndex (\stratum -> c `member` stratum) (strata grammar) + +dominates c1 c2 grammar = + let i1 = c1 `rankIn` grammar + i2 = c2 `rankIn` grammar + in i1 < i2 + +newtype Constraint = Constraint {constraintName :: Text} + deriving (Eq, Ord) + +instance IsString Constraint where + fromString = Constraint . pack + +instance Show Constraint where + show = unpack . constraintName + +data MarkDataPair = MarkDataPair + { loserMarks :: Set Constraint, + winnerMarks :: Set Constraint + } + +demoteWith :: MarkDataPair -> Grammar -> Grammar +demoteWith markDataPair grammar = + let highestRankedLoser = minimumBy (comparing (`rankIn` grammar)) (loserMarks markDataPair) + in foldr + ( \winner g -> + if (highestRankedLoser `dominates` winner) grammar + then g + else demoteBelow winner highestRankedLoser g + ) + grammar + (winnerMarks markDataPair) + +demoteBelow :: Constraint -> Constraint -> Grammar -> Grammar +demoteBelow a b grammar = + let rankB = fromJust $ b `rankIn` grammar + strataWithoutA = map (delete a) (strata grammar) + in grammar + { strata = + case splitAt rankB strataWithoutA of + (before, bStratum : belowBStratum : after) -> before ++ bStratum : insert a belowBStratum : after + (before, [bStratum]) -> before ++ [bStratum, singleton a] + } + +singleStratumFrom :: [MarkDataPair] -> Grammar +singleStratumFrom pairs = Grammar [foldr (\p g -> loserMarks p `union` winnerMarks p `union` g) empty pairs] + +learnFrom :: [MarkDataPair] -> Grammar -> Grammar +learnFrom pairs g = go g (cycle pairs) + where + go g (p : ps) = + let g' = demoteWith p g + in if g' == g + then g + else go g' ps + +initialGrammar, resultGrammar :: Grammar +initialGrammar = singleStratumFrom markData +resultGrammar = learnFrom markData initialGrammar + +markData :: [MarkDataPair] +markData = + [ MarkDataPair {loserMarks = ["All-Ft-L"], winnerMarks = ["All-Ft-R"]}, + MarkDataPair {loserMarks = ["Parse-Syl"], winnerMarks = ["All-Ft-L", "All-Ft-R"]}, + MarkDataPair {loserMarks = ["All-Ft-L", "Leftmost", "Parse-Syl"], winnerMarks = ["All-Ft-R", "Rightmost"]}, + MarkDataPair {loserMarks = ["All-Ft-L", "Ft-Bin"], winnerMarks = ["Parse-Syl"]} + ]