function [net, options, varargout] = mixopt(net, options, x, t, post, err, grad, alg, varargin);
%mixopt	Optimize the weights for an expert or gating network in the M-step of EM for a mixture of experts (or gated experts) model
%
%	MIXOPT is a helper function which facilitates the training of
%	the components (expert or gating) of a mixture of experts model  
%       trained by EM using general purpose optimizers. 
%       It can be used with any function that searches in parameter space 
%       using error and gradient functions.
%
%	[NET, OPTIONS] = MIXOPT(NET, OPTIONS, X, T, POST, ERR, GRAD, ALG) 
%       takes a network data structure NET (either one of the expert
%       networks or the gating network), together with a vector OPTIONS of 
%       parameters governing the behaviour of the optimization algorithm, a 
%       matrix X of input vectors, a matrix T of target vectors, and the
%       posterior probabilities POST.
%
%       It returns the trained network as well as an updated OPTIONS vector. 
%       The string ALG determines which optimization algorithm (CONJGRAD, 
%       QUASINEW, SCG, etc.)  will be used. The strings ERR and GRAD
%       specify the error and gradient functions to be used:
%             - 'mixerr_exp' and 'mixgrad_exp' for a GLM expert network NET
%             - 'mixmlperr_exp' and 'mixmlpgrad_exp' for a MLP expert network NET
%             - 'mixerr_gate' and 'mixgrad_gate' for a GLM gating network NET
%             - 'mixmlperr_gate' and 'mixmlp_gate' for a MLP gating network NET
%
%       [NET, OPTIONS] = MIXOPT(NET, OPTIONS, X, T, POST, ERR, GRAD, ALG, SIGMA) if the 
%       conditional  density for the experts is an isotropic Gaussian (with 
%       one variance parameter for each expert), SIGMA contains these variances. 
%
%	[NET, OPTIONS, VARARGOUT] = MIXOPT(NET, OPTIONS, X, T, POST, ERR,GRAD, ALG, VARARGIN) 
%       also returns any additional return values from the optimisation algorithm.
%
%	See also
%	mixerr_em, mixgrad_em, mixerr_gate, mixerr_exp, mixgrad_gate, mixgrad_exp, mix, quasinew, scg, graddesc
%
%       Copyright: Perry MOerland

pakstr = [net.type, 'pak'];
unpakstr = [net.type, 'unpak'];
optstring = [alg, '(''mixerr_em'', w, options, ''mixgrad_em'', net, x, t, post, err, grad, varargin{:} )'];

% Extract weights from network as single vector
w = feval(pakstr, net);

% Carry out optimisation
[s{1:nargout}] = eval(optstring);
w = s{1};

if nargout > 1
  options = s{2};

  % If there are additional arguments, extract them
  nextra = nargout - 2;
  if nextra > 0
    for i = 1:nextra
      varargout{i} = s{i+2};
    end
  end
end

% Pack the weights back into the network
net = feval(unpakstr, net, w);
