0

I am trying to create a function and a function handle of the said function where the function takes in output parameters from the previous call and new input parameters and calculates the gradient in the function as given in the toy example shown below. I would like to pass the function handle to hmcSampler. However, I am having a problem in creating the function handle and would like some help.

To clarify: I want to call logPosterior with a new value of theta, but also with the theta and logpdf output from the previous call. And I need to do this through a function handle that will be called multiple times by a function I don't control, so I need either logPosterior or its handle to manage storing the old values. In the first call, there should be different values of theta and old_theta so that the function can get going.

%% Toy implementation of hmcsampler class in Matlab
NumPredictors = 2;

trueIntercept = 2;
trueBeta = [3;0];
NumData = 100;
rng('default') %For reproducibility
X = rand(NumData,NumPredictors);
mu = X*trueBeta + trueIntercept;
y = mu;

% define the mean and variance of normal distribution of each parameter
means = [0; 0];
standevs = [1;1];


% create multivariate normal log probability distribution
[logpdf, grad_logpdf] = @(theta)logPosterior(theta, old_theta, X, y, means, standevs, old_logpdf); % <- How to write this?
% create the startpoint from which sampling starts
startpoint = randn(2, 1);

% create an HMC sampler object
smp = hmcSampler(logpdf, startpoint);

% estimate maximum of log probability density
[xhat, fitinfo] = estimateMAP(smp);

num_chains = 4;
chains = cell(num_chains, 1);
burnin = 50000;
num_samples = 2000000;


function [logpdf, grad_logpdf] = logPosterior(theta, old_theta, X, y, means, standevs, old_logpdf)
    
    % values
    intercept = theta(1);
    beta = theta(2:end);
    y_computed = X*beta + intercept; 
    log_likelihood = log(y_computed);
    del_loglikelihood = log_likelihood - old_logpdf;
    del_params = theta - old_theta;
    grad_params1 = del_loglikelihood/del_params;
    
    % compute log priors and gradients of parameters
    log_prior_params = 0;
    grad_params2 = [];
    for i = 1:3
        [lp, grad] = normalDistGrad(theta(i), means(i), standevs(i));
        log_prior_params = log_prior_params + lp;
        grad_params2 = [grad_params2; grad];
    end
    % return the log posterior and its gradient
    logpdf = log_likelihood + log_prior_params;
    grad_logpdf = grad_params1 + grad_params2;
end

function [lpdf,glpdf] = normalDistGrad(X, Mu, Sigma)
    Z = (X - Mu)./Sigma;
    lpdf = sum(-log(Sigma) - .5*log(2*pi) - .5*(Z.^2));
    glpdf = -Z./Sigma;
end
Cris Luengo
  • 55,762
  • 10
  • 62
  • 120
shunyo
  • 1,277
  • 15
  • 32
  • "I am having a problem in creating the function handle and would like some help." Could you please elaborate on this problem? Is it not working as intended? Is it giving an error message? What is `old_theta` in your example? I don't see it defined anywhere. Also, `normalDistGrad` takes only 3 arguments, you might have intended to call `logPosterior`? – Cris Luengo Sep 02 '21 at 15:32
  • So I think what you want to do is this: call `logPosterior` with a new value of `theta`, but also with the `theta` and `logpdf` output from the previous call. And you need to do this through a function handle that will be called multiple times by a function you don't control, so you need either the function itself, or the function handle, to manage storing the old values. Can you modify `logPosterior`? If so, that'd be the easiest solution. What should happen the first time you call the function? – Cris Luengo Sep 02 '21 at 15:40
  • Yes you are absolutely correct. Yes, `logPosterior` is written by me so it could be changed. Do I just store the parameters from the previous call in a global variable and then use it in `logPosterior`? In the first call, there should be different values of `theta` and `old_theta` so that the function can get going. – shunyo Sep 02 '21 at 18:31

2 Answers2

0

matlab help

first define your function in a separate m file which is in your current path :

function [o1,o2]=myfunc(in1,in2,in3)
    o1=in1+in2+in3;
    o2=in3-in2;
end

then create handle to the function :

f=@myfunc;
f(1,2,3)
ans= 6

to use just one of inputs:

f=@(x)myfunc(x,3,5);
f(1)
ans=9

to get both output:

[a,b]=f(1);

or define one output in your function and reference their index after calling them :

function o=myfunc(in1,in2,in3)
    o1=in1+in2+in3;
    o2=in3-in2;
    o=[o1,o2];
end

...

Hadi
  • 1,203
  • 1
  • 10
  • 20
  • I dont think that is the answer to the question that I asked. – shunyo Sep 02 '21 at 17:21
  • @shunyo maybe i didn't understand your problem well, I thought that you want to create a handle to a function and at the same time pass some other value (old_values) to the function. in this case, f=@(x)myfunc(x,a,b) is your answer, other than that if you want the old value to be persistent in the function (not passing explicitly to the function) you should use the solution that Cris provided – Hadi Sep 02 '21 at 19:29
0

I would implement logPosterior as follows for it to keep track of the values in the last function call. A persistent variable is local to the function, but persists between calls, making it an ideal tool to give the function memory.

function [logpdf, grad_logpdf] = logPosterior(theta, X, y, means, standevs)
    persistent old_theta old_logpdf
    if isempty(old_theta)
       % function hasn't been called before, initialize the old values:
       old_theta = zeros(size(theta));
       old_logpdf = 0; % adjust to be meaningful initial values
    end

    % ... (your original function code here)

    % save new values as old values for next call
    old_theta = theta;
    old_logpdf = logpdf;
end

You wold now take a function handle as follows:

func = @(theta)logPosterior(theta, X, y, means, standevs);

func is the handle you'd pass into whatever function will call it. You could initialize the previous variables by running your function once (I'm not sure what a suitable initialization is!):

func(startpoint);
smp = hmcSampler(func, startpoint);
Cris Luengo
  • 55,762
  • 10
  • 62
  • 120