function [P,confidence] = netpred(data, preproc, netarch, conf)
%
% Predict the output of the given dataset using a comitee of experts
% defined by netarch (obtained from findarch) and a preprocessing method
% preproc (also obtained from findarch, or from findpreproc).
% The conf structure is used to control confidence and some other params.
%
%
% Output:
%
%    P - The prediction of the architecture
%    confidence - a confidence value for each prediction, depends on the type
%                 of requested confidence.
%
% Input:
%    data    - data set 
%    preproc - a structure describing the preprocess methodology (obtained from findpreproc)
%    netarch - a structure defining the network architecture, the number of experts and
%              the experts themselvs (each expert is an ensembles of networks that uses a 
%              different architecture)
%    conf    - options structure with following parameters:
%      useweights - use weights to calculate ENSEMBLE prediction (the experts are NOT weighted)
%      confidence - one of the following options:
%             reconst - the lower the better, the input is reconstructed from the hidden
%                       units value and then compared to the real input (each network in
%                       each ensemble)
%             variance - the variance of the predictions for each input vector over ALL the
%                        networks (meaning number of experts * number of network in each ensemble)
%             value    - the predicted value (0..1), high value means clasification was probably 
%                        good (most networks agreed), and low values means that the clasification 
%                        wasn't so good
%

disp('Learning and neural computation project by - Igor Bogodlov and Ron Sidi');
disp('Function: netpred(data, preproc, netarch, conf)');

if nargin < 4
   conf = [];
end

if (~isfield(conf, 'useweights'))
   % Weights don't currently work well...
   conf.useweights = 0;
end

% make sure that a correct confidence is requested
if (~isfield(conf, 'conftype'))
   conf.conftype = 'reconst';
elseif ~strcmp(conf.conftype, 'reconst') & ~strcmp(conf.conftype, 'variance') & ~strcmp(conf.conftype, 'value')
   error(['Field conf.conftype must by either "reconst", "value" or "variance"']);
end

disp('Configuration:');

if (conf.useweights == 1)
   disp('   Weights are used to calculate prediction of each expert');
else
   disp('   Simple avarage is used to calculate prediction of each expert');
end

if (strcmp(conf.conftype, 'reconst'))
   disp('   Confidence by reconstruction');
elseif (strcmp(conf.conftype, 'value'))
   disp('   Confidence by value');
elseif (strcmp(conf.conftype, 'variance'))
   disp('   Confidence by variance');
end

% the number of experts
n = size(netarch.experts,2);
disp(['Number of experts in architecture: ', num2str(n)]);

% perform the preprocessing as defined
X = findpreproc(data, [], [], preproc);

% the predicted outputs of all the networks
Y = [];

% the confidence of all the predictions
C = [];

% for every expert, and every network in the ensemble predict the value
for j=1:n
   
   % get the current expert
   E = netarch.experts(j);
   
   % for each network in the ensemble
   for i=1:E.s
      
      % calculate the prediction
      H1 = rbf_dm(X', E.ens(i).C1, E.ens(i).R1, E.ens(i).dmc1);
      H2 = rbf_dm(X', E.ens(i).C2, E.ens(i).R2, E.ens(i).dmc2);
      
      % and add it to the predicted matrix
      if conf.useweights
         % use weights if requested
         Y = [Y, (H1*E.ens(i).w1 - H2*E.ens(i).w2) .* E.W(i)];
      else
         Y = [Y, (H1*E.ens(i).w1 - H2*E.ens(i).w2)];
      end
      
      if strcmp(conf.conftype, 'reconst')
         % Confidence by reconstruction
         rec = H1 * E.ens(i).W;
         if conf.useweights
            C = [C, mean((E.W(i) .* (X - rec)).^2,2)];
         else
            C = [C, mean((X - rec).^2,2)];
         end
      end
   end
end

% the avarage of all the networks prediction is the final prediction of the
% architecture
mu = mean(Y,2);

% the output is a boolean value
P = mu > 0;

if strcmp(conf.conftype, 'value')
   % value confidence simply returns the avarage prediction value
   confidence = abs(mu);
elseif strcmp(conf.conftype, 'variance')
   % simply calculate the variance across all the predictions
   C = Y - mu(:,ones(1,size(Y,2)));
   confidence = 1 - mean(C .^ 2, 2);
else
   % reconstruction, if the distance of the reconstructed data (in C) is small
   % then the confidence is high
   confidence = 1 - sqrt(mean(C,2));
end
