hbayes-0.5: Bayes/PrivateTypes.hs
{- | Private types for Bayes and Factors.
Those type are not exported
-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
module Bayes.PrivateTypes(
-- * Classes and types
BayesianDiscreteVariable(..)
, BayesianVariable(..)
, Set(..)
, BayesianNetwork(..)
-- * Variables
, DV(..)
, DVSet(..)
, DVISet(..)
, TDV
, tdv
, tdvi
, CV(..)
, CVI(..)
-- * Instantiations
, Instantiable(..)
, InstantiationValue(..)
, DVI(..)
, setDVValue
, instantiationVariable
, fromDVSet
-- * Vertices, Graph
, Vertex(..)
, Edge(..)
, SimpleGraph(..)
, DE(..)
, UE(..)
-- * Misc
, getMinBound
-- * Indices
, MultiIndex(..)
, forAllInstantiations
, indicesForDomain
, instantiationDetails
, instantiation
, allInstantiationsForOneVariable
-- * For sampling
, Sample(..)
, DistributionSupport(..)
, DistributionF(..)
-- * Tests
, instantiationProp
) where
import qualified Data.List as L
import qualified Data.Vector.Unboxed as V
import Test.QuickCheck
import Test.QuickCheck.Arbitrary
import System.Random(Random)
import qualified Data.IntMap as IM
import qualified Data.Map as M
import Control.Monad.Reader
-- | Bayesian network. g must be a directed graph and f a factor
type BayesianNetwork g f = g () f
{-
For sampling
-}
-- | A sample (graph of instantiations)
type Sample g a = BayesianNetwork g a
-- | Support of the distributiob p(x | abc ...) for x depending on the value of a,b,c ...
data DistributionSupport a = BoundedSupport !a
| Unbounded !Double -- ^ Standard deviation
deriving(Eq)
-- | Distribution function with a possible bound for the function target
-- The bound may depend on the current sample (because for unbounded we need a variance to be able
-- to cover enough of the support)
data DistributionF g bound inst = Distri !(Reader (Sample g inst) (DistributionSupport bound)) !(inst -> Reader (Sample g inst) Double)
{-
Set
-}
-- | A Set of variables used in a factor. s is the set and a the variable
class Set s where
-- | Empty set
emptySet :: s a
-- | Union of two sets
union :: Eq a => s a -> s a -> s a
-- | Intersection of two sets
intersection :: Eq a => s a -> s a -> s a
-- | Difference of two sets
difference :: Eq a => s a -> s a -> s a
-- | Check if the set is empty
isEmpty :: s a -> Bool
-- | Check if an element is member of the set
isElem :: Eq a => a -> s a -> Bool
-- | Add an element to the set
addElem :: Eq a => a -> s a -> s a
-- | Number of elements in the set
nbElements :: s a -> Int
-- | Check if a set is subset of another one
subset :: Eq a => s a -> s a -> Bool
-- | Check set equality
equal :: Eq a => s a -> s a -> Bool
equal sa sb = (sa `subset` sb) && (sb `subset` sa)
instance Set [] where
emptySet = []
union = L.union
intersection = L.intersect
difference a b = a L.\\ b
isEmpty [] = True
isEmpty _ = False
isElem = L.elem
addElem a l = if a `elem` l then l else a:l
nbElements = length
subset sa sb = all (`elem` sb) sa
{-
Misc
-}
-- | Vertex type used to identify a vertex in a graph
newtype Vertex = Vertex {vertexId :: Int} deriving(Eq,Ord)
-- | Edge type used to identify and edge in a graph
data Edge = Edge !Vertex !Vertex deriving(Eq,Ord,Show)
-- | Implementtaion of a SimpleGraph
data SimpleGraph local edgedata vertexdata = SP {
-- | Mapping of edge to edge data
edgeMap :: !(M.Map Edge edgedata)
-- ^ Mapping of vertex number to vertex neighborhood and vertex data
, vertexMap :: !(IM.IntMap (local, vertexdata))
-- ^ Vertex names. Used only to generate the graphviz representation. Names are useless for the algorithms
-- and I don't want them to appear in the vertex values which should only be factor. Otherwise, the algorithms
-- are less elegant since I have to extract the factors from the values
, nameMap :: !(IM.IntMap String)
}
-- | Neighborhood structure for directed or undirected edges
-- | Directed edges
data DE = DE ![Edge] ![Edge] deriving(Eq,Show)
-- | Undirected edges
data UE = UE ![Edge] deriving(Eq,Show)
instance Show Vertex where
show (Vertex v) = "v" ++ show v
-- | A Bayesian Variable is a variable part of Bayesian network and so which knows
-- its position : the vertex.
class BayesianVariable v where
vertex :: v -> Vertex
-- | A discrete variable has a number of levels which is required to size the factors
class BayesianVariable v => BayesianDiscreteVariable v where
dimension :: v -> Int
dv :: v -> DV
-- | Get the minimum bound for a type
getMinBound :: Bounded a => a -> a
getMinBound _ = minBound
instance BayesianVariable Vertex where
vertex v = v
{-
Variables
-}
-- | A continuous variable
data CV = CV !Vertex deriving(Eq,Ord)
instance Show CV where
show (CV v) = show v
instance BayesianVariable CV where
vertex (CV v) = v
-- | A continuous variable instantiation
data CVI = CVI !CV !Double deriving(Eq,Ord,Show)
instance BayesianVariable CVI where
vertex (CVI v _) = vertex v
-- | A discrete variable
data DV = DV !Vertex !Int deriving(Eq,Ord)
-- | A set of discrete variables
-- The tag is used to check that an index is used with the right set of DV
newtype DVSet s = DVSet [DV] deriving(Eq,Show)
-- | Remove the type tag when not needed
fromDVSet :: DVSet s -> [DV]
fromDVSet (DVSet l) = l
instance Show DV where
show (DV v d) = show v ++ "(" ++ show d ++ ")"
instance BayesianVariable DV where
vertex (DV v _) = v
instance BayesianDiscreteVariable DV where
dimension (DV _ d) = d
dv = id
-- | A typed discrete variable
data TDV s = TDV !Vertex !Int deriving(Eq,Ord)
instance Show (TDV s) where
show (TDV v d) = show v
instance BayesianVariable (TDV s) where
vertex (TDV v _) = v
instance BayesianDiscreteVariable (TDV s) where
dimension (TDV _ d) = d
dv (TDV v nb) = DV v nb
-- | Typed discrete variable
tdv :: DV -> TDV s
tdv (DV v nb) = TDV v nb
-- | Typed instantiation
tdvi :: Enum s => DVI -> (TDV s,s)
tdvi (DVI dv value) = (tdv dv, toEnum value)
{-
Index
-}
newtype MultiIndex s = MultiIndex (V.Vector Int) deriving(Eq,Show)
-- | Get the instantiations for a given multindex
instantiation :: DVSet s -> MultiIndex s -> [DVI]
instantiation (DVSet l) (MultiIndex v) = zipWith setDVValue l (V.toList v)
-- | Generate all the indices for a set of variables
indicesForDomain :: DVSet s -> [MultiIndex s]
{-# INLINE indicesForDomain #-}
indicesForDomain (DVSet l) = map (MultiIndex . V.fromList) $ (mapM indicesForOneDomain l)
where
indicesForOneDomain (DV _ d) = [0..d-1]
allInstantiationsForOneVariable :: DV -> [DVI]
allInstantiationsForOneVariable v@(DV _ d) = map (setDVValue v) [0..d-1]
-- | Generate all instantiations of variables
-- The DVInt can be in any order so the tag s is not used
forAllInstantiations :: DVSet s -> [[DVI]]
forAllInstantiations (DVSet l) = mapM allInstantiationsForOneVariable l
{-
Instantiations
-}
-- | Discrete Variable instantiation. A variable and its value
data DVI = DVI DV !Int deriving(Eq)
instance Show (DVI) where
show (DVI (DV v _) i) = show v ++ "=" ++ show i
-- | A set of variable instantiations
type DVISet = [DVI]
class InstantiationValue i v | i -> v where
instantiationValue :: i -> v
toDouble :: i -> Double
instance InstantiationValue DVI Int where
-- | Extract value of the instantiation
instantiationValue (DVI _ v) = v
toDouble (DVI _ v) = fromIntegral v
instance InstantiationValue CVI Double where
-- | Extract value of the instantiation
instantiationValue (CVI _ v) = v
toDouble (CVI _ v) = v
class Instantiable d v r | d -> r where
-- | Create a variable instantiation using values from
-- an enumeration
(=:) :: d -> v -> r
instance Instantiable CV Double CVI where
(=:) c x = CVI c x
instance (Bounded b, Enum b) => Instantiable DV b DVI where
(=:) a b = setDVValue a (fromEnum b - fromEnum (getMinBound b))
instance (Bounded b, Enum b) => Instantiable (TDV b) b DVI where
(=:) (TDV v nb) b = setDVValue (DV v nb) (fromEnum b - fromEnum (getMinBound b))
-- | Create a discrete variable instantiation for a given discrete variable
setDVValue :: DV -> Int -> DVI
setDVValue v a = DVI v a
instance BayesianVariable DVI where
vertex (DVI dv _) = vertex dv
instance BayesianDiscreteVariable DVI where
dimension (DVI v _) = dimension v
dv = instantiationVariable
-- | Get the variables and their values with a type constraint
instantiationDetails :: [DVI] -> (DVSet s, MultiIndex s)
instantiationDetails l = (DVSet $ map instantiationVariable l, MultiIndex . V.fromList . map (instantiationValue) $ l)
-- | Discrete variable from the instantiation
instantiationVariable (DVI dv _) = dv
{-
QuickCheck
-}
{-
CPT can't have same same vertex values but with different sizes.
But, arbitrary CPT generation will general several vertex with same vertex id
and different vertex size.
So, we introduce a function mapping a vertex ID to a vertex size. So, vertex size are hard coded
-}
quickCheckVertexSize :: Int -> Int
quickCheckVertexSize 0 = 2
quickCheckVertexSize 1 = 2
quickCheckVertexSize 2 = 2
quickCheckVertexSize _ = 2
-- | Generate a random value until this value is not already present in the list
whileIn :: (Arbitrary a, Eq a) => [a] -> Gen a -> Gen a
whileIn l m = do
newVal <- m
if newVal `elem` l
then
whileIn l m
else
return newVal
-- | Generate a random vector of n elements without replacement (no duplicate)
-- May loop if the range is too small !
generateWithoutReplacement :: (Random a, Arbitrary a, Eq a)
=> Int -- ^ Vector size
-> (a,a) -- ^ Bounds
-> Gen [a]
generateWithoutReplacement n b | n == 1 = generateSingle b
| n > 1 = generateMultiple n b
| otherwise = return []
where
generateSingle b = do
r <- choose b
return [r]
generateMultiple n b = do
l <- generateWithoutReplacement (n-1) b
newelem <- whileIn l $ choose b
return (newelem:l)
-- | Check that we can recover an instantiation from a MultiIndex
instantiationProp :: DVSet s -> Bool
instantiationProp dvl =
let dvs = DVSet (fromDVSet dvl)
in
forAllInstantiations dvs == map (instantiation dvs) (indicesForDomain dvs)
instance Arbitrary (DVSet s) where
arbitrary = do
nbVertex <- choose (1,4) :: Gen Int
vertexNumbers <- generateWithoutReplacement nbVertex (0,50)
let dimensions = map (\i -> (DV (Vertex i) (quickCheckVertexSize i))) vertexNumbers
return (DVSet dimensions)