wolffd@0
|
1 function [samples, energies, diagn] = metrop(f, x, options, gradf, varargin)
|
wolffd@0
|
2 %METROP Markov Chain Monte Carlo sampling with Metropolis algorithm.
|
wolffd@0
|
3 %
|
wolffd@0
|
4 % Description
|
wolffd@0
|
5 % SAMPLES = METROP(F, X, OPTIONS) uses the Metropolis algorithm to
|
wolffd@0
|
6 % sample from the distribution P ~ EXP(-F), where F is the first
|
wolffd@0
|
7 % argument to METROP. The Markov chain starts at the point X and each
|
wolffd@0
|
8 % candidate state is picked from a Gaussian proposal distribution and
|
wolffd@0
|
9 % accepted or rejected according to the Metropolis criterion.
|
wolffd@0
|
10 %
|
wolffd@0
|
11 % SAMPLES = METROP(F, X, OPTIONS, [], P1, P2, ...) allows additional
|
wolffd@0
|
12 % arguments to be passed to F(). The fourth argument is ignored, but
|
wolffd@0
|
13 % is included for compatibility with HMC and the optimisers.
|
wolffd@0
|
14 %
|
wolffd@0
|
15 % [SAMPLES, ENERGIES, DIAGN] = METROP(F, X, OPTIONS) also returns a log
|
wolffd@0
|
16 % of the energy values (i.e. negative log probabilities) for the
|
wolffd@0
|
17 % samples in ENERGIES and DIAGN, a structure containing diagnostic
|
wolffd@0
|
18 % information (position and acceptance threshold) for each step of the
|
wolffd@0
|
19 % chain in DIAGN.POS and DIAGN.ACC respectively. All candidate states
|
wolffd@0
|
20 % (including rejected ones) are stored in DIAGN.POS.
|
wolffd@0
|
21 %
|
wolffd@0
|
22 % S = METROP('STATE') returns a state structure that contains the state
|
wolffd@0
|
23 % of the two random number generators RAND and RANDN. These are
|
wolffd@0
|
24 % contained in fields randstate, randnstate.
|
wolffd@0
|
25 %
|
wolffd@0
|
26 % METROP('STATE', S) resets the state to S. If S is an integer, then
|
wolffd@0
|
27 % it is passed to RAND and RANDN. If S is a structure returned by
|
wolffd@0
|
28 % METROP('STATE') then it resets the generator to exactly the same
|
wolffd@0
|
29 % state.
|
wolffd@0
|
30 %
|
wolffd@0
|
31 % The optional parameters in the OPTIONS vector have the following
|
wolffd@0
|
32 % interpretations.
|
wolffd@0
|
33 %
|
wolffd@0
|
34 % OPTIONS(1) is set to 1 to display the energy values and rejection
|
wolffd@0
|
35 % threshold at each step of the Markov chain. If the value is 2, then
|
wolffd@0
|
36 % the position vectors at each step are also displayed.
|
wolffd@0
|
37 %
|
wolffd@0
|
38 % OPTIONS(14) is the number of samples retained from the Markov chain;
|
wolffd@0
|
39 % default 100.
|
wolffd@0
|
40 %
|
wolffd@0
|
41 % OPTIONS(15) is the number of samples omitted from the start of the
|
wolffd@0
|
42 % chain; default 0.
|
wolffd@0
|
43 %
|
wolffd@0
|
44 % OPTIONS(18) is the variance of the proposal distribution; default 1.
|
wolffd@0
|
45 %
|
wolffd@0
|
46 % See also
|
wolffd@0
|
47 % HMC
|
wolffd@0
|
48 %
|
wolffd@0
|
49
|
wolffd@0
|
50 % Copyright (c) Ian T Nabney (1996-2001)
|
wolffd@0
|
51
|
wolffd@0
|
52 if nargin <= 2
|
wolffd@0
|
53 if ~strcmp(f, 'state')
|
wolffd@0
|
54 error('Unknown argument to metrop');
|
wolffd@0
|
55 end
|
wolffd@0
|
56 switch nargin
|
wolffd@0
|
57 case 1
|
wolffd@0
|
58 % Return state of sampler
|
wolffd@0
|
59 samples = get_state(f); % Function defined in this module
|
wolffd@0
|
60 return;
|
wolffd@0
|
61 case 2
|
wolffd@0
|
62 % Set the state of the sampler
|
wolffd@0
|
63 set_state(f, x); % Function defined in this module
|
wolffd@0
|
64 return;
|
wolffd@0
|
65 end
|
wolffd@0
|
66 end
|
wolffd@0
|
67
|
wolffd@0
|
68 if 0
|
wolffd@0
|
69 seed = 42;
|
wolffd@0
|
70 randn('state', seed);
|
wolffd@0
|
71 rand('state', seed)
|
wolffd@0
|
72 end
|
wolffd@0
|
73
|
wolffd@0
|
74 display = options(1);
|
wolffd@0
|
75 if options(14) > 0
|
wolffd@0
|
76 nsamples = options(14);
|
wolffd@0
|
77 else
|
wolffd@0
|
78 nsamples = 100;
|
wolffd@0
|
79 end
|
wolffd@0
|
80 if options(15) >= 0
|
wolffd@0
|
81 nomit = options(15);
|
wolffd@0
|
82 else
|
wolffd@0
|
83 nomit = 0;
|
wolffd@0
|
84 end
|
wolffd@0
|
85 if options(18) > 0.0
|
wolffd@0
|
86 std_dev = sqrt(options(18));
|
wolffd@0
|
87 else
|
wolffd@0
|
88 std_dev = 1.0; % default
|
wolffd@0
|
89 end
|
wolffd@0
|
90 nparams = length(x);
|
wolffd@0
|
91
|
wolffd@0
|
92 % Set up string for evaluating potential function.
|
wolffd@0
|
93 f = fcnchk(f, length(varargin));
|
wolffd@0
|
94
|
wolffd@0
|
95 samples = zeros(nsamples, nparams); % Matrix of returned samples.
|
wolffd@0
|
96 if nargout >= 2
|
wolffd@0
|
97 en_save = 1;
|
wolffd@0
|
98 energies = zeros(nsamples, 1);
|
wolffd@0
|
99 else
|
wolffd@0
|
100 en_save = 0;
|
wolffd@0
|
101 end
|
wolffd@0
|
102 if nargout >= 3
|
wolffd@0
|
103 diagnostics = 1;
|
wolffd@0
|
104 diagn_pos = zeros(nsamples, nparams);
|
wolffd@0
|
105 diagn_acc = zeros(nsamples, 1);
|
wolffd@0
|
106 else
|
wolffd@0
|
107 diagnostics = 0;
|
wolffd@0
|
108 end
|
wolffd@0
|
109
|
wolffd@0
|
110 % Main loop.
|
wolffd@0
|
111 n = - nomit + 1;
|
wolffd@0
|
112 Eold = feval(f, x, varargin{:}); % Evaluate starting energy.
|
wolffd@0
|
113 nreject = 0; % Initialise count of rejected states.
|
wolffd@0
|
114 while n <= nsamples
|
wolffd@0
|
115
|
wolffd@0
|
116 xold = x;
|
wolffd@0
|
117 % Sample a new point from the proposal distribution
|
wolffd@0
|
118 x = xold + randn(1, nparams)*std_dev;
|
wolffd@0
|
119 %fprintf('netlab propose: xold = %5.3f,%5.3f, xnew = %5.3f,%5.3f\n',...
|
wolffd@0
|
120 % xold(1), xold(2), x(1), x(2));
|
wolffd@0
|
121
|
wolffd@0
|
122 % Now apply Metropolis algorithm.
|
wolffd@0
|
123 Enew = feval(f, x, varargin{:}); % Evaluate new energy.
|
wolffd@0
|
124 a = exp(Eold - Enew); % Acceptance threshold.
|
wolffd@0
|
125 if (diagnostics & n > 0)
|
wolffd@0
|
126 diagn_pos(n,:) = x;
|
wolffd@0
|
127 diagn_acc(n,:) = a;
|
wolffd@0
|
128 end
|
wolffd@0
|
129 if (display > 1)
|
wolffd@0
|
130 fprintf(1, 'New position is\n');
|
wolffd@0
|
131 disp(x);
|
wolffd@0
|
132 end
|
wolffd@0
|
133
|
wolffd@0
|
134 r = rand(1);
|
wolffd@0
|
135 %fprintf('netlab: n=%d, a=%f/%f=%5.3f (%5.3f), r=%5.3f\n',...
|
wolffd@0
|
136 % n, exp(-Enew), exp(-Eold), a, exp(-Enew)/exp(-Eold), r);
|
wolffd@0
|
137 if a > r % Accept the new state.
|
wolffd@0
|
138 Eold = Enew;
|
wolffd@0
|
139 if (display > 0)
|
wolffd@0
|
140 fprintf(1, 'Finished step %4d Threshold: %g\n', n, a);
|
wolffd@0
|
141 end
|
wolffd@0
|
142 else % Reject the new state
|
wolffd@0
|
143 if n > 0
|
wolffd@0
|
144 nreject = nreject + 1;
|
wolffd@0
|
145 end
|
wolffd@0
|
146 x = xold; % Reset position
|
wolffd@0
|
147 if (display > 0)
|
wolffd@0
|
148 fprintf(1, ' Sample rejected %4d. Threshold: %g\n', n, a);
|
wolffd@0
|
149 end
|
wolffd@0
|
150 end
|
wolffd@0
|
151 if n > 0
|
wolffd@0
|
152 samples(n,:) = x; % Store sample.
|
wolffd@0
|
153 if en_save
|
wolffd@0
|
154 energies(n) = Eold; % Store energy.
|
wolffd@0
|
155 end
|
wolffd@0
|
156 end
|
wolffd@0
|
157 n = n + 1;
|
wolffd@0
|
158 end
|
wolffd@0
|
159
|
wolffd@0
|
160 if (display > 0)
|
wolffd@0
|
161 fprintf(1, '\nFraction of samples rejected: %g\n', ...
|
wolffd@0
|
162 nreject/(nsamples));
|
wolffd@0
|
163 end
|
wolffd@0
|
164
|
wolffd@0
|
165 if diagnostics
|
wolffd@0
|
166 diagn.pos = diagn_pos;
|
wolffd@0
|
167 diagn.acc = diagn_acc;
|
wolffd@0
|
168 end
|
wolffd@0
|
169
|
wolffd@0
|
170 % Return complete state of the sampler.
|
wolffd@0
|
171 function state = get_state(f)
|
wolffd@0
|
172
|
wolffd@0
|
173 state.randstate = rand('state');
|
wolffd@0
|
174 state.randnstate = randn('state');
|
wolffd@0
|
175 return
|
wolffd@0
|
176
|
wolffd@0
|
177 % Set state of sampler, either from full state, or with an integer
|
wolffd@0
|
178 function set_state(f, x)
|
wolffd@0
|
179
|
wolffd@0
|
180 if isnumeric(x)
|
wolffd@0
|
181 rand('state', x);
|
wolffd@0
|
182 randn('state', x);
|
wolffd@0
|
183 else
|
wolffd@0
|
184 if ~isstruct(x)
|
wolffd@0
|
185 error('Second argument to metrop must be number or state structure');
|
wolffd@0
|
186 end
|
wolffd@0
|
187 if (~isfield(x, 'randstate') | ~isfield(x, 'randnstate'))
|
wolffd@0
|
188 error('Second argument to metrop must contain correct fields')
|
wolffd@0
|
189 end
|
wolffd@0
|
190 rand('state', x.randstate);
|
wolffd@0
|
191 randn('state', x.randnstate);
|
wolffd@0
|
192 end
|
wolffd@0
|
193 return
|