// // Math.NET Numerics, part of the Math.NET Project // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // // Copyright (c) 2009-2010 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. // using System; using System.Linq; using IStation.Numerics.Distributions; using IStation.Numerics.Random; namespace IStation.Numerics.Statistics.Mcmc { /// /// A hybrid Monte Carlo sampler for multivariate distributions. /// public class HybridMC : HybridMCGeneric { /// /// Number of parameters in the density function. /// private readonly int _length; /// /// Distribution to sample momentum from. /// private Normal _pDistribution; /// /// Standard deviations used in the sampling of different components of the /// momentum. /// private double[] _mpSdv; /// /// Gets or sets the standard deviations used in the sampling of different components of the /// momentum. /// /// When the length of pSdv is not the same as Length. public double[] MomentumStdDev { get => (double[])_mpSdv.Clone(); set { CheckVariance(value); _mpSdv = (double[])value.Clone(); } } /// /// Constructs a new Hybrid Monte Carlo sampler for a multivariate probability distribution. /// The components of the momentum will be sampled from a normal distribution with standard deviation /// 1 using the default random /// number generator. A three point estimation will be used for differentiation. /// This constructor will set the burn interval. /// /// The initial sample. /// The log density of the distribution we want to sample from. /// Number frog leap simulation steps. /// Size of the frog leap simulation steps. /// The number of iterations in between returning samples. /// When the number of burnInterval iteration is negative. public HybridMC(double[] x0, DensityLn pdfLnP, int frogLeapSteps, double stepSize, int burnInterval = 0) : this(x0, pdfLnP, frogLeapSteps, stepSize, burnInterval, new double[x0.Length], SystemRandomSource.Default, Grad) { for (int i = 0; i < _length; i++) { _mpSdv[i] = 1; } } /// /// Constructs a new Hybrid Monte Carlo sampler for a multivariate probability distribution. /// The components of the momentum will be sampled from a normal distribution with standard deviation /// specified by pSdv using the default random /// number generator. A three point estimation will be used for differentiation. /// This constructor will set the burn interval. /// /// The initial sample. /// The log density of the distribution we want to sample from. /// Number frog leap simulation steps. /// Size of the frog leap simulation steps. /// The number of iterations in between returning samples. /// The standard deviations of the normal distributions that are used to sample /// the components of the momentum. /// When the number of burnInterval iteration is negative. public HybridMC(double[] x0, DensityLn pdfLnP, int frogLeapSteps, double stepSize, int burnInterval, double[] pSdv) : this(x0, pdfLnP, frogLeapSteps, stepSize, burnInterval, pSdv, SystemRandomSource.Default) { } /// /// Constructs a new Hybrid Monte Carlo sampler for a multivariate probability distribution. /// The components of the momentum will be sampled from a normal distribution with standard deviation /// specified by pSdv using the a random number generator provided by the user. /// A three point estimation will be used for differentiation. /// This constructor will set the burn interval. /// /// The initial sample. /// The log density of the distribution we want to sample from. /// Number frog leap simulation steps. /// Size of the frog leap simulation steps. /// The number of iterations in between returning samples. /// The standard deviations of the normal distributions that are used to sample /// the components of the momentum. /// Random number generator used for sampling the momentum. /// When the number of burnInterval iteration is negative. public HybridMC(double[] x0, DensityLn pdfLnP, int frogLeapSteps, double stepSize, int burnInterval, double[] pSdv, System.Random randomSource) : this(x0, pdfLnP, frogLeapSteps, stepSize, burnInterval, pSdv, randomSource, Grad) { } /// /// Constructs a new Hybrid Monte Carlo sampler for a multivariate probability distribution. /// The components of the momentum will be sampled from a normal distribution with standard deviations /// given by pSdv. This constructor will set the burn interval, the method used for /// numerical differentiation and the random number generator. /// /// The initial sample. /// The log density of the distribution we want to sample from. /// Number frog leap simulation steps. /// Size of the frog leap simulation steps. /// The number of iterations in between returning samples. /// The standard deviations of the normal distributions that are used to sample /// the components of the momentum. /// Random number generator used for sampling the momentum. /// The method used for numerical differentiation. /// When the number of burnInterval iteration is negative. /// When the length of pSdv is not the same as x0. public HybridMC(double[] x0, DensityLn pdfLnP, int frogLeapSteps, double stepSize, int burnInterval, double[] pSdv, System.Random randomSource, DiffMethod diff) : base(x0, pdfLnP, frogLeapSteps, stepSize, burnInterval, randomSource, diff) { _length = x0.Length; MomentumStdDev = pSdv; Initialize(x0); Burn(BurnInterval); } /// /// Initialize parameters. /// /// The current location of the sampler. private void Initialize(double[] x0) { Current = (double[])x0.Clone(); _pDistribution = new Normal(0.0, 1.0, RandomSource); } /// /// Checking that the location and the momentum are of the same dimension and that each component is positive. /// /// The standard deviations used for sampling the momentum. /// When the length of pSdv is not the same as Length or if any /// component is negative. /// When pSdv is null. private void CheckVariance(double[] pSdv) { if (pSdv == null) { throw new ArgumentNullException(nameof(pSdv), "Standard deviation cannot be null."); } if (pSdv.Length != _length) { throw new ArgumentOutOfRangeException(nameof(pSdv), "Standard deviation of momentum must have same length as sample."); } if (pSdv.Any(sdv => sdv < 0)) { throw new ArgumentOutOfRangeException(nameof(pSdv), "Standard deviation must be positive."); } } /// /// Use for copying objects in the Burn method. /// /// The source of copying. /// A copy of the source object. protected override double[] Copy(double[] source) { var destination = new double[_length]; Array.Copy(source, 0, destination, 0, _length); return destination; } /// /// Use for creating temporary objects in the Burn method. /// /// An object of type T. protected override double[] Create() { return new double[_length]; } /// protected override void DoAdd(ref double[] first, double factor, double[] second) { for (int i = 0; i < _length; i++) { first[i] += factor * second[i]; } } /// protected override void DoSubtract(ref double[] first, double factor, double[] second) { for (int i = 0; i < _length; i++) { first[i] -= factor * second[i]; } } /// protected override double DoProduct(double[] first, double[] second) { double prod = 0; for (int i = 0; i < _length; i++) { prod += first[i] * second[i]; } return prod; } /// /// Samples the momentum from a normal distribution. /// /// The momentum to be randomized. protected override void RandomizeMomentum(ref double[] p) { for (int j = 0; j < _length; j++) { p[j] = _mpSdv[j] * _pDistribution.Sample(); } } /// /// The default method used for computing the gradient. Uses a simple three point estimation. /// /// Function which the gradient is to be evaluated. /// The location where the gradient is to be evaluated. /// The gradient of the function at the point x. static double[] Grad(DensityLn function, double[] x) { int length = x.Length; var returnValue = new double[length]; var increment = new double[length]; var decrement = new double[length]; Array.Copy(x, 0, increment, 0, length); Array.Copy(x, 0, decrement, 0, length); for (int i = 0; i < length; i++) { double y = x[i]; double h = Math.Max(10e-4, (10e-7) * y); increment[i] += h; decrement[i] -= h; returnValue[i] = (function(increment) - function(decrement)) / (2 * h); increment[i] = y; decrement[i] = y; } return returnValue; } } }