summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorbringert <unknown>2005-10-26 16:13:13 +0000
committerbringert <unknown>2005-10-26 16:13:13 +0000
commit3d4200d3fe6a000e76c95e434987ec1d9e3dddef (patch)
tree273406ad87efa99bb8e242a83da2ec4783f54f63 /src
parent5a9ec2714301cfee4c3a557cc3c966494c802f32 (diff)
Moved Graph and Relation stuff to separate modules. Added some QuickCheck properties for Relation. Improved relation datastructure and algorithms, making FA generation much faster.
Diffstat (limited to 'src')
-rw-r--r--src/GF/Speech/CFGToFiniteState.hs42
-rw-r--r--src/GF/Speech/FiniteState.hs70
-rw-r--r--src/GF/Speech/Graph.hs88
-rw-r--r--src/GF/Speech/Relation.hs124
-rw-r--r--src/GF/Speech/RelationQC.hs39
-rw-r--r--src/GF/Speech/SRG.hs5
6 files changed, 263 insertions, 105 deletions
diff --git a/src/GF/Speech/CFGToFiniteState.hs b/src/GF/Speech/CFGToFiniteState.hs
index 73765aed0..590fd1c6d 100644
--- a/src/GF/Speech/CFGToFiniteState.hs
+++ b/src/GF/Speech/CFGToFiniteState.hs
@@ -5,9 +5,9 @@
-- Stability : (stable)
-- Portability : (portable)
--
--- > CVS $Date: 2005/09/14 16:08:35 $
+-- > CVS $Date: 2005/10/26 17:13:13 $
-- > CVS $Author: bringert $
--- > CVS $Revision: 1.4 $
+-- > CVS $Revision: 1.5 $
--
-- Approximates CFGs with finite state networks.
-----------------------------------------------------------------------------
@@ -24,6 +24,7 @@ import GF.Infra.Ident (Ident)
import GF.Infra.Option (Options)
import GF.Speech.FiniteState
+import GF.Speech.Relation
import GF.Speech.TransformCFG
cfgToFA :: Ident -- ^ Grammar name
@@ -59,10 +60,10 @@ makeRegular g = groupProds $ concatMap trSet (mutRecCats True g)
mutRecCats :: Bool -- ^ If true, all categories will be in some set.
-- If false, only recursive categories will be included.
-> CFRules -> [[Cat_]]
-mutRecCats incAll g = equivalenceClasses $ symmetricSubrelation $ transitiveClosure r'
- where r = nub [(c,c') | (_,rs) <- g, CFRule c ss _ <- rs, Cat c' <- ss]
+mutRecCats incAll g = equivalenceClasses $ refl $ symmetricSubrelation $ transitiveClosure r
+ where r = mkRel [(c,c') | (_,rs) <- g, CFRule c ss _ <- rs, Cat c' <- ss]
allCats = map fst g
- r' = (if incAll then reflexiveClosure allCats else id) r
+ refl = if incAll then reflexiveClosure_ allCats else reflexiveSubrelation
-- Convert a strongly regular grammar to a finite automaton.
compileAutomaton :: Cat_ -- ^ Start category
@@ -130,34 +131,3 @@ isLeftLinear :: Eq c => [c] -- ^ The categories to consider
-> CFRule c n t -- ^ The rule to check for right-linearity
-> Bool
isLeftLinear cs = noCatsInSet cs . drop 1 . ruleRhs
-
-
---
--- * Relations
---
-
--- FIXME: these could use a more efficent data structures and algorithms.
-
-type Rel a = [(a,a)]
-
-isRelatedTo :: Eq a => Rel a -> a -> a -> Bool
-isRelatedTo r x y = (x,y) `elem` r
-
-transitiveClosure :: Eq a => Rel a -> Rel a
-transitiveClosure r = fix (\r -> r `union` [ (x,w) | (x,y) <- r, (z,w) <- r, y == z ]) r
-
-reflexiveClosure :: Eq a => [a] -- ^ The set over which the relation is defined.
- -> Rel a -> Rel a
-reflexiveClosure u r = [(x,x) | x <- u] `union` r
-
-symmetricSubrelation :: Eq a => Rel a -> Rel a
-symmetricSubrelation r = [p | p@(x,y) <- r, (y,x) `elem` r]
-
--- | Get the equivalence classes from an equivalence relation. Since
--- the relation is relexive, the set can be recoved from the relation.
-equivalenceClasses :: Eq a => Rel a -> [[a]]
-equivalenceClasses r = equivalenceClasses_ (nub (map fst r)) r
- where equivalenceClasses_ [] _ = []
- equivalenceClasses_ (x:xs) r = (x:ys):equivalenceClasses_ zs r
- where (ys,zs) = partition (isRelatedTo r x) xs
-
diff --git a/src/GF/Speech/FiniteState.hs b/src/GF/Speech/FiniteState.hs
index e8e80e4be..42aa99e8b 100644
--- a/src/GF/Speech/FiniteState.hs
+++ b/src/GF/Speech/FiniteState.hs
@@ -5,9 +5,9 @@
-- Stability : (stable)
-- Portability : (portable)
--
--- > CVS $Date: 2005/09/22 17:08:48 $
+-- > CVS $Date: 2005/10/26 17:13:13 $
-- > CVS $Author: bringert $
--- > CVS $Revision: 1.13 $
+-- > CVS $Revision: 1.14 $
--
-- A simple finite state network module.
-----------------------------------------------------------------------------
@@ -25,6 +25,7 @@ import Data.List
import Data.Maybe (catMaybes,fromJust)
import GF.Data.Utilities
+import GF.Speech.Graph
import qualified GF.Visualization.Graphviz as Dot
type State = Int
@@ -171,68 +172,3 @@ toGraphviz (FA (Graph _ ns es) s f) = Dot.Graph Dot.Directed [] (map mkNode ns)
++ if n `elem` f then [("style","bold")] else []
mkEdge (x,y,l) = Dot.Edge (show x) (show y) [("label",l)]
---
--- * Graphs
---
-
-data Graph n a b = Graph [n] [Node n a] [Edge n b]
- deriving (Eq,Show)
-
-type Node n a = (n,a)
-type Edge n b = (n,n,b)
-
-type Incoming n a b = [(Node n a,[Edge n b])]
-type Outgoing n a b = [(Node n a,[Edge n b])]
-
-newGraph :: [n] -> Graph n a b
-newGraph ns = Graph ns [] []
-
-nodes :: Graph n a b -> [Node n a]
-nodes (Graph _ ns _) = ns
-
-edges :: Graph n a b -> [Edge n b]
-edges (Graph _ _ es) = es
-
-nmap :: (a -> c) -> Graph n a b -> Graph n c b
-nmap f (Graph c ns es) = Graph c [(n,f l) | (n,l) <- ns] es
-
-emap :: (b -> c) -> Graph n a b -> Graph n a c
-emap f (Graph c ns es) = Graph c ns [(x,y,f l) | (x,y,l) <- es]
-
-newNode :: a -> Graph n a b -> (Graph n a b,n)
-newNode l (Graph (c:cs) ns es) = (Graph cs ((c,l):ns) es, c)
-
-newEdge :: Edge n b -> Graph n a b -> Graph n a b
-newEdge e (Graph c ns es) = Graph c ns (e:es)
-
-newEdges :: [Edge n b] -> Graph n a b -> Graph n a b
-newEdges es' (Graph c ns es) = Graph c ns (es'++es)
-
--- | Get a list of all nodes and their incoming edges.
-incoming :: Ord n => Graph n a b -> Incoming n a b
-incoming = groupEdgesBy getTo
-
--- | Get a list of all nodes and their outgoing edges.
-outgoing :: Ord n => Graph n a b -> Outgoing n a b
-outgoing = groupEdgesBy getFrom
-
-getOutgoing :: Eq n => Outgoing n a b -> n -> [Edge n b]
-getOutgoing out x = head [ es | ((y,_),es) <- out, x == y ]
-
-groupEdgesBy :: (Ord n) => (Edge n b -> n) -> Graph n a b -> [(Node n a,[Edge n b])]
-groupEdgesBy h (Graph _ ns es) =
- snd $ mapAccumL f (sortBy (compareBy h) es) (sortBy (compareBy fst) ns)
- where f es' v@(n,_) = let (nes,es'') = span ((==n) . h) es' in (es'',(v,nes))
-
-getFrom :: Edge n b -> n
-getFrom (f,_,_) = f
-
-getTo :: Edge n b -> n
-getTo (_,t,_) = t
-
-getLabel :: Edge n b -> b
-getLabel (_,_,l) = l
-
-reverseGraph :: Graph n a b -> Graph n a b
-reverseGraph (Graph c ns es) = Graph c ns [ (t,f,l) | (f,t,l) <- es ]
-
diff --git a/src/GF/Speech/Graph.hs b/src/GF/Speech/Graph.hs
new file mode 100644
index 000000000..64b73a70a
--- /dev/null
+++ b/src/GF/Speech/Graph.hs
@@ -0,0 +1,88 @@
+----------------------------------------------------------------------
+-- |
+-- Module : Graph
+-- Maintainer : BB
+-- Stability : (stable)
+-- Portability : (portable)
+--
+-- > CVS $Date: 2005/10/26 17:13:13 $
+-- > CVS $Author: bringert $
+-- > CVS $Revision: 1.1 $
+--
+-- A simple graph module.
+-----------------------------------------------------------------------------
+module GF.Speech.Graph ( Graph(..), Node, Edge, Incoming, Outgoing
+ , newGraph, nodes, edges
+ , nmap, emap, newNode, newEdge, newEdges
+ , incoming, outgoing, getOutgoing
+ , getFrom, getTo, getLabel
+ , reverseGraph
+ ) where
+
+import GF.Data.Utilities
+
+import Data.List
+
+data Graph n a b = Graph [n] [Node n a] [Edge n b]
+ deriving (Eq,Show)
+
+type Node n a = (n,a)
+type Edge n b = (n,n,b)
+
+type Incoming n a b = [(Node n a,[Edge n b])]
+type Outgoing n a b = [(Node n a,[Edge n b])]
+
+newGraph :: [n] -> Graph n a b
+newGraph ns = Graph ns [] []
+
+nodes :: Graph n a b -> [Node n a]
+nodes (Graph _ ns _) = ns
+
+edges :: Graph n a b -> [Edge n b]
+edges (Graph _ _ es) = es
+
+nmap :: (a -> c) -> Graph n a b -> Graph n c b
+nmap f (Graph c ns es) = Graph c [(n,f l) | (n,l) <- ns] es
+
+emap :: (b -> c) -> Graph n a b -> Graph n a c
+emap f (Graph c ns es) = Graph c ns [(x,y,f l) | (x,y,l) <- es]
+
+newNode :: a -> Graph n a b -> (Graph n a b,n)
+newNode l (Graph (c:cs) ns es) = (Graph cs ((c,l):ns) es, c)
+
+newEdge :: Edge n b -> Graph n a b -> Graph n a b
+newEdge e (Graph c ns es) = Graph c ns (e:es)
+
+newEdges :: [Edge n b] -> Graph n a b -> Graph n a b
+newEdges es' (Graph c ns es) = Graph c ns (es'++es)
+
+-- | Get a list of all nodes and their incoming edges.
+incoming :: Ord n => Graph n a b -> Incoming n a b
+incoming = groupEdgesBy getTo
+
+-- | Get a list of all nodes and their outgoing edges.
+outgoing :: Ord n => Graph n a b -> Outgoing n a b
+outgoing = groupEdgesBy getFrom
+
+-- | From a list of outgoing edges, get all edges
+-- starting at a given node.
+getOutgoing :: Eq n => Outgoing n a b -> n -> [Edge n b]
+getOutgoing out x = head [ es | ((y,_),es) <- out, x == y ]
+
+groupEdgesBy :: (Ord n) => (Edge n b -> n) -> Graph n a b -> [(Node n a,[Edge n b])]
+groupEdgesBy h (Graph _ ns es) =
+ snd $ mapAccumL f (sortBy (compareBy h) es) (sortBy (compareBy fst) ns)
+ where f es' v@(n,_) = let (nes,es'') = span ((==n) . h) es' in (es'',(v,nes))
+
+getFrom :: Edge n b -> n
+getFrom (f,_,_) = f
+
+getTo :: Edge n b -> n
+getTo (_,t,_) = t
+
+getLabel :: Edge n b -> b
+getLabel (_,_,l) = l
+
+reverseGraph :: Graph n a b -> Graph n a b
+reverseGraph (Graph c ns es) = Graph c ns [ (t,f,l) | (f,t,l) <- es ]
+
diff --git a/src/GF/Speech/Relation.hs b/src/GF/Speech/Relation.hs
new file mode 100644
index 000000000..2ad3faccb
--- /dev/null
+++ b/src/GF/Speech/Relation.hs
@@ -0,0 +1,124 @@
+----------------------------------------------------------------------
+-- |
+-- Module : Relation
+-- Maintainer : BB
+-- Stability : (stable)
+-- Portability : (portable)
+--
+-- > CVS $Date: 2005/10/26 17:13:13 $
+-- > CVS $Author: bringert $
+-- > CVS $Revision: 1.1 $
+--
+-- A simple module for relations.
+-----------------------------------------------------------------------------
+
+module GF.Speech.Relation (Rel, mkRel
+ , isRelatedTo
+ , transitiveClosure
+ , reflexiveClosure, reflexiveClosure_
+ , symmetricClosure
+ , symmetricSubrelation, reflexiveSubrelation
+ , equivalenceClasses
+ , isTransitive, isReflexive, isSymmetric
+ , isEquivalence,
+ , isSubRelationOf) where
+
+import Data.List
+import Data.Maybe
+import Data.Map (Map)
+import qualified Data.Map as Map
+import Data.Set (Set)
+import qualified Data.Set as Set
+
+import GF.Data.Utilities
+
+type Rel a = Map a (Set a)
+
+-- | Creates a relation from a list of related pairs.
+mkRel :: Ord a => [(a,a)] -> Rel a
+mkRel ps = relates ps Map.empty
+
+relToList :: Rel a -> [(a,a)]
+relToList r = [ (x,y) | (x,ys) <- Map.toList r, y <- Set.toList ys ]
+
+-- | Add a pair to the relation.
+relate :: Ord a => a -> a -> Rel a -> Rel a
+relate x y r = Map.insertWith Set.union x (Set.singleton y) r
+
+-- | Add a list of pairs to the relation.
+relates :: Ord a => [(a,a)] -> Rel a -> Rel a
+relates ps r = foldl (\r' (x,y) -> relate x y r') r ps
+
+-- | Checks if an element is related to another.
+isRelatedTo :: Ord a => Rel a -> a -> a -> Bool
+isRelatedTo r x y = maybe False (y `Set.member`) (Map.lookup x r)
+
+-- | Get the set of elements to which a given element is related.
+allRelated :: Ord a => Rel a -> a -> Set a
+allRelated r x = fromMaybe Set.empty (Map.lookup x r)
+
+-- | Get all elements in the relation.
+domain :: Ord a => Rel a -> Set a
+domain r = foldl Set.union (Map.keysSet r) (Map.elems r)
+
+-- | Keep only pairs for which both elements are in the given set.
+intersectSetRel :: Ord a => Set a -> Rel a -> Rel a
+intersectSetRel s = filterRel (\x y -> x `Set.member` s && y `Set.member` s)
+
+transitiveClosure :: Ord a => Rel a -> Rel a
+transitiveClosure r = fix (Map.map growSet) r
+ where growSet ys = foldl Set.union ys (map (allRelated r) $ Set.toList ys)
+
+reflexiveClosure_ :: Ord a => [a] -- ^ The set over which the relation is defined.
+ -> Rel a -> Rel a
+reflexiveClosure_ u r = relates [(x,x) | x <- u] r
+
+-- | Uses 'domain'
+reflexiveClosure :: Ord a => Rel a -> Rel a
+reflexiveClosure r = reflexiveClosure_ (Set.toList $ domain r) r
+
+symmetricClosure :: Ord a => Rel a -> Rel a
+symmetricClosure r = relates [ (y,x) | (x,y) <- relToList r ] r
+
+symmetricSubrelation :: Ord a => Rel a -> Rel a
+symmetricSubrelation r = filterRel (flip $ isRelatedTo r) r
+
+reflexiveSubrelation :: Ord a => Rel a -> Rel a
+reflexiveSubrelation r = intersectSetRel (reflexiveElements r) r
+
+-- | Get the set of elements which are related to themselves.
+reflexiveElements :: Ord a => Rel a -> Set a
+reflexiveElements r = Set.fromList [ x | (x,ys) <- Map.toList r, x `Set.member` ys ]
+
+-- | Keep the related pairs for which the predicate is true.
+filterRel :: Ord a => (a -> a -> Bool) -> Rel a -> Rel a
+filterRel p = purgeEmpty . Map.mapWithKey (Set.filter . p)
+
+-- | Remove keys that map to no elements.
+purgeEmpty :: Ord a => Rel a -> Rel a
+purgeEmpty r = Map.filter (not . Set.null) r
+
+
+-- | Get the equivalence classes from an equivalence relation.
+equivalenceClasses :: Ord a => Rel a -> [[a]]
+equivalenceClasses r = equivalenceClasses_ (Map.keys r) r
+ where equivalenceClasses_ [] _ = []
+ equivalenceClasses_ (x:xs) r = Set.toList ys:equivalenceClasses_ zs r
+ where ys = allRelated r x
+ zs = [x' | x' <- xs, not (x' `Set.member` ys)]
+
+isTransitive :: Ord a => Rel a -> Bool
+isTransitive r = and [z `Set.member` ys | (x,ys) <- Map.toList r,
+ y <- Set.toList ys, z <- Set.toList (allRelated r y)]
+
+isReflexive :: Ord a => Rel a -> Bool
+isReflexive r = all (\ (x,ys) -> x `Set.member` ys) (Map.toList r)
+
+isSymmetric :: Ord a => Rel a -> Bool
+isSymmetric r = and [isRelatedTo r y x | (x,y) <- relToList r]
+
+isEquivalence :: Ord a => Rel a -> Bool
+isEquivalence r = isReflexive r && isSymmetric r && isTransitive r
+
+isSubRelationOf :: Ord a => Rel a -> Rel a -> Bool
+isSubRelationOf r1 r2 = all (uncurry (isRelatedTo r2)) (relToList r1) \ No newline at end of file
diff --git a/src/GF/Speech/RelationQC.hs b/src/GF/Speech/RelationQC.hs
new file mode 100644
index 000000000..157a53462
--- /dev/null
+++ b/src/GF/Speech/RelationQC.hs
@@ -0,0 +1,39 @@
+----------------------------------------------------------------------
+-- |
+-- Module : RelationQC
+-- Maintainer : BB
+-- Stability : (stable)
+-- Portability : (portable)
+--
+-- > CVS $Date: 2005/10/26 17:13:13 $
+-- > CVS $Author: bringert $
+-- > CVS $Revision: 1.1 $
+--
+-- QuickCheck properties for GF.Speech.Relation
+-----------------------------------------------------------------------------
+
+module GF.Speech.RelationQC where
+
+import GF.Speech.Relation
+
+import Test.QuickCheck
+
+prop_transitiveClosure_trans :: [(Int,Int)] -> Bool
+prop_transitiveClosure_trans ps = isTransitive (transitiveClosure (mkRel ps))
+
+prop_symmetricSubrelation_symm :: [(Int,Int)] -> Bool
+prop_symmetricSubrelation_symm ps = isSymmetric (symmetricSubrelation (mkRel ps))
+
+prop_symmetricSubrelation_sub :: [(Int,Int)] -> Bool
+prop_symmetricSubrelation_sub ps = symmetricSubrelation r `isSubRelationOf` r
+ where r = mkRel ps
+
+prop_symmetricClosure_symm :: [(Int,Int)] -> Bool
+prop_symmetricClosure_symm ps = isSymmetric (symmetricClosure (mkRel ps))
+
+prop_reflexiveClosure_refl :: [(Int,Int)] -> Bool
+prop_reflexiveClosure_refl ps = isReflexive (reflexiveClosure (mkRel ps))
+
+prop_mkEquiv_equiv :: [(Int,Int)] -> Bool
+prop_mkEquiv_equiv ps = isEquivalence (mkEquiv ps)
+ where mkEquiv = transitiveClosure . symmetricClosure . reflexiveClosure . mkRel \ No newline at end of file
diff --git a/src/GF/Speech/SRG.hs b/src/GF/Speech/SRG.hs
index 24f2e868d..e9fb000be 100644
--- a/src/GF/Speech/SRG.hs
+++ b/src/GF/Speech/SRG.hs
@@ -5,9 +5,9 @@
-- Stability : (stable)
-- Portability : (portable)
--
--- > CVS $Date: 2005/09/14 15:17:30 $
+-- > CVS $Date: 2005/10/26 17:13:13 $
-- > CVS $Author: bringert $
--- > CVS $Revision: 1.17 $
+-- > CVS $Revision: 1.18 $
--
-- Representation of, conversion to, and utilities for
-- printing of a general Speech Recognition Grammar.
@@ -33,6 +33,7 @@ import Data.List
import Data.Maybe (fromMaybe)
import Data.FiniteMap
+import Debug.Trace
data SRG = SRG { grammarName :: String -- ^ grammar name
, startCat :: String -- ^ start category name