Math.Net Numerics has capability to conduct Markov Chair Monte Carlo simulations, yet the document is very sparse. The only examples I found are in F# (see below). In this note, I attempt to port these examples into C# and hope others may find it useful in their research. Note that there are some errors in the original F# code, and this note corrected them. The ported code has been published as ASP.NET web services at x.ecoruse.org. Thus, one can easily copied over to any windows or web development projects.
Ported C# Code
using System;
using System.Collections.Generic;
using System.Web;
using System.Web.Services;
using MathNet.Numerics.Distributions;
using MathNet.Numerics.Statistics;
using MathNet.Numerics.Random;
using MathNet.Numerics.Statistics.Mcmc;
[WebService(Namespace = "http://x.ecourse.org")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.Web.Script.Services.ScriptService]
public class MCMC : System.Web.Services.WebService {
public MCMC () {}
[WebMethod(Description = "Sampling Beta variable via rejection")]
public double[] BetaViaRejection(double a, double b, int N) {
var rnd = new MersenneTwister();
var beta = new Beta(a, b);
var uniform = new ContinuousUniform(0.0, 1.0, rnd);
var rs = new RejectionSampler(
(x => Math.Pow(x, beta.A - 1.0) * Math.Pow(1.0 - x, beta.B - 1.0)),
(x => 0.021), (() => uniform.Sample()));
var arr= rs.Sample(N);
return arr;
//string result = "Theoretical Mean:" + beta.Mean + " vs Sample Mean: "
+ Statistics.Mean(arr) + ";" + "Theoretical StarndardDeviation:"
+ beta.StdDev + " vs Sample StandardDeviation: "
+ Statistics.StandardDeviation(arr) + ";" + " Acceptance Rate:" + rs.AcceptanceRate;
//return result;
}
[WebMethod(Description = "Sampling a normal variable via Metropolis")]
public double[] NormaViaMetropolis(double mean, double stdev, int N)
{
var rnd = new MersenneTwister();
var normal = new Normal(mean, stdev);
var ms = new MetropolisHastingsSampler(0.1, x => Math.Log(normal.Density(x)),
(x,y)=>Normal.PDFLn(x,0.3,y),
x => Normal.Sample(rnd, x, 0.3), 20);
var arr = ms.Sample(N);
return arr;
//string result = "Theoretical Mean:" + beta.Mean + " vs Sample Mean: "
+ Statistics.Mean(arr) + ";" + "Theoretical StarndardDeviation:"
+ beta.StdDev + " vs Sample StandardDeviation: "
+ Statistics.StandardDeviation(arr) + ";" + " Acceptance Rate:" + rs.AcceptanceRate;
//return result;
}
[WebMethod(Description = "Sampling a normal variable via Metropolis symmetric proposal")]
public double[] NormaViaMetropolisSymmetricProposal(double mean, double stdev, int N)
{
var rnd = new MersenneTwister();
var normal = new Normal(mean, stdev);
var ms = new MetropolisHastingsSampler(0.1, x => Math.Log(normal.Density(x)),
(x,y) => npdf(x,y,03),
x => Normal.Sample(rnd,x,0.3), 10);
var arr = ms.Sample(N);
return arr;
//string result = "Theoretical Mean:" + beta.Mean + " vs Sample Mean: "
+ Statistics.Mean(arr) + ";" + "Theoretical StarndardDeviation:"
+ beta.StdDev + " vs Sample StandardDeviation: "
+ Statistics.StandardDeviation(arr) + ";" + " Acceptance Rate:" + rs.AcceptanceRate;
//return result;
}
[WebMethod(Description = "Sampling a normal variable via Metropolis asymmetric proposal")]
public double[] NormaViaMetropolisAsymmetricProposal(double mean, double stdev, int N)
{
var rnd = new MersenneTwister();
var normal = new Normal(mean, stdev);
var ms = new MetropolisHastingsSampler(0.1, x => Math.Log(normal.Density(x)),
(xnew, x) => Math.Log(0.5 * Math.Exp(npdf(xnew,x, 0.3))
+ 0.5 * Math.Exp(npdf(xnew, x+0.1, 0.3))),
x => MixSample(x), 10);
var arr = ms.Sample(N);
return arr;
//string result = "Theoretical Mean:" + beta.Mean + " vs Sample Mean: "
+ Statistics.Mean(arr) + ";" + "Theoretical StarndardDeviation:"
+ beta.StdDev + " vs Sample StandardDeviation: "
+ Statistics.StandardDeviation(arr) + ";" + " Acceptance Rate:" + rs.AcceptanceRate;
//return result;
}
[WebMethod(Description = "Slice sampling a normal distributed random variable")]
public double[] NormaViaSliceSampling(double mean, double stdev, int N)
{
var rnd = new MersenneTwister();
var normal = new Normal(mean, stdev);
var ms = new UnivariateSliceSampler(0.1, x => npdfNoNormalized(x, mean, stdev), 5, 1.0);
var arr = ms.Sample(N);
return arr;
//string result = "Theoretical Mean:" + beta.Mean + " vs Sample Mean: "
+ Statistics.Mean(arr) + ";" + "Theoretical StarndardDeviation:"
+ beta.StdDev + " vs Sample StandardDeviation: "
+ Statistics.StandardDeviation(arr) + ";" + " Acceptance Rate:" + rs.AcceptanceRate;
//return result;
}
public double npdf(double x, double m, double s)
{
return -0.5 * (x - m) * (x - m) / (s * s) - 0.5 * Math.Log(2.0 * System.Math.PI * s * s);
}
public double npdfNoNormalized(double x, double m, double s)
{
return -0.5 * (x - m) * (x - m) / (s * s);
}
public double MixSample(double x)
{
var rnd = new MersenneTwister();
if (Bernoulli.Sample(rnd, 0.5) == 1)
return Normal.Sample(rnd, x, 0.3);
else
return Normal.Sample(rnd, x + 0.1, 0.3);
}
}
Original F# Code
/
// Math.NET Numerics, part of the Math.NET Project
// http://numerics.mathdotnet.com
// http://github.com/mathnet/mathnet-numerics
// http://mathnetnumerics.codeplex.com
//
// Copyright (c) 2009-2013 Math.NET
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
#r "../../out/lib/Net40/MathNet.Numerics.dll"
#r "../../out/lib/Net40/MathNet.Numerics.FSharp.dll"
open MathNet.Numerics
open MathNet.Numerics.Random
open MathNet.Numerics.Statistics
open MathNet.Numerics.Distributions
open MathNet.Numerics.Statistics.Mcmc
/// The number of samples to gather for each sampler.
let N = 10000
/// The random number generator we use for the examples.
let rnd = new MersenneTwister()
//
// Example 1: Sampling a Beta distributed variable through rejection sampling.
//
// Target Distribution: Beta(2.7, 6.3)
//
// -----------------------------------------------------------------------------
do
printfn "Rejection Sampling Example"
/// The target distribution.
let beta = new Beta(2.7, 6.3)
/// Samples uniform distributed variables.
let uniform = new ContinuousUniform(0.0, 1.0, RandomSource = rnd)
/// Implements the rejection sampling procedure.
let rs = new RejectionSampler( ( fun x -> x**(beta.A-1.0) * (1.0 - x)**(beta.B-1.0) ),
( fun x -> 0.021 ),
( fun () -> uniform.Sample()) )
/// An array of samples from the rejection sampler.
let arr = rs.Sample(N)
/// The true distribution.
printfn " Empirical Mean = %f (should be %f)" (Statistics.Mean(arr)) beta.Mean
printfn " Empirical StdDev = %f (should be %f)" (Statistics.StandardDeviation(arr)) beta.StdDev
printfn " Acceptance rate = %f" rs.AcceptanceRate
printfn ""
//
// Example 2: Sampling a normal distributed variable through Metropolis sampling.
//
// Target Distribution: Normal(1.0, 3.5)
//
// -----------------------------------------------------------------------------
do
printfn "Metropolis Sampling Example"
let mean, stddev = 1.0, 3.5
let normal = new Normal(mean, stddev)
/// Implements the rejection sampling procedure.
let ms = new MetropolisSampler( 0.1, (fun x -> log(normal.Density(x))),
(fun x -> Normal.Sample(rnd, x, 0.3)), 20,
RandomSource = rnd )
/// An array of samples from the rejection sampler.
let arr = ms.Sample(N)
/// The true distribution.
printfn " Empirical Mean = %f (should be %f)" (Statistics.Mean(arr)) normal.Mean
printfn " Empirical StdDev = %f (should be %f)" (Statistics.StandardDeviation(arr)) normal.StdDev
printfn " Acceptance rate = %f" ms.AcceptanceRate
printfn ""
//
// Example 3: Sampling a normal distributed variable through Metropolis-Hastings sampling
// with a symmetric proposal distribution.
//
// Target Distribution: Normal(1.0, 3.5)
//
// -----------------------------------------------------------------------------------------
do
printfn "Metropolis Hastings Sampling Example (Symmetric Proposal)"
let mean, stddev = 1.0, 3.5
let normal = new Normal(mean, stddev)
/// Evaluates the log normal distribution.
let npdf x m s = -0.5*(x-m)*(x-m)/(s*s) - 0.5 * log(Constants.Pi2 * s * s)
/// Implements the rejection sampling procedure.
let ms = new MetropolisHastingsSampler( 0.1, (fun x -> log(normal.Density(x))),
(fun x y -> npdf x y 0.3), (fun x -> Normal.Sample(rnd, x, 0.3)), 10,
RandomSource = rnd )
/// An array of samples from the rejection sampler.
let arr = ms.Sample(N)
/// The true distribution.
printfn " Empirical Mean = %f (should be %f)" (Statistics.Mean(arr)) normal.Mean
printfn " Empirical StdDev = %f (should be %f)" (Statistics.StandardDeviation(arr)) normal.StdDev
printfn " Acceptance rate = %f" ms.AcceptanceRate
printfn ""
//
// Example 4: Sampling a normal distributed variable through Metropolis-Hastings sampling
// with a asymmetric proposal distribution.
//
// Target Distribution: Normal(1.0, 3.5)
//
// -----------------------------------------------------------------------------------------
do
printfn "Metropolis Hastings Sampling Example (Assymetric Proposal)"
let mean, stddev = 1.0, 3.5
let normal = new Normal(mean, stddev)
/// Evaluates the logarithm of the normal distribution function.
let npdf x m s = -0.5*(x-m)*(x-m)/(s*s) - 0.5 * log(Constants.Pi2 * s * s)
/// Samples from a mixture that is biased towards samples larger than x.
let mixSample x =
if Bernoulli.Sample(rnd, 0.5) = 1 then
Normal.Sample(rnd, x, 0.3)
else
Normal.Sample(rnd, x + 0.1, 0.3)
/// The transition kernel for the proposal above.
let krnl xnew x = log (0.5 * exp(npdf xnew x 0.3) + 0.5 * exp(npdf xnew (x+0.1) 0.3))
/// Implements the rejection sampling procedure.
let ms = new MetropolisHastingsSampler( 0.1, (fun x -> log(normal.Density(x))),
(fun xnew x -> krnl xnew x), (fun x -> mixSample x), 10,
RandomSource = rnd )
/// An array of samples from the rejection sampler.
let arr = ms.Sample(N)
/// The true distribution.
printfn " Empirical Mean = %f (should be %f)" (Statistics.Mean(arr)) normal.Mean
printfn " Empirical StdDev = %f (should be %f)" (Statistics.StandardDeviation(arr)) normal.StdDev
printfn " Acceptance rate = %f" ms.AcceptanceRate
printfn ""
//
// Example 5: Slice sampling a normal distributed random variable.
//
// Target Distribution: Normal(1.0, 3.5)
//
// -----------------------------------------------------------------------------------------
do
printfn "Slice Sampling Example"
let mean, stddev = 1.0, 3.5
let normal = new Normal(mean, stddev)
/// Evaluates the unnormalized logarithm of the normal distribution function.
let npdf x m s = -0.5*(x-m)*(x-m)/(s*s)
/// Implements the rejection sampling procedure.
let ms = new UnivariateSliceSampler( 0.1, (fun x -> npdf x mean stddev), 5, 1.0, RandomSource = rnd )
/// An array of samples from the rejection sampler.
let arr = ms.Sample(N)
/// The true distribution.
printfn " Empirical Mean = %f (should be %f)" (Statistics.Mean(arr)) normal.Mean
printfn " Empirical StdDev = %f (should be %f)" (Statistics.StandardDeviation(arr)) normal.StdDev
printfn ""