Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2893_BNLR/HeuristicLab.Random/3.3/HamiltonianMonteCarlo.cs @ 18062

Last change on this file since 18062 was 15749, checked in by gkronber, 7 years ago

#2893: normalized line endings

File size: 6.8 KB
Line 
1#region License Information
2
3/* HeuristicLab
4 * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
5 *
6 * This file is part of HeuristicLab.
7 *
8 * HeuristicLab is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * HeuristicLab is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#endregion
23
24using System;
25using System.Collections.Generic;
26using System.Linq;
27using HeuristicLab.Core;
28
29namespace HeuristicLab.Random {
30  /// <summary>
31  /// See Radford Neal, MCMC using Hamiltonian dynamics, 2011, https://arxiv.org/pdf/1206.1901.pdf
32  /// Algorithm from the paper:
33  ///
34  /// HMC = function (U, grad_U, epsilon, L, current_q)
35  /// {
36  ///   q = current_q
37  ///   p = rnorm(length(q), 0, 1) # independent standard normal variates
38  ///   current_p = p
39  ///   # Make a half step for momentum at the beginning
40  ///   p = p - epsilon * grad_U(q) / 2
41  ///   # Alternate full steps for position and momentum
42  ///   for (i in 1:L)
43  ///   {
44  ///     # Make a full step for the position
45  ///     q = q + epsilon* p
46  ///     # Make a full step for the momentum, except at end of trajectory
47  ///     if (i!=L) p = p - epsilon * grad_U(q)
48  ///   }
49  ///   # Make a half step for momentum at the end.
50  ///   p = p - epsilon* grad_U(q) / 2
51  ///   # Negate momentum at end of trajectory to make the proposal symmetric
52  ///   p = -p
53  ///   # Evaluate potential and kinetic energies at start and end of trajectory
54  ///     current_U = U(current_q)
55  ///     current_K = sum(current_p^2) / 2
56  ///     proposed_U = U(q)
57  ///     proposed_K = sum(p^2) / 2
58  ///   # Accept or reject the state at end of trajectory, returning either
59  ///   # the position at the end of the trajectory or the initial position
60  ///   if (runif(1) < exp(current_U-proposed_U+current_K-proposed_K)) {
61  ///     return (q) # accept
62  ///   }
63  ///   else {
64  ///     return (current_q) # reject
65  ///   }
66  /// }
67
68  /// </summary>
69  public static class HamiltonianMonteCarlo {
70
71    public static IEnumerable<double[]> SampleChain(double[] startingPosition,
72      Func<double[], Tuple<double, double[]>> potentialEnergyFunction,
73      IRandom uniformRandom, double stepSize, int steps = 10) {
74      var x = (double[])startingPosition.Clone();
75      while(true) {
76        x = Sample(x, potentialEnergyFunction, uniformRandom, stepSize, steps);
77        yield return x;
78      }
79    }
80
81    public static double[] Sample(
82      double[] startingPosition,
83      Func<double[], Tuple<double, double[]>> potentialEnergyFunction, // U
84      IRandom uniformRandom, double stepSize, int steps) {
85      // step 1
86      // new values for the momentum variables are randomly drawn from their Gaussian
87      // distribution, independently if the current values of the position variables
88
89      // step 2
90      // Metropolis update is performed, using Hamiltonian dynamics to propose a new state.
91      // Starting with the current state Hamiltonian dynamics is simulated for L steps
92      // using the Leapfrog method with a step size of eps.
93      // L and eps are parameters of the algorithm, which need to be tuned to obtain
94      // good performance.
95      // The proposed state is accepted as the next state of the Markov chain with
96      // probability [...].
97      // If the proposed state is not accepted the next state is the same as the current state
98      // (and is counted again when estimating the expectation of some function of
99      // state by its average over states of the Markov chain).
100
101
102      int nVars = startingPosition.Length;
103      var u_initial = potentialEnergyFunction(startingPosition).Item1;
104
105      // rename variables to match Neal's algorithm
106      var current_q = startingPosition;
107      var epsilon = stepSize;
108      var L = steps;
109
110      var q = current_q;
111      // p = rnorm(length(q), 0, 1) # independent standard normal variates
112      var p = q
113        .Select(_ => NormalDistributedRandom.NextDouble(uniformRandom, 0, 1))
114        .ToArray();
115      var current_p = p;
116      // Make a half step for momentum at the beginning
117      // p = p - epsilon * grad_U(q) / 2;
118      p = VectorSum(p, ScaleVector(-0.5 * epsilon, potentialEnergyFunction(q).Item2));
119      // Alternate full steps for position and momentum
120
121      for (int i = 1; i <= L; i++) {
122        // Make a full step for the position
123        // q = q + epsilon * p;
124        q = VectorSum(q, ScaleVector(epsilon, p));
125        // Make a full step for the momentum, except at end of trajectory
126        if (i != L)
127          p = VectorSum(p, ScaleVector(-epsilon, potentialEnergyFunction(q).Item2));
128      }
129      // Make a half step for momentum at the end.
130      ///   p = p - epsilon* grad_U(q) / 2
131      p = VectorSum(p, ScaleVector(-0.5 * epsilon, potentialEnergyFunction(q).Item2));
132      // Negate momentum at end of trajectory to make the proposal symmetric
133      ///   p = -p
134      p = ScaleVector(-1.0, p);
135      // Evaluate potential and kinetic energies at start and end of trajectory
136      double current_U = potentialEnergyFunction(current_q).Item1;
137      // sum(current_p ^ 2) / 2
138      double current_K = 0.5 * current_p.Sum(pi => pi * pi);
139      double proposed_U = potentialEnergyFunction(q).Item1;
140      // sum(p^2) / 2
141      double proposed_K = 0.5 * p.Sum(pi => pi * pi);
142      ///   # Accept or reject the state at end of trajectory, returning either
143      ///   # the position at the end of the trajectory or the initial position
144      if (uniformRandom.NextDouble() < Math.Exp(current_U - proposed_U + current_K - proposed_K)) {
145        return q; // accept
146      } else {
147        return current_q; // # reject
148      }
149    }
150
151    #region to be improved or removed
152    private static double VectorProd(double[] x, double[] y) {
153      double s = 0.0;
154      for (int i = 0; i < x.Length; i++)
155        s += x[i] * y[i];
156      return s;
157    }
158
159    private static double[] VectorSum(double[] x, double[] y) {
160      double[] s = new double[x.Length];
161      for (int i = 0; i < x.Length; i++)
162        s[i] += x[i] + y[i];
163      return s;
164    }
165
166    private static double[] ScaleVector(double a, double[] x) {
167      double[] s = new double[x.Length];
168      for (int i = 0; i < x.Length; i++)
169        s[i] = a * x[i];
170      return s;
171    }
172    #endregion
173  }
174}
Note: See TracBrowser for help on using the repository browser.