function results = ols_gcbma(y,x,ndraw,prior)
% PURPOSE: MC^3 x-matrix specification for homoscedastic OLS model
% ------------------------------------------------------
% usage: results = ols_gcbma(y,x,ndraw,prior)
% where: % where: y = dependent variable vector (nobs x 1)
%        x = studentized or well-scaled x-matrix (you should include an
%        intercept)
%    ndraw = # of draws
%    prior = a structure variable with:
%            prior.g     = g-value for g-prior, default: max(1/n,1/k^2)
%            prior.nmodels = # of top models to save (default = 1000)
%            prior.nu    = informative Gamma(nu,d0) prior on sige
%            prior.d0    = default: nu=0,d0=0 (diffuse prior)
%-------------------------------------------------------------
% RETURNS:  a structure:
%          results.meth    = 'ols_gcbma'
%          results.munique = # of unique models found
%          results.nmodels = # of top models to save from input or (default = 1000)
%          results.freqs   = a matrix with frequency distribution of
%                            variables appearing in all unique models found
%          results.models  = a matrix with top nmodels information
%          results.allmodels = a matrix with info on all unique models found
%          results.mprob   = a vector of posterior model probabilities
%          results.vprob   = a vector with variable probabilities
%          results.nobs    = # of observations
%          results.nvar    = # of variables in x-matrix
%          results.ndraw   = # of draws
%          results.y       = y-vector from input (nobs x 1)
%          results.nu      = nu prior parameter
%          results.d0      = d0 prior parameter
%          results.time1   = time for BMA sampling
%          results.time2   = total time taken  
% --------------------------------------------------------------
% NOTES: 1) the model descriptions and log-marginals for all unique models found
%           are written  to a file lmarginal.ols
% --------------------------------------------------------------
% SEE ALSO: prt_bmao(), for printing results
%           model_averageo() function, that can be used to construct
%           estimates based on averaging over models using posterior
%           model probabilities as weights
% --------------------------------------------------------------
% REFERENCES: 
% Fernandez,Carmen, Eduardo Ley, and Mark F. J. Steel, (2001a)
%'Model uncertainty in cross-country growth regressions,'
% Journal of Applied Econometrics}, Volume 16, number 5, pp. 563 - 576.
%
% Fernandez,Carmen, Eduardo Ley, and Mark F. J. Steel, (2001b)
% 'Benchmark priors for Bayesian model averaging', Journal of Econometrics
% Volume 100, number 2, pp. 381-427.
%----------------------------------------------------------------

% written by:
% James P. LeSage, last updated 6/2004
% Dept of Economics
% University of Toledo
% 2801 W. Bancroft St,
% Toledo, OH 43606
% jlesage@spatial-econometrics.com

t1 = clock;

% error checking on inputs
[n junk] = size(y);
[n1 k] = size(x);
time1 = 0;
time2 = 0;

nobsa = n;
results.nobs  = n;
results.nvar  = k;
results.y = y;      

if n1 ~= n
error('ols_gcbma: x-matrix contains wrong # of observations');
end;

[nu,d0,sige,g,gmodels] = ols_parse(prior,x);

nmodels = gmodels;
% storage for draws
          nvar = k;
          mlog = [];
          msave = [];
          visits = zeros(ndraw,1);
          vsave = zeros(ndraw,nvar);

% ====== initializations
% compute this stuff once to save time
in = ones(n,1);

% start with a random selection of variables
vtmp=zeros(nvar,1);
for v=1:nvar
    vtmp(v)=bino_rnd(1,0.5,1,1);
end
vin=selif(seqa(1,1,nvar).*vtmp,vtmp)';
vout=delif(seqa(1,1,nvar)-seqa(1,1,nvar).*vtmp,vtmp)'; 

cnt = 0;

%<====================== start draws
t0 = clock;
hwait = waitbar(0,'OLS BMA : MCMC sampling ...');
for i=1:ndraw; 
    
% choose new model
[vinew vonew] = sample(vin,vout);
for j=1:length(vinew);
vsave(i,vinew(1,j)) = 1.0;
end;

[j visits] = find_new(i,vsave,vinew,visits);

if i == 1 % first draw, compute logpost for initial model
lmarg = bmapost(y,x,vinew,nu,d0,g);
mlog =  [mlog
         lmarg];
msave = [msave
         vsave(i,:)];
cnt = cnt+1; % count of unique models found
  lmarg_old = lmarg;
end; % end of if i == 1


if visits(i,1) ~= 0 % we found a new model
cnt = cnt+1; % increase counter for new models
lmarg_new = bmapost(y,x,vinew,nu,d0,g);
mlog = [mlog
        lmarg_new];
msave  = [msave
         vsave(i,:)];

else % we have a model that we have already computed the log-marginal for
     % so, we could look it up, but it is faster to just compute it again
  lmarg_new = bmapost(y,x,vinew,nu,d0,g);
end;

bf = model_odds(lmarg_new,lmarg_old);
 if bf >=1; 
     flag = 1; 
 else 
     flag = bino_rnd(1,bf,1,1); 
 end;

 if flag == 1 % change models
 vin = vinew;
 vout = vonew;
 lmarg_old = lmarg_new;
 end;
 
waitbar(i/ndraw);         
end; % end of sampling loop
close(hwait);

time1 = abs(etime(clock,t0));
results.time1 = time1;

results.munique = cnt;

% write out results for 1000 best models
out = [mlog msave];
clear mlog;
clear msave;
save lmarginal.ols out -ascii;

psum = 0.0;
posts = [];

load lmarginal.ols;

mlog = lmarginal(:,1);
models = lmarginal(:,2:end);
adj = max(mlog);
madj = mlog - adj;
  isum = exp(madj);
  postprob = isum/sum(isum);
  
[probs_sort,pind] = sort(postprob);
results.mprob = probs_sort;
msort = models(pind,:);
out = [msort probs_sort];

[no,junk] = size(out);

% return all unique models sampled
results.allmodels = out;
results.vprob = sum(out)/no;
results.freq = sum(out);

% just pull out nmodels requested by the user
if (nmodels > no) % the user wants more models output than were found
    nmodels = no; % we can only output what was found
else
    % the user is okay
end;
outn = out(no-nmodels+1:no,:);
results.models = msort(no-nmodels+1:no,:);

results.ndraw = ndraw;
results.nu = nu;
results.d0 = d0;

time2 = abs(etime(clock,t1));
results.time2 = time2;


function [nu,d0,sige,g,gmodels] = ols_parse(prior,x)
% PURPOSE: parses input arguments for ols_gcbma models
% ---------------------------------------------------
%  USAGE: [nu,d0,rho,sige,g,gmodels] = 
%                           ols_parse(prior,x)
% where prior contains the structure variable with inputs,
% x is the matrix of explanatory variables input
% and the outputs are either user-inputs or default values
% ---------------------------------------------------

% set defaults
[n,k] = size(x);
g1 = (k*k);
g2 = n;
g = 1/max([g1 g2]); % Fernandez, Ley Steel default g-value
gmodels = 1000;

sige = 1.0;
nu = 0;
d0 = 0;

fields = fieldnames(prior);
nf = length(fields);
if nf > 0
 for i=1:nf
    if strcmp(fields{i},'nu')
        nu = prior.nu;
    elseif strcmp(fields{i},'d0')
        d0 = prior.d0;  
    elseif strcmp(fields{i},'g')
       g = prior.g; 
    elseif strcmp(fields{i},'nmodels')
       gmodels = prior.nmodels; 
    end;
 end;
else, % the user has input a blank info structure
      % so we use the defaults
end; 


function [j,visits] = find_new(i,vsave,vinew,visits)
% PURPOSE: determines if the variables in vinew represent a new model
%          (called by bma_g)
%-------------------------------------------------------
% USAGE: [j,visits] = find_new(i,vsave,vinew,visits)
% where:        i   = size of vsave to search = draw #
%             vsave = (i x nvar) matrix of indicators for models
%             vinew = (1 x nvar) vector with current model indicators
%            visits = (i x nvar) matrix for recording # of visits to a model
%-------------------------------------------------------
% RETURNS:      j = index to an old model found
%          visits = matrix recording visits to old models
%-------------------------------------------------------

% written by:
% James P. LeSage, Dept of Economics 7/2003
% University of Toledo
% 2801 W. Bancroft St,
% Toledo, OH 43606
% jlesage@spatial-econometrics.com

[junk nvar] = size(vsave);
vitmp = zeros(1,nvar);
for k=1:nvar
    for j=1:length(vinew)
      if vinew(1,j) == k
        vitmp(1,k) = 1;
      end;
    end;
end;

nobs = i;
new = 0;
j = 1;
while (new == 0 & j <= nobs)
if vsave(j,:) == vitmp(1,:)
visits(j,1) = visits(j,1) + 1;
new = 1;
end;
j = j+1;
end;

j = j-1;

function lmout = bmapost(y,xall,vin,nu1,d1,g)
% PURPOSE: evaluates log marginal posterior of OLS bma model
%          (OLS Bayesian model averaging)
%-----------------------------------------------------------
% USAGE: lpost = bmapost(y,xall,vin,nu1,d1,g)
% where:   y = dependent variable vector (nobs x 1)
%       xall = explanatory variables matrix (nobs x k)
%        vin = a 1xk vector of columns to use from x, e.g. [1 3 5]
%        nu1 = prior parameter on sigma
%        d1  = prior parameter on sigma
%          g = g prior hyperparameter
%-----------------------------------------------------------
% RETURNS: lpost = log marginal posterior, a scalar
%                  containing the log marginal 
%-----------------------------------------------------------
% REFERENCES: Raftery and Madigan (1997) 'Bayesian model averaging
% for linear regression models', 92, pp. 179-191
% ----------------------------------------------------------

% written by:
% James P. LeSage, Dept of Economics 7/2003
% University of Toledo
% 2801 W. Bancroft St,
% Toledo, OH 43606
% jlesage@spatial-econometrics.com

[nobs,nv1] = size(xall);
nvar = length(vin);
v1 = nvar;
for i=1:nvar
    if vin(1,i) >= nv1
        v1 = i; % # of variables in x1
    end;
end;    

iota = ones(nobs,1);
xt = [iota xall(:,vin)];
[nobs,k] = size(xt);
nm1o2 = (nobs-1)/2;
no2 = nobs/2;

% now compute vectorized log-marginal likelihood
% to use in trapezoid numerical integration over rho
          xpx = xt'*xt;
          b0 = (xpx)\(xt'*y);
          e0 = y - xt*b0;
          epe0 = (1/(1+g))*(e0'*e0);
          ybar = mean(y);
          yhat = y - ybar*iota;
          Qterm = (g/(1+g))*(yhat'*yhat);
          logdetx = (nvar/2)*log(g/(1+g));
          den = logdetx - nm1o2*log(nu1*d1 + epe0 + Qterm);
          kterm = gammaln(nm1o2) -no2*log(pi);
          
lmout = kterm + den;


function odds = model_odds(lmarg1,lmarg2)
% PURPOSE: computes Bayes factor for 2 models using log-marginals as input
% ---------------------------------------------------
%  USAGE: probs = model_odds(logmarginal1,logmargina2)
%  where: logmarginal1,logmarginal2 = the log-marginal posterior for the
%  two models
% ---------------------------------------------------
%  RETURNS: a Bayes factor, a scalar
% ---------------------------------------------------

% written by:
% James P. LeSage, 6/2004
% Dept of Economics
% University of Toledo
% 2801 W. Bancroft St,
% Toledo, OH 43606
% jlesage@spatial-econometrics.com


lmarginal = [lmarg1 lmarg2];

% now scale log-marginals before exponentiating 
adj = max(lmarginal);
madj = lmarginal - adj;

xx = exp(madj);

% compute posterior probabilities
psum = sum(xx);
probs = xx/psum;
odds = (probs(1)+realmin)/(probs(2)+realmin);


function [vinew,vonew] = sample(vin,vout)
% PURPOSE: function used by sar BMA models to sample variables for changing model size
% ----------------------------------------------------------
% USAGE: [vinew vonew] = sample(vin,vout)
% where:   vin  = a 1 x nvar1 vector of variable #'s for
%                 variables included in the model
%          vout = a 1 x nvar2 vector of variable #'s for
%                 variables excluded from the model
% ----------------------------------------------------------
% RETURNS: vinew = a 1 x nvar1+1 or 1 x nvar1-1 vector of
%                  variable #'s in the new model
%          vonew = a 1 x nvar2+1 or 1 x nvar2-1 vector of
%                  variable #'s excluded from the new model
% ----------------------------------------------------------

% written by:
% James P. LeSage, Dept of Economics
% University of Toledo
% 2801 W. Bancroft St,
% Toledo, OH 43606
% jlesage@spatial-econometrics.com
% last modified June, 2004

% find size of variables in/out of the model
nv1 = length(vout); 
nv2 = length(vin);
nvar = nv1+nv2;

% decide on increase, move, decrease model size
coin = unif_rnd(1,0,1);
if coin < 0.33
    if nv1 == 0
        increase = 1;
    else
    increase = 0;
    end
elseif coin > 0.66
    if nv2 == 0
        increase = 0;
    else
    increase = 1;
    end
else
    increase = 2;
end;


switch increase
case {0} % decrease the # variables in the model
choose = round(unif_rnd(1,1,length(vout)));
vinew = [vin vout(choose)];
vonew = zeros(1,length(vout)-1);
cnt = 1;
for i=1:length(vout);
 if i~= choose
 vonew(cnt) = vout(i);
 cnt = cnt+1;
 end;
end; % end of for loop
case {1} % increase the # variables in the model
choose = round(unif_rnd(1,1,length(vin)));
vinew = zeros(1,length(vin)-1);
cnt = 1;
for i=1:length(vin);
 if i~= choose
 vinew(cnt) = vin(i);
 cnt = cnt+1;
 end;
end; % end of for loop
vonew = zeros(1,length(vout)+1);
cnt = 1;
for i=1:length(vout);
    vonew(cnt) = vout(cnt);
    cnt = cnt+1;
end;
vonew(cnt) = vin(choose);
case {2} % change a variable that is in with one that is out
    choose1 = round(unif_rnd(1,1,length(vout)));
    choose2 = round(unif_rnd(1,1,length(vin)));
vinew = vin;
vonew = vout;
for i=1:length(vout);
 if i == choose1
     for j=1:length(vin);
        if j == choose2
        vinew(j) = vout(i);
        vonew(i) = vin(j);
        end;
    end; % end of for j loop
 end; % end of if i == choose1
end; % end of for i loop


otherwise
disp('error in sample function');    
end; % end of switch   
    

