1
votes

I am just starting out with Haskell, I have read up to the defining data types section of LYAH and am attempting to implement the Sum-Product algorithm for Belief Propagation. One of the rudimentary tasks is to define the Probabilistic Graphical Model.

As shown below, I have attempted to create a graph by tying the knot to represent the graph where each node represents a Gaussian distribution and has constant weight links(for now) to it's neighbours. However, when trying to define the Mean and Covariance types I am having some difficulty in specifying the types of the Matrix and Vector types, i.e. Float or Double.

module Graph(Graph) where

import Numeric.LinearAlgebra

data Mean = Mean Vector
data Covariance = Covariance Matrix
data Gaussian = Gaussian Mean Covariance

data Node = Node [Node] Gaussian
data Graph = Graph [Node]

In this simple example, what is the syntax to define Mean as a Vector of type Double and Covariance as a Matrix of type Double. Additionally, how would one generalise so that Mean and Covariance can be of type Float or Double?

I currently get the following from GHCi

Graph.hs:5:18: error:
    • Expecting one more argument to ‘Vector’
      Expected a type, but ‘Vector’ has kind ‘* -> *’
    • In the type ‘Vector’
      In the definition of data constructor ‘Mean’
      In the data declaration for ‘Mean’
Failed, modules loaded: none.

I am using the hmatrix package as described here

1

1 Answers

2
votes

Vector and Matrix are parameterised on the scalar type (so you can not only have matrices of floating-point “real numbers”, but also matrices of integers, complex numbers etc.). This is what GHC tells you by ‘Vector’ has kind ‘* -> *’: by itself, Vector is not a type (types have kind *, aka Type). Rather it is a type function mapping types of kind * to types of kind *. Scalars like Double are already plain types, so you can just apply Vector to them.

GHCi> :kind Vector
Vector :: * -> *
GHCi> :k Double
Double :: *
GHCi> :k Vector Double
Vector Double :: *

Thus you need

newtype Mean = Mean (Vector Double)
newtype Covariance = Covariance (Matrix Double)

(newtype does the same thing as data here, but it's a bit more efficient because no extra box/pointer is needed).

Alternatively, you may use more meaningfully-typed vector spaces, e.g.

import Math.LinearMap.Category

newtype Mean v = Mean v
newtype Covariance v = Covariance (v +> DualVector v)

The advantage of this is that dimensions are checked at compile time, which prevents nasty runtime errors (and can in principle also improve performance, though frankly the linearmap-category library is not optimised at all yet).

You'd then also parameterise the other types over the vector space:

data Gaußian v = Gaußian (Mean v) (Covariance v)
data Node v = Node [Node v] (Gaussian v)
data Graph v = Graph [Node v]

Somewhat unrelated to your question: this knot-tying sure feels elegant, but it's not really a suitable way to represent a graph, because nodes can't be identity-checked. Any cycles in the graph lead to, for all distinguishable means, an infinite structure. In practice, you won't get around giving your nodes e.g. Int labels and keeping a separate structure for the edges.