function [bnet, LL] = learn_params(bnet, vals, obs_nodes, max_iter, thresh, verbose, ...
    soft_vals, soft_obs_nodes)
% LEARN_PARAMS Set the parameters of each adjustable node to their ML/MAP values using batch EM.
% 
% [BNET, LL] = LEARN_PARAMS(BNET, VALS) uses the data in VALS. If BNET is a static BN,
% each row of VALS is a different training case. If BNET is a DBN, VALS(:,t,l) is the value of
% the nodes in slice t of example l. If each sequence is not the same length, VALS should be a
% cell array. VALS{l} is then a matrix, whose t'th column contains the values for slice t.
% LL is the "learning curve": a vector of the log lik. values at each iteration.
%
% [BNET, LL] = LEARN_PARAMS(BNET, VALS, OBS_NODES) specifies what nodes are observed.
% (The default is to use the observed nodes that were specified when BNET was created.)
% If OBS_NODES is a vector, the same nodes are assumed to be observed in every case (and, for
% DBNs, in every slice). If this is not the case, VALS and OBS_NODES need to be cell arrays.
% OBS_NODES{l} specifies what nodes are observed in case l, and VALS{l} contains their values.
% If BNET is a static BN, OBS_NODES{l} and VALS{l} should be vectors.
% If BNET is a DBN, OBS_NODES{l} should be a vector or cell array, and
% VALS{l} should be a matrix or cell array, depending on whether the nodes observed in each
% timeslice are the same or not: see enter_evidence for details.
%
% [BNET, LL] = LEARN_PARAMS(BNET, VALS, OBS_NODES, MAX_ITER) ensures that EM takes at most
% MAX_ITER iterations (default 10).
%
% [BNET, LL] = LEARN_PARAMS(BNET, VALS, OBS_NODES, MAX_ITER, THRESH) specifies the threshold for
% stopping EM (default 1e-4). We stop when |f(t) - f(t-1)| / avg < threshold,
% where avg = (|f(t)| + |f(t-1)|)/2 and f is log lik (default 1e-4).
%
% [BNET, LL] = LEARN_PARAMS(BNET, VALS, OBS_NODES, MAX_ITER, THRESH, VERBOSE) specifies whether
% to display the log lik. at each iteration. (Default: VERBOSE = 1.)
%
% [BNET, LL] = LEARN_PARAMS(BNET, VALS, OBS_NODES, MAX_ITER, THRESH, VERBOSE, SOFT_VALS, SOFT_NODES)
% provides soft evidence. SOFT_VALS{l} should be a cell array contain the soft evidence for case l
% (see enter_evidence for details). If SOFT_NODES is a vector, it is assumed that the same
% nodes are soft-observed in each case; otherwise, this should be a cell array.

if ~exist('max_iter'), max_iter = 10; end
if ~exist('thresh'), thresh = 1e-4; end
if ~exist('verbose'), verbose = 1; end
if ~exist('soft_vals'), soft_vals = []; end
if ~exist('soft_obs_nodes'), soft_obs_nodes = []; end

if ~iscell(vals)
  if bnet.is_dbn
    vals = num2cell(vals, [1 2]); % each elt of the 3rd dim gets its own cell
  else
    vals = num2cell(vals, 2); % each row gets its own cell
  end
end
num_examples = max(length(vals), length(soft_vals));

if ~exist('obs_nodes')
  if bnet.is_dbn
    obs_nodes = bnet.observed_slice;
  else
    obs_nodes = bnet.observed_nodes;
  end
end

if ~iscell(obs_nodes)
  % Use the same pattern of evidence in each example
  obs_nodes = num2cell(repmat(obs_nodes(:)', [num_examples 1]),  2);  % each row gets its own cell
end



if ~iscell(soft_obs_nodes)
  % Use the same pattern of evidence in each example
  soft_obs_nodes = num2cell(repmat(soft_obs_nodes(:)', [num_examples 1]),  2);  % each row gets its own cell
end


loglik = 0;
previous_loglik = -inf;
converged = 0;
num_iter = 1;
LL = [];
  
while ~converged & (num_iter <= max_iter)
  [bnet.bnodes, loglik] = EM_step(bnet, bnet.bnodes, vals, obs_nodes, soft_vals, soft_obs_nodes);
  bnet = compile_params(bnet);
  if verbose, fprintf(1, 'EM iteration %d, ll = %8.4f\n', num_iter, loglik); end
  num_iter = num_iter + 1;
  converged = em_converged(loglik, previous_loglik, thresh);
  previous_loglik = loglik;
  LL = [LL loglik];
end


    
% Many relevant variations of EM are known, but have not yet been implemented. See e.g.,
%
% Speedup tricks
% - Jamshidian and Jennrich, "Conjugate gradient acceletation of the EM algorithm",
%      JASA 88(421):221-228, 1993
% - Thiesson, "Accelerated quantitifcation of Bayesian networks with incomplete data",
%      Proc. of the Conf. on Knowledge Discovery and Data Mining, 1995.
%
% Online / speedup
% - Bauer, Koller and Singer. "Batch and on-line parameter estimation in Bayesian networks", UAI 97.
% - Neal and Hinton, "A view of the EM algorith, that justifies incremental, sparse
%     and other variants", in "Learning in Graphical Models", ed. M. Jordan, 1998.
%
% Online/ Bayesian sequential learning
% - Spiegelhalter and Lauritzen, "Sequential updating of conditional probabilities on directed
%    graphical structures", Networks, 20:579--605, 1990.
%
% Deterministic annealing
% - Ueda and Nakano, "Deterministic annealing EM algorithm", Neural Networks, 11:271-282, 1998.
%
% It is also possible to use gradient descent techniques. See e.g.,
% - Binder, Koller, Russell and Kanazawa. "Adaptive probabilistic networks with hidden
%     variables", Machine Learning, 1997.

