function [model, metProduction, essentialRxnsForTasks, addedRxnsForTasks, deletedDeadEndRxns, deletedRxnsInINIT, taskReport]=getINITModel(refModel, tissue, celltype, hpaData, arrayData, metabolomicsData, taskFile, useScoresForTasks, printReport, taskStructure, params, paramsFT)
% getINITModel_legacy
%   Generates a model using the INIT algorithm, based on proteomics and/or
%   transcriptomics and/or metabolomics and/or metabolic tasks. This is the original
%   implementation of tINIT, which is replaced by ftINIT.
%
%   Input:
%   refModel            a model structure. The model should be in the
%                       closed form (no exchange reactions open). Import
%                       using import(filename,false). If the model is not
%                       loaded using importModel, it might be that there
%                       is no "unconstrained" field. In that case,
%                       manually add the field like:
%                       model.unconstrained=false(numel(model.mets),1);
%   tissue              tissue to score for. Should exist in either
%                       hpaData.tissues or arrayData.tissues
%   celltype            cell type to score for. Should exist in either
%                       hpaData.celltypes or arrayData.celltypes for this
%                       tissue (opt, default is to use the best values
%                       among all the cell types for the tissue. Use [] if
%                       you want to supply more arguments)
%   hpaData             HPA data structure from parseHPA (opt if arrayData is
%                       supplied, default [])
%   arrayData           gene expression data structure (opt if hpaData is
%                       supplied, default [])
%       genes           cell array with the unique gene names
%       tissues         cell array with the tissue names. The list may not be
%                       unique, as there can be multiple cell types per tissue
%       celltypes       cell array with the cell type names for each tissue
%       levels          GENESxTISSUES array with the expression level for
%                       each gene in each tissue/celltype. NaN should be
%                       used when no measurement was performed
%       threshold       a single value or a vector of gene expression
%                       thresholds, above which genes are considered to be
%                       "expressed". (opt, by default, the mean expression
%                       levels of each gene across all tissues in arrayData
%                       will be used as the threshold values)
%       singleCells     binary value selecting whether to use the
%                       single-cell algorithm to identify expressed genes.
%                       If used, specify cell subpopulations in CELLTYPES
%                       (opt, default [])
%       plotResults     true if single cell probability distributions
%                       should be plotted (opt, default = False)
%   metabolomicsData    cell array with metabolite names that the model
%                       should produce (opt, default [])
%   taskFile            a task list in Excel format. See parseTaskList for
%                       details (opt, default [])
%   useScoresForTasks   true if the calculated reaction scored should be used as
%                       weights in the fitting to tasks (opt, default true)
%   printReport         true if a report should be printed to the screen
%                       (opt, default true)
%   taskStructure       task structure as from parseTaskList. Can be used
%                       as an alternative way to define tasks when Excel
%                       sheets are not suitable. Overrides taskFile (opt,
%                       default [])
%   params              parameter structure as used by getMILPParams. This is
%                       for the INIT algorithm. For the the MILP problems
%                       solved to fit tasks, see paramsFT (opt, default [])
%   paramsFT            parameter structure as used by getMILPParams. This is
%                       for the fitTasks step. For the INIT algorithm, see
%                       params (opt, default [])
%
%
%   Output:
%   model                   the resulting model structure
%   metProduction           array that indicates which of the
%                           metabolites in metabolomicsData that could be
%                           produced. Note that this is before the
%                           gap-filling process to enable defined tasks. To
%                           see which metabolites that can be produced in
%                           the final model, use canProduce.
%                           -2: metabolite name not found in model
%                           -1: metabolite found, but it could not be produced
%                           1: metabolite could be produced
%   essentialRxnsForTasks   cell array of the reactions which were
%                           essential to perform the tasks
%   addedRxnsForTasks       cell array of the reactions which were added in
%                           order to perform the tasks
%   deletedDeadEndRxns      cell array of reactions deleted because they
%                           could not carry flux (INIT requires a
%                           functional input model)
%   deletedRxnsInINIT       cell array of the reactions which were deleted by
%                           the INIT algorithm
%   taskReport              structure with the results for each task
%   	id                  cell array with the id of the task
%       description         cell array with the description of the task
%       ok                  boolean array with true if the task was successful
%       essential           cell array with cell arrays of essential
%                           reactions for the task
%       gapfill             cell array of cell arrays of reactions included
%                           in the gap-filling for the task
%
%   This is the main function for automatic reconstruction of models based
%   on the (t)INIT algorithm (PLoS Comput Biol. 2012;8(5):e1002518,
%   Mol Syst Biol. 2014;10:721). Not all settings are possible using this
%   function, and you may want to call the functions scoreModel, runINIT
%   and fitTasks individually instead.
%
%   NOTE: Exchange metabolites should normally not be removed from the model
%   when using this approach, since checkTasks/fitTasks rely on putting specific
%   constraints for each task. The INIT algorithm will remove exchange metabolites
%   if any are present. Use importModel(file,false) to import a model with
%   exchange metabolites remaining.
%
%   Usage: [model, metProduction, essentialRxnsForTasks, addedRxnsForTasks,...
%               deletedDeadEndRxns, deletedRxnsInINIT, taskReport]=...
%               getINITModel(refModel, tissue, celltype, hpaData, arrayData,...
%               metabolomicsData, taskFile, useScoresForTasks, printReport,...
%               taskStructure, params, paramsFT)

if nargin<3
    celltype=[];
else
    celltype=char(celltype);
end
if nargin<4
    hpaData=[];
end
if nargin<5
    arrayData=[];
end
if nargin<6
    metabolomicsData=[];
end
if nargin<7
    taskFile=[];
else
    taskFile=char(taskFile);
end
if nargin<8 || isempty(useScoresForTasks)
    useScoresForTasks=true;
end
if nargin<9 || isempty(printReport)
    printReport=true;
end
if nargin<10
    taskStructure=[];
end
if nargin<11
    params=[];
end
if nargin<12
    paramsFT=[];
end

%Check that the model is in the closed form
if ~isfield(refModel,'unconstrained')
    EM='Exchange metabolites should normally not be removed from the model when using getINITModel. Use importModel(file,false) to import a model with exchange metabolites remaining (see the documentation for details)';
    dispEM(EM);
end

%Create the task structure if not supplied
if any(taskFile) && isempty(taskStructure)
    taskStructure=parseTaskList(taskFile);
end

% sc-tINIT to identify confidence levels of gene expression
if ~isempty(arrayData) && isfield(arrayData,'singleCells')
    if arrayData.singleCells == 1
        % Check to ensure cell type is defined
        if ~isfield(arrayData,'celltypes')
            dispEM('arrayData must contain cell type information if sc-tINIT is to be used','false');
        end
        if ~ismember(upper(celltype),upper(arrayData.celltypes))
            dispEM('The cell type name does not match');
        end

        % Analyze only cell type of interest
        J= strcmpi(arrayData.celltypes,celltype);

        % Analyze only genes included in the reference model
        I=ismember(arrayData.genes,refModel.genes);

        % Convert expression data to population fractions
        binary_levels = arrayData.levels(I,J)~=0; % Classify each gene as detected (1) or not (0) in each cell
        cell_count_levels = sum(binary_levels,2); % Number of cells expressing each transcript
        cell_frac_levels = cell_count_levels/size(binary_levels,2); % Number of cells expressing each transcript

        % Bin cell_frac_counts manually
        x = 0:.01:1;
        for(i = 1:length(x))
            cell_frac_count(i) = sum(round(cell_frac_levels,2)==x(i));
        end

        % Fit four beta distributions
        cell_frac_count(cell_frac_count==0) = NaN; % Remove zeros from optimization
        cell_frac_count(1) = NaN; % Remove non-expressed genes from optimization
        x_lim = 1; % Somewhat arbitrary parameter to fit left tail of distr.
        myfun = @(par) nansum((cell_frac_count(1:find(x>=x_lim,1)) - ...
            abs(par(1))*betapdf(x(1:find(x>=x_lim,1)),abs(par(2)),abs(par(3))) - ...
            abs(par(4))*betapdf(x(1:find(x>=x_lim,1)),abs(par(5)),abs(par(6))) - ...
            abs(par(7))*betapdf(x(1:find(x>=x_lim,1)),abs(par(8)),abs(par(9))) - ...
            abs(par(10))*betapdf(x(1:find(x>=x_lim,1)),abs(par(11)),abs(par(12)))).^2);

        par0 = [4,2,100,7,2,30,7,5,20,5,15,20];
        opts = optimset('Display','off');
        [par,f_val] = fminsearch(myfun,par0,opts);
        par = abs(par);

        % Plot results
        if (isfield(arrayData,'plotResults'))
            if arrayData.plotResults == true
                figure(); hold on; plot(x,cell_frac_count,'ko','MarkerSize',5);
                plot(x,abs(par(1))*betapdf(x,abs(par(2)),abs(par(3))),'b-','LineWidth',1)
                plot(x,abs(par(4))*betapdf(x,abs(par(5)),abs(par(6))),'b-','LineWidth',1)
                plot(x,abs(par(7))*betapdf(x,abs(par(8)),abs(par(9))),'b-','LineWidth',1)
                plot(x,abs(par(10))*betapdf(x,abs(par(11)),abs(par(12))),'b-','LineWidth',1)
                plot(x,abs(par(1))*betapdf(x,abs(par(2)),abs(par(3))) + ...
                    abs(par(4))*betapdf(x,abs(par(5)),abs(par(6))) + ...
                    abs(par(7))*betapdf(x,abs(par(8)),abs(par(9))) + ...
                    abs(par(10))*betapdf(x,abs(par(11)),abs(par(12))),'-','Color',[.5 .5 .5],'LineWidth',2)
                xlabel('Expression Probability');ylabel('# of genes');set(gca,'FontSize',14,'LineWidth',1.25);
                title('Expression prediction','FontSize',18,'FontWeight','bold')
            end
        end

        % Score genes based on population expression (p = .05)
        exprs_cutoff_1 = find(cdf('beta',x,par(2),par(3)) >.95,1)-1; % Find index of no confidence genes
        exprs_cutoff_2 = find(cdf('beta',x,par(5),par(6)) >.95,1)-1; % Find index of low confidence genes
        exprs_cutoff_3 = find(cdf('beta',x,par(8),par(9)) >.95,1)-1; % Find index of low confidence genes
        exprs_cutoffs = sort([exprs_cutoff_1,exprs_cutoff_2,exprs_cutoff_3]);
        gene_scores = cell_frac_levels*0;
        gene_scores(cell_frac_levels <= x(exprs_cutoffs(1))) = 4; % Not detected
        gene_scores(logical((cell_frac_levels >= x(exprs_cutoffs(1))).*(cell_frac_levels < x(exprs_cutoffs(2))))) = 3; % Low detection
        gene_scores(logical((cell_frac_levels >= x(exprs_cutoffs(2))).*(cell_frac_levels < x(exprs_cutoffs(3))))) = 2; % Medium detection
        gene_scores(cell_frac_levels > x(exprs_cutoffs(3))) = 1; % High detection

        % Replace hpaData with singleCellData
        if printReport==true
            dispEM('Single cell data is not currently compatible with HPA data. \n         Replacing hpaData with single cell-based scoring.',false);
        end
        hpaData.genes = arrayData.genes;
        hpaData.tissues = arrayData.tissues;
        hpaData.celltypes = arrayData.celltypes;
        hpaData.levels = [{'High'},{'Medium'},{'Low'},{'None'}];
        hpaData.gene2Level = zeros(length(arrayData.genes),length(arrayData.celltypes));
        for i = 1:length(find(J))
            find_var = find(J,i);
            hpaData.gene2Level(I,find_var(end)) = gene_scores;
        end

        % Remove arrayData from the analysis (Might be a bad idea)
        clear arrayData
        arrayData=[];
    end
end

if printReport==true
    if any(celltype)
        fprintf(['***Generating model for: ' tissue ' - ' celltype '\n']);
    else
        fprintf(['***Generating model for: ' tissue '\n']);
    end
    if ~isempty(hpaData)
        fprintf('-Using HPA data\n');
    end
    if ~isempty(arrayData)
        fprintf('-Using array data\n');
    end
    if ~isempty(metabolomicsData)
        fprintf('-Using metabolomics data\n');
    end
    if ~isempty(taskFile) || ~isempty(taskStructure)
        fprintf('-Using metabolic tasks\n');
    end
    fprintf('\n');

    printScores(refModel,'Reference model statistics',hpaData,arrayData,tissue,celltype);
end

%Remove dead-end reactions to speed up the optimization and to
%differentiate between reactions removed by INIT and those that are
%dead-end
[~, deletedDeadEndRxns]=simplifyModel(refModel,true,false,true,true,true);
cModel=removeReactions(refModel,deletedDeadEndRxns,false,true);

%Store the connected model like this to keep track of stuff
if printReport==true
    printScores(cModel,'Pruned model statistics',hpaData,arrayData,tissue,celltype);
end

%If tasks have been defined, then go through them and get essential
%reactions
if ~isempty(taskStructure)
    [taskReport, essentialRxnMat]=checkTasks(cModel,[],printReport,true,true,taskStructure);

    essentialRxnsForTasks=cModel.rxns(any(essentialRxnMat,2));

    %Remove tasks that cannot be performed
    taskStructure(taskReport.ok==false)=[];
    if printReport==true
        printScores(removeReactions(cModel,setdiff(cModel.rxns,essentialRxnsForTasks),true,true),'Reactions essential for tasks',hpaData,arrayData,tissue,celltype);
    end
else
    essentialRxnsForTasks={};
end

%Score the connected model
[rxnScores, geneScores]=scoreModel(cModel,hpaData,arrayData,tissue,celltype);

%Run the INIT algorithm. The exchange reactions that are used in the final
%reactions will be open, which doesn't fit with the last step. Therefore
%delete reactions from the original model instead of taking the output. The
%default implementation does not constrain reversible reactions to only
%carry flux in one direction. Runs without the constraints on reversibility
%and with all output allowed. This is to reduce the complexity of the
%problem.
[~, deletedRxnsInINIT, metProduction]=runINIT(simplifyModel(cModel),rxnScores,metabolomicsData,essentialRxnsForTasks,0,true,false,params);
initModel=removeReactions(cModel,deletedRxnsInINIT,true,true);
if printReport==true
    printScores(initModel,'INIT model statistics',hpaData,arrayData,tissue,celltype);
    printScores(removeReactions(cModel,setdiff(cModel.rxns,deletedRxnsInINIT),true,true),'Reactions deleted by INIT',hpaData,arrayData,tissue,celltype);
end

%The full model has exchange reactions in it. fitTasks calls on fillGaps,
%which automatically removes exchange metabolites (because it assumes that
%the reactions are constrained when appropriate). In this case the
%uptakes/outputs are retrieved from the task sheet instead. To prevent
%exchange reactions being used to fill gaps, they are delete from the
%reference model here.
initModel.id='INITModel';

%If gaps in the model should be filled using a task list
if ~isempty(taskStructure)
    %Remove exchange reactions and reactions already included in the INIT
    %model
    refModelNoExc=removeReactions(refModel,union(initModel.rxns,getExchangeRxns(refModel)),true,true);

    %At this stage the model is fully connected and most of the genes with
    %good scores should have been included. The final gap-filling should
    %take the scores of the genes into account, so that "rather bad"
    %reactions are preferred to "very bad" reactions. However, reactions
    %with positive scores will be included even if they are not connected
    %in the current formulation. Therefore, such reactions will have to be
    %assigned a small negative score instead.
    if useScoresForTasks==true
        refRxnScores=scoreModel(refModelNoExc,hpaData,arrayData,tissue,celltype);
        [outModel, addedRxnMat]=fitTasks(initModel,refModelNoExc,[],true,min(refRxnScores,-0.1),taskStructure,paramsFT);
    else
        [outModel, addedRxnMat]=fitTasks(initModel,refModelNoExc,[],true,[],taskStructure,paramsFT);
    end
    if printReport==true
        printScores(outModel,'Functional model statistics',hpaData,arrayData,tissue,celltype);
        printScores(removeReactions(outModel,intersect(outModel.rxns,initModel.rxns),true,true),'Reactions added to perform the tasks',hpaData,arrayData,tissue,celltype);
    end

    addedRxnsForTasks=refModelNoExc.rxns(any(addedRxnMat,2));
else
    outModel=initModel;
    addedRxnMat=[];
    addedRxnsForTasks={};
end

%The model can now perform all the tasks defined in the task list. The
%algorithm cannot deal with gene-complexes at the moment. It is therefore
%ok to remove bad genes from a reaction (as long as at least one gene is
%kept)
model=outModel;

[~, I]=ismember(model.genes,cModel.genes); %All should be found
%This is a little weird way to make sure that only one bad gene is included
%if there are no good ones (since all -Inf==max(-Inf))
geneScores(isinf(geneScores))=-1000+rand(sum(isinf(geneScores)),1);

model.grRules(:)={''};
for i=1:numel(model.rxns)
    ids=find(model.rxnGeneMat(i,:));
    if numel(ids)>1
        scores=geneScores(I(ids));
        %Only keep the positive ones if possible
        model.rxnGeneMat(i,ids(~(scores>0 | scores==max(scores))))=0;
    end
    %Rewrite the grRules to be only OR
    if isfield(model,'grRules')
        J=find(model.rxnGeneMat(i,:));
        for j=1:numel(J)
            model.grRules{i}=[model.grRules{i} '(' model.genes{J(j)} ')'];
            if j<numel(J)
                model.grRules{i}=[model.grRules{i} ' or '];
            end
        end
    end
end

%Find all genes that are not used and delete them
I=sum(model.rxnGeneMat)==0;
model.genes(I)=[];
model.rxnGeneMat(:,I)=[];
if isfield(model,'geneShortNames')
    model.geneShortNames(I)=[];
end
if isfield(model,'geneMiriams')
    model.geneMiriams(I)=[];
end
if isfield(model,'geneFrom')
    model.geneFrom(I)=[];
end
if isfield(model,'geneComps')
    model.geneComps(I)=[];
end

%At this stage the model will contain some exchange reactions but probably
%not all (and maybe zero). This can be inconvenient, so all exchange
%reactions from the reference model are added, except for those which
%involve metabolites that are not in the model.

%First delete and included exchange reactions in order to prevent the order
%from changing
model=removeReactions(model,getExchangeRxns(model));

%Create a model with only the exchange reactions in refModel
excModel=removeReactions(refModel,setdiff(refModel.rxns,getExchangeRxns(refModel)),true,true);

%Find the metabolites there which are not exchange metabolites and which do
%not exist in the output model
I=~ismember(excModel.mets,model.mets) & excModel.unconstrained==0;

%Then find those reactions and delete them
[~, J]=find(excModel.S(I,:));
excModel=removeReactions(excModel,J,true,true);

%Merge with the output model
model=mergeModels({model;excModel},'metNames');
model.id='INITModel';
model.name=['Automatically generated model for ' tissue];
if any(celltype)
    model.name=[model.name ' - ' celltype];
end

if printReport==true
    printScores(model,'Final model statistics',hpaData,arrayData,tissue,celltype);
end

%Add information about essential reactions and reactions included for
%gap-filling and return a taskReport
if ~isempty(taskStructure)
    I=find(taskReport.ok); %Ignore failed tasks
    for i=1:numel(I)
        taskReport.essential{I(i),1}=cModel.rxns(essentialRxnMat(:,I(i)));
        taskReport.gapfill{I(i),1}=refModelNoExc.rxns(addedRxnMat(:,i));
    end
else
    taskReport=[];
end

%Fix grRules and reconstruct rxnGeneMat
[grRules,rxnGeneMat] = standardizeGrRules(model,true);
model.grRules = grRules;
model.rxnGeneMat = rxnGeneMat;
end

%This is for printing a summary of a model
function [rxnS, geneS]=printScores(model,name,hpaData,arrayData,tissue,celltype)
[a, b]=scoreModel(model,hpaData,arrayData,tissue,celltype);
rxnS=mean(a);
geneS=mean(b(~isinf(b)));
fprintf([name ':\n']);
fprintf(['\t' num2str(numel(model.rxns)) ' reactions, ' num2str(numel(model.genes)) ' genes\n']);
fprintf(['\tMean reaction score: ' num2str(rxnS) '\n']);
fprintf(['\tMean gene score: ' num2str(geneS) '\n']);
fprintf(['\tReactions with positive scores: ' num2str(100*sum(a>0)/numel(a)) '%%\n\n']);
end


function [struct_irf_record D_record gamma_record]=mairfres(beta_gibbs,sigma_gibbs,It,Bu,IRFperiods,n,p,k1,signrestable,signresperiods)

% function [struct_irf_record D_record gamma_record Qdraw Qsuccess]=mairfres(beta_gibbs,sigma_gibbs,It,Bu,IRFperiods,n,p,k1,signrestable,signresperiods)
% runs the gibbs sampler to obtain draws from the posterior distribution of IRFs, orthogonalised with a sign restriction setting
% inputs:  - matrix 'beta_gibbs': record of the gibbs sampler draws for the beta vector
%          - matrix 'sigma_gibbs': record of the gibbs sampler draws for the sigma matrix (vectorised)
%          - integer 'It': total number of iterations of the Gibbs sampler (defined p 28 of technical guide)
%          - integer 'Bu': number of burn-in iterations of the Gibbs sampler (defined p 28 of technical guide)
%          - integer 'IRFperiods': number of periods for IRFs
%          - integer 'n': the number of endogenous variables in the model
%          - integer 'p': number of lags included in the model (defined p 7 of technical guide)
%          - integer 'k1': the number of coefficients related to the endogenous variables for each equation in the model
%          - cell 'signrestable': table recording the sign restriction input from the user
%          - cell 'signresperiods': table containing the periods corresponding to each restriction
% outputs: - cell 'struct_irf_record': record of the gibbs sampler draws for the orthogonalised IRFs
%          - matrix 'D_record': record of the gibbs sampler draws for the structural matrix D
%          - matrix 'gamma_record': record of the gibbs sampler draws for the structural disturbances variance-covariance matrix gamma
%          - integer 'Qdraw': total number of draws of the Q matrix
%          - integer 'Qsuccess': number of successful draws of the Q matrix

% preliminary tasks
% create first the cell that will store the results from the simulations
struct_irf_record=cell(n,n);
% storage cell
storage1=cell(It-Bu,1);
storage2=cell(It-Bu,1);

% now identify all the periods concerned with restrictions
% first expand the non-empty entries in signresperiods since they are only expressed in intervals: transform into list
% for instance, translate [1 4] into [1 2 3 4]; I don't think this can done without a loop
temp=cell2mat(signresperiods(~cellfun(@isempty,signresperiods)));
periods=[];
for ii=1:size(temp,1)
periods=[periods temp(ii,1):temp(ii,2)];
end
% suppress duplicates and sort
periods=sort(unique(periods))';
% count the total number of restriction periods (required for IRF matrix)
nperiods=size(periods,1);

% Identify the restriction matrices
% create five cells, corresponding to the three possible restrictions:
% one cell for sign restrictions, three cells for magnitude restrictions, one cell for zero restrictions
Scell=cell(1,n);
Mcell=cell(1,n);
Mlcell=cell(1,n);
Mucell=cell(1,n);
Zcell=cell(1,n);

% Check if value and periods restrictions correspond to each other
if sum(sum(~cellfun(@isempty,signresperiods) == ~cellfun(@isempty,signrestable))) == n^2
    % All cells with sign restrictions also specify the horizon over which
    % these are applied
else
    disp('Warning: Value restrictions do not correspond to period restrictions one to one')
    pause(1)
end

% loop over rows and columns of the period matrix
for ii=1:n
   for jj=1:n
      % if entry (ii,jj) of the period matrix is not empty...
      if ~isempty(signresperiods{ii,jj}) && ~isempty(signrestable{ii,jj})
      % ... then there is a restriction over one (or several) periods
      % loop overt those periods
         for kk=signresperiods{ii,jj}(1,1):signresperiods{ii,jj}(1,2)
         % identify the position of the considered period within the list of all periods (required to build the matrix)
         position=find(periods==kk);
         % now create the restriction matrix: this will depend on the type of restriction
            % if it is a positive sign restriction...
            if strcmp(signrestable{ii,jj},'+')
            % ... then input a 1 entry in the corresponding S matrix
            Scell{1,jj}=[Scell{1,jj};zeros(1,n*nperiods)];
            Scell{1,jj}(end,(position-1)*n+ii)=1;
            % if it is a negative sign restriction...
            elseif strcmp(signrestable{ii,jj},'-')
            % ... then input a -1 entry in the corresponding S matrix
            Scell{1,jj}=[Scell{1,jj};zeros(1,n*nperiods)];
            Scell{1,jj}(end,(position-1)*n+ii)=-1;
            % if it is a zero restriction...
            elseif strcmp(signrestable{ii,jj},'0')
            % ... then input a 1 entry in the corresponding Z matrix
            Zcell{1,jj}=[Zcell{1,jj};zeros(1,n*nperiods)];
            Zcell{1,jj}(end,(position-1)*n+ii)=1;
            % else, a non-empty entry being neither a sign nor a zero restriction has to be a magnitude restriction
            else
            % fill the corresponding M matrices:
            % input a 1 in M
            Mcell{1,jj}=[Mcell{1,jj};zeros(1,n*nperiods)];
            Mcell{1,jj}(end,(position-1)*n+ii)=1;
            % input the lower value of the interval in Ml
            temp=str2num(signrestable{ii,jj});
            Mlcell{1,jj}=[Mlcell{1,jj};temp(1,1)];
            % input the upper value of the interval in Mu
            Mucell{1,jj}=[Mucell{1,jj};temp(1,2)];
            end
         end
      end
   end
end

% now check what kind of restrictions apply among sign, zero and magnitude restrictions
% check for sign restrictions: if there are any, at least one entry in the cell Scell is non-empty
if sum(~cellfun(@isempty,Scell))~=0
signres=1;
else
signres=0;
end
% similarly check for zero restrictions
if sum(~cellfun(@isempty,Zcell))~=0
zerores=1;
else
zerores=0;
end
% and finally, check for magnitude restrictions
if sum(~cellfun(@isempty,Mcell))~=0
magnres=1;
else
magnres=0;
end

not_successful = 0;
hbar = bear.parfor_progressbar(It-Bu,'Progress of Sign Restriction Draws');  %create the progress bar

% initiate Gibbs algorithm
parfor ii=1:It-Bu
% initiate the variable 'success'; this variable will be used to check whether the restrictions are satisfied
success=0;

   while success==0
   not_successful = not_successful+1;
   % switch 'success' to 1; it will be turned back to zero if at any time Q is detected as a candidate not satisfying the restrictions
   success=1;

   % draw the vector of VAR coefficients
   % select first a draw index randomly
   index=floor(rand*(It-Bu))+1;
   % then draw a random set of beta and sigma corresponding to this index (this is done to make it possible to draw, if required, an infinite number of values from the gibbs sampler record, with equal probability on each value)
   B=reshape(beta_gibbs(:,ii),k1,n);
   sigma=reshape(sigma_gibbs(:,ii),n,n);
   hsigma=chol(bear.nspd(sigma),'lower');
   % obtain orthogonalised IRFs
   [irfmatrix ortirfmatrix]=bear.mairfsim(B,hsigma,p,n,max(IRFperiods,max(periods)));

   % generate the stacked IRF matrix
   stackedirfmat=[];
      for jj=1:numel(periods)
      stackedirfmat=[stackedirfmat;ortirfmatrix(:,:,periods(jj,1)+1)];
      end
   % draw an entire random matrix Q satisfying the zero restrictions
   [Q]=bear.qzerores(n,Zcell,stackedirfmat);

   % generate the candidate matrix of structural IRFs
   candidate=stackedirfmat*Q;

   % verify the restrictions in turn (sign, magnitude, or both)
   % the zero restrictions don't have to be verified, there are satisfied by construction
      % consider first the case of pure sign restrictions
      if signres==1 && magnres==0
         % loop over structural shocks; stop as soon as the draw is detected as a fail
         jj=1;
         while success==1 && jj<=n
            % if the corresponding entry in Scell is not empty, it contains a restriction to be checked
            if ~isempty(Scell{1,jj})
               % check if the restrictions hold
               if all(Scell{1,jj}*candidate(:,jj)>=0)
               % if the restrictions do not hold, there may still be a possibility by switching the sign of the Q column
               elseif all(Scell{1,jj}*((-1)*candidate(:,jj))>=0)
               Q(:,jj)=-Q(:,jj);
               % else, if there is no way to have Q succesful, count it as a fail and switch the variable success to 0
               else
               success=0;
               end
            end
         jj=jj+1;
         end
      % consider now the case of pure magnitude restrictions
      elseif signres==0 && magnres==1
         % loop over structural shocks
         jj=1;
         while success==1 && jj<=n
            % if the corresponding entry is not empty, it contains a restriction to be checked
            if ~isempty(Mcell{1,jj})
               % check if the restriction holds
               if all((Mcell{1,jj}*candidate(:,jj)-Mlcell{1,jj}).*(Mucell{1,jj}-Mcell{1,jj}*candidate(:,jj))>=0)
               % if they do not hold, there may still be a possibility by switching the sign of the Q column
               elseif all((Mcell{1,jj}*(-1)*candidate(:,jj)-Mlcell{1,jj}).*(Mucell{1,jj}-Mcell{1,jj}*(-1)*candidate(:,jj))>=0)
               Q(:,jj)=-Q(:,jj);
               % else, if there is no way to have Q succesful, count it as a fail and switch the variable success to 0
               else
               success=0;
               end
            end
         jj=jj+1;
         end
      % consider then the case of mixed sign and magnitude restrictions
      elseif signres==1 && magnres==1
         % loop over structural shocks
         jj=1;
         while success==1 && jj<=n
         % for a given structural shock, there may be no restrictions, only one type, or both types
            % if both types
            if ~isempty(Scell{1,jj}) && ~isempty(Mcell{1,jj})
               % check both restrictions
               if all(Scell{1,jj}*candidate(:,jj)>=0) && all((Mcell{1,jj}*candidate(:,jj)-Mlcell{1,jj}).*(Mucell{1,jj}-Mcell{1,jj}*candidate(:,jj))>=0)
               % if not, try to switch the sign of the corresponding column in Q
               elseif all(Scell{1,jj}*((-1)*candidate(:,jj))>=0) && all((Mcell{1,jj}*(-1)*candidate(:,jj)-Mlcell{1,jj}).*(Mucell{1,jj}-Mcell{1,jj}*(-1)*candidate(:,jj))>=0)
               Q(:,jj)=-Q(:,jj);
               % else, if there is no way to have Q succesful, count it as a fail and switch the variable success to 0
               else
               success=0;
               end
            % if only sign restrictions
            elseif ~isempty(Scell{1,jj}) && isempty(Mcell{1,jj})
               % check if the restrictions hold
               if all(Scell{1,jj}*candidate(:,jj)>=0)
               % if the restrictions do not hold, there may still be a possibility by switching the sign of the Q column
               elseif all(Scell{1,jj}*((-1)*candidate(:,jj))>=0)
               Q(:,jj)=-Q(:,jj);
               % else, if there is no way to have Q succesful, count it as a fail and switch the variable success to 0
               else
               success=0;
               end
            % if only magnitude restrictions
            elseif isempty(Scell{1,jj}) && ~isempty(Mcell{1,jj})
               % check if the restriction holds
               if all((Mcell{1,jj}*candidate(:,jj)-Mlcell{1,jj}).*(Mucell{1,jj}-Mcell{1,jj}*candidate(:,jj))>=0)
               % if they do not hold, there may still be a possibility by switching the sign of the Q column
               elseif all((Mcell{1,jj}*(-1)*candidate(:,jj)-Mlcell{1,jj}).*(Mucell{1,jj}-Mcell{1,jj}*(-1)*candidate(:,jj))>=0)
               Q(:,jj)=-Q(:,jj);
               % else, if there is no way to have Q succesful, count it as a fail and switch the variable success to 0
               else
               success=0;
               end
            end
         jj=jj+1;
         end
      % finally, the only possible remaining case is that of pure zero restrictions, satisfied by construction: no need to check
      else
      end

   % now, if sucess is still equal to 1, it means that the draw was successful for all the restrictions: keep it
   % otherwise, if success has been switched to 0, there was at least one failure: discard the draw and try with a new draw
   end

   % store
   for jj=1:IRFperiods
   storage1{ii,1}(:,:,jj)=ortirfmatrix(:,:,jj)*Q;
   end
   storage2{ii,1}=hsigma*Q;

   hbar.iterate(1);   % update progress by one iteration

end

close(hbar);   %close progress bar

% reorganise storage
% loop over iterations
for ii=1:It-Bu
   % loop over IRF periods
   for jj=1:IRFperiods
      % loop over variables
      for kk=1:n
         % loop over shocks
         for ll=1:n
         struct_irf_record{kk,ll}(ii,jj)=storage1{ii,1}(kk,ll,jj);
         end
      end
   end
D_record(:,ii)=storage2{ii,1}(:);
gamma_record(:,ii)=bear.vec(eye(n));
end

fprintf('Accepted Draws in Percent of Total Number of Draws: %f', 100*(It-Bu)/(not_successful + It-Bu))


function rez = learnAndSolve8b(rez, iorder)
% This is the main optimization. Takes the longest time and uses the GPU heavily.

Nbatches = rez.ops.Nbatch;

rng(iseed);
% if getOr(rez.ops, 'midpoint', 0)
%     rez.iorig = randperm(Nbatches);
% end
% rez.istart = ceil(Nbatches/2); % this doesn't really matter anymore

rez.iorig = iorder;

rez     = learnTemplates(rez, rez.iorig);

% if ~isfield(rez, 'W') || isempty(rez.W)
%     rez     = learnTemplates(rez, rez.iorig);
% else
%     rez     = learnTemplates2(rez, rez.iorig);
% end

rez.ops.fig = 0;
rez         = runTemplates(rez);


function matRad_writeVTK(filepath,cube,metadata)
% matRad function to write vtk cubes
%
% call
%   matRad_writeVTK(filepath,cube,metadata)
%
% input
%   filepath:   full filename (with extension)
%   cube:       3D array to be written into file
%   metadata:   struct of metadata. Writer will wrap the existing metadata
%               to VTK standard-specific fields
%               Necessary fieldnames are:
%               - resolution: [x y z]
%               - datatype: numeric MATLAB-Datatype
%
% output
%   file will be written to disk
%
% References
%   -
%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Copyright 2015 the matRad development team.
%
% This file is part of the matRad project. It is subject to the license
% terms in the LICENSE file found in the top-level directory of this
% distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part
% of the matRad project, including this file, may be copied, modified,
% propagated, or distributed except according to the terms contained in the
% LICENSE file.
%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Sanity checks and restrictions
dimensions = size(cube);
if numel(dimensions) ~= 3
    error('Sorry! matRad only supports 3-dimensional VTK output');
end

fid = fopen(filepath, 'wb');
if fid <= 0
    error('Could not open VTK destination file!');
end
cleaner = onCleanup(@() fclose(fid));

%We perform
if isfield(metadata,'axisPermutation')
    cube = permute(cube,metadata.axisPermutation);
end

fprintf(fid, '# vtk DataFile Version 3.0\n');
fprintf(fid, 'vtk output\n');
fprintf(fid, 'BINARY\n');
fprintf(fid, 'DATASET STRUCTURED_POINTS\n');
fprintf(fid, 'DIMENSIONS    %d   %d   %d\n', dimensions(1),dimensions(2),dimensions(3));
fprintf(fid, 'SPACING    %f   %f   %f\n',metadata.resolution(1),metadata.resolution(2),metadata.resolution(3));
fprintf(fid, 'ORIGIN    %f   %f   %f\n',metadata.imageOrigin(1),metadata.imageOrigin(2),metadata.imageOrigin(3));
fprintf(fid, 'POINT_DATA   %d\n',prod(dimensions));
if isfield(metadata,'dataName')
    dataName = metadata.dataName;
else
    dataName = 'scalars';
end
fprintf(fid, 'SCALARS %s %s\n',dataName,metadata.datatype);
fprintf(fid, 'LOOKUP_TABLE default\n');
fwrite(fid,cube,metadata.datatype,'b');

end


%% Example social DB

clear all;
close all;
clear classes
clc;

graph = prtGraphDataGenKarate;
plot(graph);


function [SwarmRBF,DeltaRBF] = RBFOperator(net,Demons,SwarmRBF,DeltaRBF,Gbest,Problem)
% OperatorPSO - The operator of particle swarm optimization.

%------------------------------- Copyright --------------------------------
% Copyright (c) 2023 BIMK Group. You are free to use the PlatEMO for
% research purposes. All publications which use this platform or any code
% in the platform should acknowledge the use of "PlatEMO" and reference "Ye
% Tian, Ran Cheng, Xingyi Zhang, and Yaochu Jin, PlatEMO: A MATLAB platform
% for evolutionary multi-objective optimization [educational forum], IEEE
% Computational Intelligence Magazine, 2017, 12(4): 73-87".
%--------------------------------------------------------------------------

    %% Parameter setting
    factor = 0;
    [N,D]  = size(SwarmRBF);
    D      = D - 1;
    BU     = Problem.upper;
    BD     = Problem.lower;

    %% SL-PSO
    for i = 1 : N
        % Choose an individual to learn one dimension to follow
        BetterInd = find(Demons(:,D+1)<SwarmRBF(i,D+1));
        ChoseId   = zeros(1,D);
        if length(BetterInd) == 1
            ChoseId = Demons(BetterInd(1),1:D);
        elseif length(BetterInd) == 2
            Mix = rand(1,D) > 0.5;
            ChoseId(Mix) = Demons(BetterInd(1),Mix);
            ChoseId(~Mix) = Demons(BetterInd(2),~Mix);
        elseif length(BetterInd) > 2
            for t = 1 : D
                ChoseId(t) = Demons(BetterInd(randperm(length(BetterInd),1)),t);
            end
        end
        if ~isempty(BetterInd)
            DeltaRBF(i,:) = rand(1,D).*DeltaRBF(i,:) + rand(1,D).*(ChoseId-SwarmRBF(i,1:D));
            % Update
            DeltaRBF(i,1:D) = max(DeltaRBF(i,1:D),BD);
            DeltaRBF(i,1:D) = min(DeltaRBF(i,1:D),BU);
            SwarmRBF(i,1:D) = DeltaRBF(i,1:D);
        end
    end
    %% Repair
    SwarmRBF(:,1:D) = max(SwarmRBF(:,1:D),repmat(Problem.lower,N,1));
    SwarmRBF(:,1:D) = min(SwarmRBF(:,1:D),repmat(Problem.upper,N,1));
    % Approximate the fitness of each particle
    SwarmRBF(:,1+D) = sim(net,SwarmRBF(:,1:D)')';
end


% This code is in association with the following paper
% "Ma J, Zhou Z, Wang B, et al. Infrared and visible image fusion based on visual saliency map and weighted least square optimization[J].
% Infrared Physics & Technology, 2017, 82:8-17."
% Authors: Jinlei Ma, Zhiqiang Zhou, Bo Wang, Hua Zong
% Code edited by Jinlei Ma, email: majinlei121@163.com

clear all
close all

label='';
for k = 1:5
if k==1
    label='_gau_001';
end
if k==2
    label='_gau_0005';
end
if k==3
    label='_sp_01';
end
if k==4
    label='_sp_02';
end
if k==5
    label='_poi';
end
disp(label);

for i=1:10
index = i;

% path1 = ['./MF_images/image',num2str(index),'_left.png'];
% path2 = ['./MF_images/image',num2str(index),'_right.png'];
% fused_path = ['./fused_mf/fused',num2str(index),'_wls.png'];

path1 = ['./mf_noise_images/image',num2str(i),label,'_left.png'];
path2 = ['./mf_noise_images/image',num2str(i),label,'_right.png'];
fused_path = ['./fused_mf_noise/fused',num2str(i),label,'_wls.png'];

% I1 is a visible image, and I2 is an infrared image.
I1 = imread(path1);
I2 = imread(path2);

I1 = im2double(I1);
I2 = im2double(I2);

% figure;imshow(I1);
% figure;imshow(I2);
tic
fused = WLS_Fusion(I1,I2);
toc

% figure;imshow(fused);
imwrite(fused,fused_path,'png');

end
end


function [varargout]=subEdge(varargin)

%%

switch nargin
    case 2
        F=varargin{1};
        V=varargin{2};
        n=1;
        uniqueOpt=1;
    case 3
        F=varargin{1};
        V=varargin{2};
        n=varargin{3};
        uniqueOpt=1;
    case 4
        F=varargin{1};
        V=varargin{2};
        n=varargin{3};
        uniqueOpt=varargin{4};
end

%%

%Check if n can be achieved through splitting
nSplitIterations=log2(n+1); %Check for integer solution
logicInteger=abs(round(nSplitIterations)-nSplitIterations)<eps(nSplitIterations);

if uniqueOpt==1 && logicInteger
    subMethod='split';
else
    subMethod='seed';
end

switch subMethod
    case 'split' %iteratively split edges (no double points created, unique operation avoided)
        Fs=F; Vs=V;
        for q=1:1:nSplitIterations
            F=Fs; V=Vs;

            Fs=zeros(size(Fs,1),size(Fs,2)*2);
            Fs(:,1:2:end)=F;

            [E]=patchEdges(F,1);

            numPoints = size(V,1);
            numEdges = size(E,1);

            % Get indices of the edges associated with each face
            A = sparse(E(:,1),E(:,2),(1:numEdges)+numPoints,numPoints,numPoints,numEdges);
            A = max(A,A'); %Copy symmetric

            indFs=2:2:size(Fs,2);
            for qp=1:1:size(F,2)
                if qp<size(F,2)
                    indA_now=F(:,qp)+(F(:,qp+1)-1)*numPoints;
                else
                    indA_now=F(:,qp)+(F(:,1)-1)*numPoints;
                end

                indV_now=full(A(indA_now));
                Fs(:,indFs(qp))=indV_now;
            end

            %Create vertex array
            Vn=0.5*(V(E(:,1),:)+V(E(:,2),:)); %new mid-edge points
            Vs = [V; Vn]; %Join point sets
        end
    case 'seed' %Seed edge points and remove doubles (more memory intensive)
        %Edges matrix
        [E]=patchEdges(F,0);

        Vs=zeros(size(E,1)*(n+1),size(V,2));
        for q=1:1:size(V,2)
            X=V(:,q);
            XE=X(E);
            X_add=linspacen(XE(:,1),XE(:,2),n+2);
            X_add=X_add(:,1:end-1)';
            Vs(:,q)=X_add(:);
        end

        ind=(1:1:size(Vs,1))';
        Fs=reshape(ind,[(n+1)*size(F,2),size(Vs,1)./((n+1)*size(F,2))])';

        %Merge non-unique nodes
        [Fs,Vs]=mergeVertices(Fs,Vs);

end

%% Collect output
varargout{1}=Fs;
varargout{2}=Vs;

end
%%
% _*GIBBON footer text*_
%
% License: <https://github.com/gibbonCode/GIBBON/blob/master/LICENSE>
%
% GIBBON: The Geometry and Image-based Bioengineering add-On. A toolbox for
% image segmentation, image-based modeling, meshing, and finite element
% analysis.
%
% Copyright (C) 2006-2022 Kevin Mattheus Moerman and the GIBBON contributors
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program.  If not, see <http://www.gnu.org/licenses/>.


function test_bug2834

% MEM 2gb
% WALLTIME 00:20:00
% DEPENDENCY ft_sourceanalysis

% get some data
filename = dccnpath('/home/common/matlab/fieldtrip/data/test/latest/freq/meg/freq_mtmconvol_powandcsd_ctf151.mat');
load(filename);

filename = dccnpath('/home/common/matlab/fieldtrip/data/test/latest/vol/Subject01vol_singleshell.mat');
load(filename);

% the issue seems to be due to the fact that ft_selectdata with cfg.channel
% does not automatically subselect the corresponding channelcmbs

cfg = [];
cfg.channel = {'MEG' '-MLO21' '-MLF21'};
freq2 = ft_selectdata(cfg, freq);

% this works from the point of view of ft_selectdata
tmp = repmat(ft_channelselection(cfg.channel, freq.label), [1 149]);
tmp2 = tmp';

cfg = [];
cfg.channel = {'MEG' '-MLO21' '-MLF21'};
cfg.channelcmb = [tmp(tril(ones(149),-1)>0) tmp2(tril(ones(149),-1)>0)];
freq3 = ft_selectdata(cfg, freq);

cfg = [];
cfg.headmodel = vol;
cfg.sourcemodel.resolution = 1;
cfg.method = 'dics';
cfg.frequency = 14;
cfg.latency   = 0.5;
source = ft_sourceanalysis(cfg, freq3);


function [bnet, LL, engine] = learn_params_dbn_em(engine, evidence, varargin)
% LEARN_PARAMS_DBN Set the parameters in a DBN to their ML/MAP values using batch EM.
% [bnet, LLtrace, engine] = learn_params_dbn_em(engine, data, ...)
%
% data{l}{i,t} = value of node i in slice t of time-series l, or [] if hidden.
%   Suppose you have L time series, each of length T, in an O*T*L array D,
%   where O is the num of observed scalar nodes, and N is the total num nodes per slice.
%   Then you can create data as follows, where onodes is the index of the observable nodes:
%      data = cell(1,L);
%      for l=1:L
%        data{l} = cell(N, T);
%        data{l}(onodes,:) = num2cell(D(:,:,l));
%      end
% Of course it is possible for different sets of nodes to be observed in
% each slice/ sequence, and for each sequence to be a different length.
%
% LLtrace is the learning curve: the vector of log-likelihood scores at each iteration.
%
% Optional arguments [default]
%
% max_iter - specifies the maximum number of iterations [100]
% thresh - specifies the thresold for stopping EM [1e-3]
%   We stop when |f(t) - f(t-1)| / avg < threshold,
%   where avg = (|f(t)| + |f(t-1)|)/2 and f is log lik.
% verbose - display loglik at each iteration [1]
% anneal - 1 means do deterministic annealing (only for entropic priors) [0]
% anneal_rate - geometric cooling rate [0.8]
% init_temp - initial annealing temperature [10]
% final_temp - final annealing temperature [1e-3]
%

max_iter = 100;
thresh = 1e-3;
anneal = 0;
anneal_rate = 0.8;
init_temp = 10;
final_temp = 1e-3;
verbose = 1;

for i=1:2:length(varargin)
  switch varargin{i}
   case 'max_iter', max_iter = varargin{i+1};
   case 'thresh', thresh = varargin{i+1};
   case 'anneal', anneal = varargin{i+1};
   case 'anneal_rate', anneal_rate = varargin{i+1};
   case 'init_temp', init_temp = varargin{i+1};
   case 'final_temp', final_temp = varargin{i+1};
   otherwise, error(['unrecognized argument' varargin{i}])
  end
end

% take 1 EM step at each temperature value, then when temp=0, run to convergence
% When using an entropic prior, Z = 1-T, so
% T=2 => Z=-1 (max entropy)
% T=1 => Z=0 (max likelihood)
% T=0 => Z=1 (min entropy / max structure)
num_iter = 1;
LL = [];
if anneal
  temperature = init_temp;
  while temperature > final_temp
    [engine, loglik, logpost] = EM_step(engine, evidence, temperature);
    if verbose
      fprintf('EM iteration %d, loglik = %8.4f, logpost = %8.4f, temp=%8.4f\n', ...
	      num_iter, loglik, logpost, temperature);
    end
    num_iter = num_iter + 1;
    LL = [LL loglik];
    temperature = temperature * anneal_rate;
  end
  temperature = 0;
  previous_loglik = loglik;
  previous_logpost = logpost;
else
  temperature = 0;
  previous_loglik = -inf;
  previous_logpost = -inf;
end

converged = 0;
while ~converged & (num_iter <= max_iter)
  [engine, loglik, logpost] = EM_step(engine, evidence, temperature);
  if verbose
    %fprintf('EM iteration %d, loglik = %8.4f, logpost = %8.4f\n', ...
    %	    num_iter, loglik, logpost);
    fprintf('EM iteration %d, loglik = %8.4f\n', num_iter, loglik);
  end
  num_iter = num_iter + 1;
  [converged, decreased] = em_converged(loglik, previous_loglik, thresh);
  %[converged, decreased] = em_converged(logpost, previous_logpost, thresh);
  previous_loglik = loglik;
  previous_logpost = logpost;
  LL = [LL loglik];
end

bnet = bnet_from_engine(engine);

%%%%%%%%%

function [engine, loglik, logpost] = EM_step(engine, cases, temp)

bnet = bnet_from_engine(engine); % engine contains the old params that are used for the E step
ss = length(bnet.intra);
CPDs = bnet.CPD; % these are the new params that get maximized
num_CPDs = length(CPDs);

% log P(theta|D) = (log P(D|theta) + log P(theta)) - log(P(D))
% where log P(D|theta) = sum_cases log P(case|theta)
% and log P(theta) = sum_CPDs log P(CPD) - only count once even if tied!
% logpost = log P(theta,D) (un-normalized)
% This should be negative, and increase at every step.

adjustable = zeros(1,num_CPDs);
logprior = zeros(1, num_CPDs);
for e=1:num_CPDs
  adjustable(e) = adjustable_CPD(CPDs{e});
end
adj = find(adjustable);

for e=adj(:)'
  logprior(e) = log_prior(CPDs{e});
  CPDs{e} = reset_ess(CPDs{e});
end

loglik = 0;
for l=1:length(cases)
  evidence = cases{l};
  if ~iscell(evidence)
    error('training data must be a cell array of cell arrays')
  end
  [engine, ll] = enter_evidence(engine, evidence);
  assert(~isnan(ll))
  loglik = loglik + ll;
  T = size(evidence, 2);

  % We unroll ns etc because in update_ess, we refer to nodes by their unrolled number
  % so that they extract evidence from the right place.
  % (The CPD should really store its own version of ns and cnodes...)
  ns = repmat(bnet.node_sizes_slice(:), [1 T]);
  cnodes = unroll_set(bnet.cnodes_slice, ss, T);

  %hidden_bitv = repmat(bnet.hidden_bitv(1:ss), [1 T]);
  hidden_bitv = zeros(ss, T);
  hidden_bitv(isemptycell(evidence))=1;
  % hidden_bitv(i) = 1 means node i is hidden.
  % We pass this in, rather than using isemptycell(evidence(dom)), because
  % isemptycell is very slow.

  t = 1;
  for i=1:ss
    e = bnet.equiv_class(i,1);
    if adjustable(e)
      fmarg = marginal_family(engine, i, t);
      CPDs{e} = update_ess(CPDs{e}, fmarg, evidence, ns(:), cnodes(:), hidden_bitv(:));
    end
  end

  for i=1:ss
    e = bnet.equiv_class(i,2);
    if adjustable(e)
      for t=2:T
	fmarg = marginal_family(engine, i, t);
	CPDs{e} = update_ess(CPDs{e}, fmarg, evidence, ns(:), cnodes(:), hidden_bitv(:));
      end
    end
  end
end

logpost = loglik + sum(logprior(:));

for e=adj(:)'
  CPDs{e} = maximize_params(CPDs{e}, temp);
end

engine = update_engine(engine, CPDs);


% From examples/imagenet/cnn_imagenet_deploy.m
% -------------------------------------------------------------------------
function dagMergeBatchNorm(net, names)
% -------------------------------------------------------------------------
for name = names
  name = char(name) ;
  layer = net.layers(net.getLayerIndex(name)) ;

  % merge into previous conv layer
  playerName = dagFindLayersWithOutput(net, layer.inputs{1}) ;
  playerName = playerName{1} ;
  playerIndex = net.getLayerIndex(playerName) ;
  player = net.layers(playerIndex) ;
  if ~isa(player.block, 'dagnn.Conv')
    error('Batch normalization cannot be merged as it is not preceded by a conv layer.') ;
  end

  % if the convolution layer does not have a bias,
  % recreate it to have one
  if ~player.block.hasBias
    block = player.block ;
    block.hasBias = true ;
    net.renameLayer(playerName, 'tmp') ;
    net.addLayer(playerName, ...
                 block, ...
                 player.inputs, ...
                 player.outputs, ...
                 {player.params{1}, sprintf('%s_b',playerName)}) ;
    net.removeLayer('tmp') ;
    playerIndex = net.getLayerIndex(playerName) ;
    player = net.layers(playerIndex) ;
    biases = net.getParamIndex(player.params{2}) ;
    net.params(biases).value = zeros(block.size(4), 1, 'single') ;
  end

  filters = net.getParamIndex(player.params{1}) ;
  biases = net.getParamIndex(player.params{2}) ;
  multipliers = net.getParamIndex(layer.params{1}) ;
  offsets = net.getParamIndex(layer.params{2}) ;
  moments = net.getParamIndex(layer.params{3}) ;

  [filtersValue, biasesValue] = mergeBatchNorm(...
    net.params(filters).value, ...
    net.params(biases).value, ...
    net.params(multipliers).value, ...
    net.params(offsets).value, ...
    net.params(moments).value) ;

  net.params(filters).value = filtersValue ;
  net.params(biases).value = biasesValue ;
end

% -------------------------------------------------------------------------
function [filters, biases] = mergeBatchNorm(filters, biases, multipliers, offsets, moments)
% -------------------------------------------------------------------------
% wk / sqrt(sigmak^2 + eps)
% bk - wk muk / sqrt(sigmak^2 + eps)
a = multipliers(:) ./ moments(:,2) ;
b = offsets(:) - moments(:,1) .* a ;
biases(:) = biases(:) + b(:) ;
sz = size(filters) ;
numFilters = sz(4) ;
filters = reshape(bsxfun(@times, reshape(filters, [], numFilters), a'), sz) ;

% -------------------------------------------------------------------------
function layers = dagFindLayersWithOutput(net, outVarName)
% -------------------------------------------------------------------------
layers = {} ;
for l = 1:numel(net.layers)
  if any(strcmp(net.layers(l).outputs, outVarName))
    layers{1,end+1} = net.layers(l).name ;
  end
end


function [We, words] = loadWeWords(modelFile, modelFormat)
% modelFormat:
% 0 -- Matlab file
% 1 -- text file with a header line <numWords> <embDim>.  Subsequent lines has <word> <values>
% 2 -- text file with each line has <word> <values>
% 3 -- assume that there are two files modelFile.We, modelFile.words

  verbose=0;
  %% load from Matlab mat file
  if modelFormat==0
    %% load We
    load(modelFile, 'We', 'words');
    if ~exist('We', 'var')
      load(modelFile , 'allW');

      if isfield(allW, 'We')
        We = allW.We;
      else
        error('evaluateWordSim: no We in %s', modelFile);
      end
    end

    %% load vocab
    if ~exist('words', 'var')
      error('No words in the model file %s\n', modelFile);
    end
  elseif modelFormat==3 % modelFile.We, modelFile.words
    % words
    fid = fopen([modelFile '.words'], 'r');
    tmp = textscan(fid, '%s');
    words = tmp{1};
    fclose(fid);
    We = dlmread([modelFile '.We']);
    We = We';
  %% load from a text file
  else
    fid = fopen(modelFile, 'r');
    line = fgetl(fid);
    assert(ischar(line));
    if modelFormat==1 % header line <numWord> <embDim>
      [~, embDimStr] = strtok(line, ' ');
      embDim = str2double(embDimStr);
      line = fgetl(fid);
    else
      tokens = strsplit(line);
      embDim = length(tokens)-1;
    end

    if verbose==1
      fprintf(2, '# Reading file %s, embDim=%d\n', modelFile, embDim);
    end

    words = {};
    We = [];
    numWords = 0;
    while ischar(line)
      line = strtrim(line);
      if strcmp(line, '')
        line = fgetl(fid);
        continuel
      end

      [word, valueStr] = strtok(line, ' ');
      % word
      words = [words word];
      % emb
      We = [We sscanf(valueStr, '%f')];

      numWords = numWords+1;
      if verbose==1 && (mod(numWords, 10000)==0)
        fprintf(2, ' (%d)', numWords);
      end
      line = fgetl(fid);
    end

    if verbose==1
      fprintf(2, ' Done! Num words = %d\n', numWords);
    end
  end
end


function rgb = createRGBStack(img1, img2, img3)
%CREATERGBSTACK Concatenate 2 or 3 grayscale stacks to form a color stack
%
%   RGB = createRGBStack(RED, GREEN)
%   RGB = createRGBStack(RED, GREEN, BLUE)
%   Create a new 3D image containing RGB values from 2 or 3 grayscale
%   images. All input images must have the same size M-by-N-ny-P.
%   The resulting RGB image has size M-by-N-by-3-by-P.
%
%   It is also possible to specify empty red or green channel:
%   RGB = createRGBStack([], GREEN, BLUE)
%   RGB = createRGBStack(RED, [], BLUE)
%
%   Example
%     % Colorize in red the bright parts of a 3D image
%     metadata = analyze75info('brainMRI.hdr');
%     I = analyze75read(metadata) * 3;
%     I2 = I;
%     I2(I > 200) = 0;
%     rgb = createRGBStack(I, I2, I2);
%     slicer(rgb)
%
%   See also
%
% ------
% Author: David Legland
% e-mail: david.legland@grignon.inra.fr
% Created: 2010-10-26,    using Matlab 7.9.0.529 (R2009b)
% Copyright 2010 INRA - Cepia Software Platform.

%% Process input arguments

% ensure initialisation of variable
if nargin < 3
    img3 = [];
end

% choose one of the 3 inputs as reference
ref = img1;
if isempty(img1)
    ref = img2;
    if isempty(img2)
        ref = img3;
        if isempty(ref)
            error('At least one channel must be specified');
        end
    end
end

%% Initialize result

% size of reference and new image
dim = size(ref);
newDim = [dim(1:2) 3 dim(3)];

% create empty result image
if islogical(ref)
    rgb = false(newDim);
else
    rgb = zeros(newDim, class(ref));
end

%% Fill up result with data

% Red channel
if ~isempty(img1)
    rgb(:,:,1,:) = img1;
end

% green channel
if ~isempty(img2)
    if sum(size(img2) ~= dim) > 0
        error('Red and Green images must have the same size');
    end
    rgb(:,:,2,:) = img2;
end

% blue channel
if ~isempty(img3)
    if sum(size(img3) ~= dim) > 0
        error('Blue and reference images must have the same size');
    end
    rgb(:,:,3,:) = img3;
end


function G = createGroupIncidenceMatrix_old(model, gcmOutputFile, gcmMetList, jankowskiGroupData)
% Creates `groupData` struct to calculate reaction Gibbs energies with reduced
% error in `vonB`.
%
% USAGE:
%
%    G = createGroupIncidenceMatrix_old(model, gcmOutputFile, gcmMetList, jankowskiGroupData)
%
% INPUTS:
%    model:
%    gcmOutputFile:
%    gcmMetList:
%    jankowskiGroupData:
%
% OUTPUT:
%    G:

groups = model.jankowskiGroupData.groups;
metList = model.gcmMetList;
fidin = fopen(model.gcmOutputFile, 'r');

tline1 = fgetl(fidin); % Header row

G = zeros(length(model.mets),length(groups));
counter = 0;

while 1
    tline1 = fgetl(fidin);
    counter = counter + 1;

    if ~ischar(tline1)
        break;
    end

    if ~strcmp('NONE;NONE;', tline1(1:10))
        semiColonIdx = strfind(tline1, ';');
        barIdx = strfind(tline1, '|');
        colonIdx = strfind(tline1, ':');

        thisMetGroup = tline1((semiColonIdx(2) + 1):(colonIdx(1) - 1));
        thisMetGroupCount = str2double(tline1((colonIdx(1) + 1):(barIdx(1) - 1)));

        if any(ismember(groups,thisMetGroup))
            G(ismember(model.mets,metList(counter)),ismember(groups,thisMetGroup)) = thisMetGroupCount;
        else
            error([thisMetGroup, ' not in group list from GCM paper.'])
        end

        for n = 1:(length(barIdx)-1)
            thisMetGroup = tline1((barIdx(n) + 1):(colonIdx(n+1) - 1));
            thisMetGroupCount = str2double(tline1((colonIdx(n+1) + 1):(barIdx(n+1) - 1)));
            if any(ismember(groups,thisMetGroup))
                G(ismember(model.mets,metList(counter)),ismember(groups,thisMetGroup)) = thisMetGroupCount;
            else
                error([thisMetGroup, ' not in group list from GCM paper.'])
            end
        end

    end

end

fclose(fidin);


% evalreprerror ... computes and plots the final error statistics
%
% cam = evalreprerror(cam,config)
% cam, config ... see the main GOCAL script
%
% $Id: evalreprerror.m,v 2.0 2003/06/19 12:07:03 svoboda Exp $

function cam = evalreprerror(cam,config)

Octave = exist('OCTAVE_VERSION', 'builtin') ~= 0;

disp('2D reprojection error')
disp(sprintf('All points: mean  %2.2f pixels, std is %2.2f',mean([cam.err2d]), std([cam.err2d])'));
% disp(sprintf('Inliers:    mean  %2.2f pixels, std is %2.2f',mean([cam.inerr2d]), std([cam.inerr2d])'));
if mean([cam.err2d])>1.5 | std([cam.err2d])>1.5
	disp('***************************************************')
	disp('W A R N I N G: the reprojection error is relatively high !')
end

%%%
% evaluate the reprojection error for each camera separately to detect possible problems
%%%
for i=1:size(config.cal.cams2use,2),
	cam(i).mean2Derr = mean(cam(i).err2d);
	cam(i).std2Derr  = std(cam(i).err2d);
end
% sort the values and print them to the 2D graphs

if ~Octave,
figure(30),
clf
plot(config.cal.cams2use,[cam.mean2Derr],'bd'),
hold on, grid on,
plot(config.cal.cams2use,[cam.mean2Derr],'b-'),
plot(config.cal.cams2use,[cam.std2Derr],'rd')
plot(config.cal.cams2use,[cam.std2Derr],'r-')
xlabel('Id of the camera')
title('2D error: mean (blue), std (red)')
ylabel('pixels')

figure(31)
clf
bar(config.cal.cams2use,[cam.mean2Derr;cam.std2Derr]',1.5)
grid on
xlabel('Id of the camera')
title('2D error: mean (blue), std (red)')
ylabel('pixels')

figure(31),
eval(['print -depsc ', config.paths.data, 'reprerrors.eps'])

figure(4),
eval(['print -depsc ', config.paths.data, 'reconstructedsetup.eps'])
end

Ret = 1;
return


% This is the main script runner for training, it collects the
% training samples, followed by model training

clear

% define the root name of database
root = '../data_preparation/prepared_data/';

% which scales we're doing
scales = [0.25, 0.35, 0.5];

% the data generation parameters
sigma = 1;
num_samples = 5e5;
ratio_neg = 5;
norm = 1;
normalisation_size = 19;

patch_types = {'reg', 'grad'};

% Should a frontal view be created
frontalView = [1];

% The other views to be used
profileViewInds = [2,3,4];
upDownViewInds = [];

version = 'multi_pie';

% the naming of
wild_loc = 'mpie_';

for s=scales
    Train_all(root, frontalView, profileViewInds, upDownViewInds, s,...
        sigma, version, norm, 'ratio_neg', ratio_neg,...
        'num_samples', num_samples, 'data_loc', wild_loc,...
        'patch_types', patch_types, 'normalisation_size', normalisation_size);
end


function [Psi, algParams, outParams] = initBPHMMSeq( data, model, initParams, algParams, outParams )
% Initialize BP-HMM hidden variables for MCMC sampling
%  via a **sequential** process
% That first fits a crude model to a subset of M sequences alone,
%  and then iteratively adds sequences ONE AT A TIME
%    trying to create unique features when appropriate
% Be warned: there is no guaranteed number of features that exist
%  after this procedure. It depends not only on number of sequences
%  but also on the complexity of those sequences

M = initParams.nSubsetObj;

% ------------------------------- Remember old state to use again afterward
curStream = RandStream.getGlobalStream();
entryState = curStream.State;

% Reset PRNG state to associate with specific *task*
reset( RandStream.getGlobalStream(), outParams.taskID );

% -------------------------------------------- Init BPHMM for first M sequences
if strcmp( class(data), 'SeqData' )
    Dsubset = SeqData();
elseif strcmp( class(data), 'ARSeqData' )
    Dsubset = ARSeqData( data.R );
end
subsetObjIDs = randsample( data.N, min(M,data.N)  );
remObjIDs    = setdiff( 1:data.N, subsetObjIDs );
for ii = subsetObjIDs'
   Dsubset = Dsubset.addSeq( data.seq(ii), data.seqNames{ii} );
end
outP = outParams;
outP.doPrintHeaderInfo = 0;

fprintf( 'Initializing: %d unique on subset of %d sequences\n', initParams.F.nUniquePerObj, M );

% Initialize model on first M sequences
%  using a few unique featuress per sequence
subPsi = initBPHMMFresh( Dsubset, model, initParams, algParams, outP );

if ~isempty( algParams.doAnneal )
   subPsi.invTemp = 0; % forget reversibility... give me a good init!
end

% Run through a few iters of full sampler on this small dataset
for warmiter = 1:5
    subPsi = sampleSharedFeats( subPsi, Dsubset );
    subPsi = sampleStateSeq( subPsi, Dsubset );

    subPsi = sampleSplitMerge_SeqAlloc( subPsi, Dsubset, algParams );

    subPsi.ThetaM = subPsi.ThetaM.sampleAllTheta( Dsubset, subPsi.stateSeq );
    subPsi.TransM = subPsi.TransM.sampleAllEta( subPsi.F, subPsi.stateSeq );

    % Try to delete extra features if possible
    algPdeath = algParams;
    algPdeath.Debug.MoveType = 0; %force death move!
    algPdeath.doAvoidCache   = 1;
    if isfield( subPsi, 'cache' )
        subPsi = rmfield( subPsi, 'cache' );
    end
    subPsi = sampleUniqueFeats( subPsi, Dsubset, algPdeath, 0 );
    subPsi = sampleStateSeq( subPsi, Dsubset );

    checkPsiFeatureConsistency( subPsi );

end

fprintf( 'Sampling features for remaining sequences\n' );
tic;
for rr = 1:length( remObjIDs )
    Dsubset = Dsubset.addSeq( data.seq( remObjIDs(rr) ), data.seqNames{remObjIDs(rr)} );
    curObjID = Dsubset.N;
    % Assume new object possesses all features
    %   and has prior mean on transition params
    subPsi.F(curObjID,:) = 1;
    subPsi.TransM = subPsi.TransM.getAllEta_PriorMean( 1:curObjID, subPsi.F, []);

    %if isfield( subPsi, 'cache' )
    %       subPsi = rmfield( subPsi, 'cache' );
    %end
    %subPsi = sampleStateSeq( subPsi, Dsubset, curObjID );
    %checkPsiFeatureConsistency( subPsi );

    % Perform MH updates to turn shared features on and off
    [subPsi] = sampleSharedFeats( subPsi, Dsubset, curObjID );

    % Attempt to do birth move using *entire sequence* data
    algPbirth = algParams;
    algPbirth.Debug.MoveType = 1;
    algPbirth.Debug.wstart   = 1;
    algPbirth.Debug.wend     = Dsubset.Ts( curObjID );
    [subPsi,~, ~] = sampleUniqueFeats( subPsi, Dsubset, algPbirth, 0, curObjID );
    subPsi.TransM.K = size( subPsi.F, 2 );
    subPsi.TransM.N = size( subPsi.F, 1 );

    % Every so often, resample ALL shared feat assignments + emit params
    if rr < 3 || mod(rr,5)==0 || rr == length(remObjIDs)
        if isfield( subPsi, 'cache' )
            subPsi = rmfield( subPsi, 'cache' );
        end
        subPsi = sampleSharedFeats( subPsi, Dsubset );
        % Try to do feature death moves
        subPsi = sampleUniqueFeats( subPsi, Dsubset, algPdeath, 0 );

        if isfield( subPsi, 'cache' )
            subPsi = rmfield( subPsi, 'cache' );
        end
        subPsi = sampleStateSeq( subPsi, Dsubset );
        checkPsiFeatureConsistency( subPsi );

        subPsi.ThetaM = subPsi.ThetaM.sampleAllTheta( Dsubset, subPsi.stateSeq );
        subPsi.TransM = subPsi.TransM.sampleAllEta( subPsi.F, subPsi.stateSeq );

        checkPsiFeatureConsistency( subPsi );
    end

    if mod(rr,20)==0
        fprintf( '    seq %4d/%4d  after %4.0f sec | nF= %d\n', rr, length(remObjIDs), toc, size( subPsi.F,2) );
    end
end

% Finally,
%   shuffle so that the data sequence is in the right (original) order
if isfield( subPsi, 'cache' )
    subPsi = rmfield( subPsi, 'cache' );
end
Psi = subPsi;
reorderIDs = [subsetObjIDs(:)' remObjIDs(:)'];
Psi.F = subPsi.F( reorderIDs, : );
Psi.stateSeq = subPsi.stateSeq( reorderIDs );

Psi.TransM = Psi.TransM.sampleAllEta( Psi.F, Psi.stateSeq );
Psi.ThetaM = Psi.ThetaM.sampleAllTheta( data, Psi.stateSeq );

checkPsiFeatureConsistency( Psi );
Psi = reallocateFeatIDs( Psi );
checkPsiFeatureConsistency( Psi );

% ---------------------------------------------------------  Reset stream
curStream = RandStream.getDefaultStream();
curStream.State = entryState;


function aedat = ResampleSpace(aedat, newX, newY)

%{
This function take a structure which has been imported and squeezes or
stretches the events (polarity only for now ...)
into the params newX and newY.
parameters give the array size and one-based, but the address space is zero based.
%}

dbstop if error

if isfield(aedat, 'data') && isfield(aedat.data, 'polarity')
    % There should be a more principled way of finding out the array size,
    % but for now just look at the max addresses ...
    mx = uint64(max(aedat.data.polarity.x));
	temp = uint64(aedat.data.polarity.x) * (newX - 1);
	aedat.data.polarity.x = uint16(temp / mx);

    mx = uint64(max(aedat.data.polarity.y));
	temp = uint64(aedat.data.polarity.y) * (newY - 1);
	aedat.data.polarity.y = uint16(temp / mx);
end


function ind = subsind(grains,subs)

% dermine grains by x,y coordinates
if numel(subs)==2 && all(cellfun(@isnumeric, subs))
  ind = grains.findByLocation([subs{:}]);
  return
elseif numel(subs)==2 && ischar(subs{1}) && strcmpi(subs{1},'id')
  ind = reshape(grains.id2ind(subs{2}),size(subs{2}));
  if any(ind(:)==0)
    error('No data with the specified ids in the data set');
  end
  return
end

ind = true(length(grains),1);

for i = 1:length(subs)

  if ischar(subs{i}) && strcmpi(subs{i},'indexed')

    ind = ind & grains.isIndexed;

  elseif ischar(subs{i}) || iscellstr(subs{i})

    phases = false(length(grains.phaseMap),1);
    mineralsSubs = ensurecell(subs{i});
    phaseNumbers = cellfun(@num2str,num2cell(grains.phaseMap(:)),'Uniformoutput',false);

    for k=1:numel(mineralsSubs)
      phases = phases ...
        | strcmpi(grains.mineralList(:),mineralsSubs{k}) ...
        | strcmpi(phaseNumbers,mineralsSubs{k});
    end

    % if no complete match was found allow also for partial match
    if ~any(phases)
      for k=1:numel(mineralsSubs)
        phases = phases ...
          | strncmpi(grains.mineralList(:),mineralsSubs{k},length(mineralsSubs{k}));
      end
    end

    if ~any(phases)
      disp(' ');
      warning off backtrace
      warning(['There is no such phase "' mineralsSubs{1} '". Maybe you mispelled it?']);
      warning on backtrace
    end

    %miner = ensurecell(subs{i});
    %alt_mineral = cellfun(@num2str,num2cell(grains.phaseMap),'Uniformoutput',false);
    %for k=1:numel(miner)
    %  phases = phases | ~cellfun('isempty',regexpi(grains.mineralList(:),['^' miner{k}])) | ...
    %    strcmpi(alt_mineral(:),miner{k});
    %end
    ind = ind & phases(grains.phaseId(:));

  elseif isa(subs{i},'logical')

    sub = any(subs{i}, find(size(subs{i}')==max(size(ind)),1));

    ind = ind & reshape(sub,size(ind));

  elseif isnumeric(subs{i})

    if any(subs{i} <= 0 | subs{i} > length(grains))
      error('Out of range; index must be a positive integer or logical.')
    end

    ind = subs{i};
    return

  elseif isa(subs{i},'polygon')

    ind = ind & inpolygon(grains,subs{i})';

  elseif isa(subs{i},'crystalSymmetry')

    phaseId = grains.cs2phaseId(subs{i});
    ind = ind & grains.phaseId == phaseId;

  end
end
end


function [ehmm,Gamma,crithist] = ehmmtrain(data,T,ehmm,Gamma,residuals)
%
% Train ehmm using using Variational Framework
%
% INPUTS:
%
% data          observations - a struct with X (time series) and C (classes)
% T             Number of time points for each time series
% ehmm          ehmm structure with options specified in ehmm.train
% Gamma         Initial state courses
% residuals     in case we train on residuals, the value of those.
%
% OUTPUTS
% ehmm           estimated ehmm
% Gamma         estimated p(state | data)
% crithist     historic of Gamma amount of change across iterations
%
% Author: Diego Vidaurre,
%         CFIN, Aarhus University / OHBA, University of Oxford (2021)

setxx_ehmm;
crithist = [];

for cycle = 1:ehmm.train.cyc

%     tt =  799676-10000:799875+10000;
%     figure(2);imagesc(Gamma(tt,:)');colorbar;%xlim([tt(1) tt(end)])
%     W = zeros(length(ehmm.state(1).W.Mu_W),ehmm.train.K);
%     for k = 1:ehmm.train.K+1, W(:,k) = ehmm.state(k).W.Mu_W; end;
%     fit = hmmspectramar(residuals,size(residuals,1),ehmm);
%     plot_hmmspectra (fit,[],[],5);legend
    %figure(5);imagesc(W);colorbar

    %%% E step - state inference
    if ehmm.train.updateGamma
        if cycle > 1 && strcmpi(ehmm.train.stopcriterion,'ChGamma')
            Gamma0 = Gamma;
        end
        if cycle == 1
            [Gamma,~,Xi] = hsinference(data,T,ehmm,residuals,[],XX,Gamma);
        else
            [Gamma,~,Xi] = hsinference(data,T,ehmm,residuals,[],XX,Gamma,Xi);
        end
    else
        Xi = approximateXi_ehmm(Gamma,size(Gamma,1)+ehmm.train.order,ehmm.train.order);
    end

    %%% M STEP

    % Observation model
    if ehmm.train.updateObs
        ehmm = obsupdate_ehmm(Gamma,ehmm,residuals,XX);
    end

    % Transition matrices and initial state
    if ehmm.train.updateP
        ehmm = hsupdate_ehmm(Xi,Gamma,T,ehmm);
    end

    % Stopping conditions and reporting
    if strcmpi(ehmm.train.stopcriterion,'FreeEnergy')
        % computation of free energy is not exact
        crithist(end+1) = sum(evalfreeenergy_ehmm(T,Gamma,Xi,ehmm,residuals,XX));
        if ehmm.train.verbose
            fprintf('cycle %i Approx free energy = %.10g \n',cycle,crithist(end));
        end
        if cycle > 1
            chgFrEn = (crithist(end) - crithist(end-1)) ...
                / abs(crithist(1) - crithist(end));
            if (abs(chgFrEn) < ehmm.train.tol), break; end
        end

    elseif strcmpi(ehmm.train.stopcriterion,'ChGamma')
        if cycle > 1
            crithist(end+1) = 100 * mean(sum(abs(Gamma0 - Gamma),2)/2 );
            if ehmm.train.verbose
                fprintf('cycle %i Gamma change = %.3g %% \n',...
                    cycle,crithist(end));
            end
            if (crithist(end) < ehmm.train.tol), break; end
        else
            crithist(end+1) = 0;
            if ehmm.train.verbose
                fprintf('cycle 1  \n')
            end
        end
    else % log likelihood
        crithist(end+1) = -sum(evalfreeenergy_ehmm(T,Gamma,Xi,ehmm,residuals,...
            XX,[0 1 1 0 0]));
        if ehmm.train.verbose
            fprintf('cycle %i Approx log likelihood = %.10g \n',cycle,crithist(end));
        end
        if cycle > 1
            chL = (crithist(end) - crithist(end-1)) ...
                / abs(crithist(1) - crithist(end));
            if  (abs(chL) < ehmm.train.tol), break; end  % (chL < 0) ||
         end
    end

    % plot state time courses if requested
    if ehmm.train.plotGamma > 0
        figure(100);clf(100);
        if ehmm.train.plotGamma == 1 % continuous data
            plot_Gamma (Gamma,T,1);
        elseif ehmm.train.plotGamma == 2 % full plot
            plot_Gamma (Gamma,T,0);
        end
        drawnow
    end

    if ~ehmm.train.updateGamma
        break % one iteration is enough
    end

end

for k = 1:K
    if isfield(ehmm.state(k),'cache')
        ehmm.state(k) = rmfield(ehmm.state(k),'cache');
    end
end

if ehmm.train.verbose
    str = 'ehmm '; str2 = 'chains';
    if ~isfield(ehmm.train,'distribution') || strcmp(ehmm.train.distribution,'Gaussian')
        fprintf('%s Model: %d %s, %d data samples, order %d \n', ...
            str,K,str2,sum(T),ehmm.train.order);
    elseif strcmp(ehmm.train.distribution,'logistic')
        fprintf('%s Model: %d %s, %d data samples, logistic regression model. \n', ...
            str,K,str2,sum(T));
    end
    if ehmm.train.useMEX==0
        fprintf('MEX file was not used \n')
    else
        fprintf('MEX file was used for acceleration \n')
    end
end

end


function [jaccards,inters,false_pos,false_neg,true_areas] = eval_one(proposals, ground_truth)

    n_objs = length(ground_truth.masks);

    % Store true_areas
    true_areas = zeros(n_objs,1);
    for kk=1:n_objs
        true_areas(kk) = sum(ground_truth.masks{kk}(:));
    end

    % Case when proposals is a subfield
    if isfield(proposals,'proposals')
        proposals = proposals.proposals;
    end

    % Which type of result are we evaluating?
    % - Labels: Superpixel matrix + labels for each proposal
    % - Masks : 3D matrix of boolean masks
    % - Blobs : Boolean masks in a bounding box
    % You can add a case in this file to adapt the evaluation to your type
    % of result
    if isfield(proposals,'labels') || isfield(proposals,'indicator_matrix')
        if ~isfield(proposals,'labels')
            if isa(proposals.superpixels,'uint16')
                superpixels = uint32(proposals.superpixels); % That's RIGOR
            else
                superpixels = uint32(proposals.superpixels'); % That's GOP, transposed
            end

            assert(length(unique(superpixels))==size(proposals.indicator_matrix,1))
            n_cands = size(proposals.indicator_matrix,2);
            labels = cell(n_cands,1);
            for jj=1:n_cands
               labels{jj} = uint32(find(proposals.indicator_matrix(:,jj)'));
            end
        else
            labels = proposals.labels;
            superpixels = proposals.superpixels;
        end

        % Handle all as the multiple-superpixels case
        % [jaccards,inters,false_pos,false_neg,true_areas]
        if ~iscell(superpixels)
            superpixels = {superpixels};
            labels = {labels};
        end

        jaccards  = [];
        inters    = [];
        false_pos = [];
        false_neg = [];

        % Sweep all 'models' (Done for LPO)
        for mm=1:length(superpixels)
            % Overlap superpixels with GT objects
            n_leaves = length(unique(superpixels{mm}));
            superpixels{mm}(~ground_truth.valid_pixels) = 0;
            leave_fp   = zeros(n_objs,n_leaves);  % False positives
            leave_int  = zeros(n_objs,n_leaves);  % Intersection with GT
            for kk=1:n_objs
                tmp = hist(double(superpixels{mm}(:)).*(ground_truth.masks{kk}(:)),(0:n_leaves));
                leave_int(kk,:) = tmp(2:end);
                tmp = hist(double(superpixels{mm}(:)).*(~ground_truth.masks{kk}(:)),(0:n_leaves));
                leave_fp(kk,:) = tmp(2:end);
            end

            % Create matrix padded with zeros to be compatible with mex_eval_labels
            n_proposals  = length(labels{mm});
            n_max_labels = length(unique(superpixels{mm}));
            label_matrix = zeros(n_proposals,n_max_labels);
            for jj=1:n_proposals
                label_matrix(jj,1:length(labels{mm}{jj})) = labels{mm}{jj};
            end

            % Compute fp, fn etc. from these values on the superpixels
            this_inters     = zeros(n_objs,n_proposals);
            this_false_pos  = zeros(n_objs,n_proposals);
            this_false_neg  = zeros(n_objs,n_proposals);
            this_jaccards   = zeros(n_objs,n_proposals);
            if n_proposals>0
                for kk=1:n_objs
                    [this_inters(kk,:),this_false_pos(kk,:)] = mex_eval_labels(leave_int(kk,:),leave_fp(kk,:),label_matrix);
                    this_false_neg(kk,:) = true_areas(kk)-this_inters(kk,:);
                    this_jaccards(kk,:) = this_inters(kk,:)./(this_false_pos(kk,:)+true_areas(kk));
                end
            end
            assert(sum(isnan(this_jaccards(:)))==0)

            % Accumulate
            jaccards  = [jaccards  this_jaccards]; %#ok<AGROW>
            inters    = [inters    this_inters];   %#ok<AGROW>
            false_pos = [false_pos this_false_pos];%#ok<AGROW>
            false_neg = [false_neg this_false_neg];%#ok<AGROW>
        end
    elseif isfield(proposals,'masks')
        % Sweep and compare all masks
        [areas,inters,false_neg] = mex_eval_masks(proposals.masks,ground_truth.masks,ground_truth.valid_pixels);
        false_pos = repmat(areas,n_objs,1)-inters;
        jaccards = inters./(inters+false_pos+false_neg);
        assert(sum(isnan(jaccards(:)))==0)
    elseif isfield(proposals,'hBlobs')
        % Sweep and compare all blobs
        [areas,inters,false_neg] = mex_eval_blobs(proposals.hBlobs,ground_truth.masks,ground_truth.valid_pixels);
        false_pos = repmat(areas,n_objs,1)-inters;
        jaccards = inters./(inters+false_pos+false_neg);
        assert(sum(isnan(jaccards(:)))==0)
    end
end


classdef (InferiorClasses = {?rotation,?quaternion}) orientation < rotation
%
% The class *orientation* represents orientations and misorientations.
%
% Syntax
%   ori = orientation(rot)
%   ori = orientation.byEuler(phi1,Phi,phi2,cs,ss)
%   ori = orientation.byEuler(alpha,beta,gamma,'ZYZ',cs,ss)
%   ori = orientation.byMiller([h k l],[u v w],cs,ss)
%   ori = orientation.byAxisAngle(v,omega,cs,ss)
%   ori = orientation.byMatrix(A,cs)
%   ori = orientation.map(u1,v1,u2,v2,cs)
%   ori = orientation.goss(cs)
%   ori = orientation.cube(cs)
%
% Input
%  rot    - @rotation
%  cs, ss - @crystalSymmetry / @specimenSymmetry
%
% Output
%  ori    - @orientation
%
% Class Properties
%  CS, SS          - @crystalSymmetry / @specimenSymmetry
%  antipodal       - grain exchange symmetry for misorientations
%  phi1, Phi, phi2 - Euler angles
%  i               - inversion
%  a, b, c, d      - quaternion components
%
% See also
% DefinitionAsCoordinateTransform CrystalOperations CrystalReferenceSystem

properties

  CS = crystalSymmetry('1');   % crystal symmetry
  SS = specimenSymmetry('1');  % specimen symmetry or crystal symmetry
  antipodal = false

end

methods

  function o = orientation(varargin)

    % find and remove symmetries
    args  = cellfun(@(s) isa(s,'symmetry'),varargin,'uniformoutput',true);
    sym = varargin(args);
    varargin(args) = [];

    % call rotation constructor
    o = o@rotation(varargin{:});

    if nargin == 0, return;end

    % set symmetry
    if ~isempty(varargin) && isa(varargin{1},'orientation')
      o.CS = varargin{1}.CS;
      o.SS = varargin{1}.SS;
      o.antipodal = varargin{1}.antipodal;
    elseif ~isempty(varargin) && ischar(varargin{1}) && strcmpi(varargin{1},'map')
      if isa(varargin{2},'Miller'), o.CS = varargin{2}.CS; end
      if isa(varargin{3},'Miller'), o.SS = varargin{3}.CS; end
    else
      try %#ok<TRYNC>
        a = get_option(varargin,'axis');
        o.CS = a.CS;
        o.SS = a.CS;
      end
    end
    if ~isempty(sym), o.CS = sym{1};end
    if length(sym) > 1, o.SS = sym{2};end

    % empty constructor -> done
    if isempty(varargin), return; end

    % copy constructor
    switch class(varargin{1})

      case 'char'

        switch lower(varargin{1})

          case 'miller'

            o = orientation.byMiller(varargin{:});

          otherwise

            if exist([varargin{1},'Orientation'],'file')

              % there is a file defining this specific orientation
              o = eval([varargin{1},'Orientation(o.CS,o.SS)']);

            end
        end
    end
    o.antipodal = o.antipodal | check_option(varargin,'antipodal');
    if o.antipodal && o.CS ~= o.SS
      warning('antipodal symmetry is only meaningfull for misorientations between the same phase.')
    end
  end
end

methods (Static = true)

  function ori = nan(varargin)
    s = varargin(cellfun(@isnumeric,varargin));
    q = quaternion.nan(s{:});
    ori = orientation(q,varargin{:});
  end

  function ori = id(varargin)
    id = find(~cellfun(@isnumeric,varargin),1)-1;
    q = quaternion.id(varargin{1:id});
    ori = orientation(q,varargin{id+1:end});
  end

  function ori = rand(varargin)
    q = quaternion.rand(varargin{:});
    ori = orientation(q,varargin{:});
  end

  ori = byMiller(m1,m2,varargin);
  ori = byEuler(phi1,Phi,phi2,varargin);
  ori = byAxisAngle(v,omega,varargin);
  ori = byMatrix(M,varargin);
  ori = map(varargin);
  ori = fit(varargin);
  [ori,interface,options] = load(fname,varargin);

  function ori = cube(varargin)
    ori = orientation.byEuler(0,0,0,varargin{:});
  end

  function ori = cubeND22(varargin)
    ori = orientation.byEuler(22*degree,0,0,varargin{:});
  end

  function ori = cubeND45(varargin)
    ori = orientation.byEuler(45*degree,0,0,varargin{:});
  end

  function ori = cubeRD(varargin)
    ori = orientation.byEuler(0,22*degree,0,varargin{:});
  end

  function ori = goss(varargin)
    ori = orientation.byEuler(0,45*degree,0,varargin{:});
  end

  function ori = copper(varargin)
    %ori = orientation.byEuler(90*degree,35*degree,45*degree,varargin{:});
    ori = orientation.byMiller([1 1 2],[-1 1 -1],varargin{:});
  end

  function ori = copper2(varargin)
    %ori = orientation.byEuler(270*degree,35*degree,45*degree,varargin{:});
    ori = orientation.byMiller([1 1 2],[1 -1 1],varargin{:});
  end

  function ori = SR(varargin)
    ori = orientation.byEuler(53*degree,35*degree,63*degree,varargin{:});
  end

  function ori = SR2(varargin)
    ori = orientation.byEuler(233*degree,35*degree,63*degree,varargin{:});
  end

  function ori = SR3(varargin)
    ori = orientation.byEuler(307*degree,35*degree,27*degree,varargin{:});
  end

  function ori = SR4(varargin)
    ori = orientation.byEuler(127*degree,35*degree,27*degree,varargin{:});
  end

  function ori = brass(varargin)
    ori = orientation.byEuler(35*degree,45*degree,0*degree,varargin{:});
  end

  function ori = brass2(varargin)
    ori = orientation.byEuler(325*degree,45*degree,0*degree,varargin{:});
  end

  function ori = PLage(varargin)
    ori = orientation.byEuler(65*degree,45*degree,0*degree,varargin{:});
  end

  function ori = PLage2(varargin)
    ori = orientation.byEuler(245*degree,45*degree,0*degree,varargin{:});
  end

  function ori = QLage(varargin)
    ori = orientation.byEuler(65*degree,20*degree,0*degree,varargin{:});
  end

  function ori = QLage2(varargin)
    ori = orientation.byEuler(245*degree,20*degree,0*degree,varargin{:});
  end

  function ori = QLage3(varargin)
    ori = orientation.byEuler(115*degree,160*degree,0*degree,varargin{:});
  end

  function ori = QLage4(varargin)
    ori = orientation.byEuler(295*degree,160*degree,0*degree,varargin{:});
  end

  function ori = invGoss(varargin)
    ori = orientation.byEuler(90*degree,45*degree,0*degree,varargin{:});
  end

  function mori = Bain(csGamma,csAlpha)
    %
    % Syntax:
    %   mori = Bain(csGamma,csAlpha)
    %
    % Input
    %  csGamma - parent @crystalSymmetry (cubic fcc)
    %  csAlpha - child @crystalSymmetry (cubic bcc)
    %

    mori = orientation.map(Miller(1,0,0,csGamma),Miller(1,0,0,csAlpha),...
      Miller(0,1,0,csGamma,'uvw'),Miller(0,1,1,csAlpha,'uvw'));
  end

  function mori = KurdjumovSachs(csGamma,csAlpha)
    %
    % Syntax:
    %   mori = KurdjumovSachs(csGamma,csAlpha)
    %
    % Input
    %  csGamma - parent @crystalSymmetry (cubic fcc)
    %  csAlpha - child @crystalSymmetry (cubic bcc)
    %

    mori = orientation.map(Miller(1,1,1,csGamma),Miller(0,1,1,csAlpha),...
      Miller(-1,0,1,csGamma,'uvw'),Miller(-1,-1,1,csAlpha,'uvw'));
  end

  function mori = NishiyamaWassermann(csGamma,csAlpha)
    %
    % Syntax:
    %   mori = NishiyamaWassermann(csGamma,csAlpha)
    %
    % Input
    %  csGamma - parent @crystalSymmetry (cubic fcc)
    %  csAlpha - child @crystalSymmetry (cubic bcc)
    %

    mori = orientation.map(Miller(1,1,1,csGamma),Miller(0,1,1,csAlpha),...
      Miller(1,1,-2,csGamma,'uvw'),Miller(0,-1,1,csAlpha,'uvw'));
  end

  function mori = Pitsch(csGamma,csAlpha)
    %
    % Syntax:
    %   mori = Pitch(csGamma,csAlpha)
    %
    % Input
    %  csGamma - parent @crystalSymmetry (cubic fcc)
    %  csAlpha - child @crystalSymmetry (cubic bcc)
    %

    mori = orientation.map(Miller(0,1,0,csGamma),Miller(1,0,1,csAlpha),...
      Miller(1,0,1,csGamma,'uvw'),Miller(-1,1,1,csAlpha,'uvw'));

    %mori = orientation.map(Miller(1,1,0,csGamma),Miller(1,1,1,csAlpha),...
    %  Miller(0,0,1,csGamma,'uvw'),Miller(-1,1,0,csAlpha,'uvw'));

  end

  function mori = GreningerTrojano(csGamma,csAlpha)
    %
    % Syntax:
    %   mori = orientation.GreningerTrojano(csGamma,csAlpha)
    %
    % Input
    %  csGamma - parent @crystalSymmetry (cubic fcc)
    %  csAlpha - child @crystalSymmetry (cubic bcc)
    %

    mori = inv(orientation.byEuler(2.7*degree,46.6*degree,7.5*degree,csAlpha,csGamma));

    %mori = orientation.map(Miller(1,1,1,csGamma),Miller(1,1,0,csAlpha),...
    %  Miller(5,12,17,csGamma,'uvw'),Miller(17,17,7,csAlpha,'uvw'));

  end

  function mori = Burgers(csParent,csChild)
    %
    % Syntax:
    %   mori = orientation.Burgers(csParent,csChild)
    %
    % Input
    %  csParent - parent @crystalSymmetry
    %  csChild  - child @crystalSymmetry
    %

    if csParent.lattice.isTriHex
      mori = inv(orientation.Burgers(csChild,csParent));
    else
      mori = orientation.map(Miller(1,1,0,csParent),Miller(0,0,0,1,csChild),...
        Miller(-1,1,-1,csParent),Miller(2,-1,-1,0,csChild));
    end

  end

  function mori = Burger(varargin)

    warning('orientation.Burger has been renamed to orientation.Burgers')

    mori = orientation.Burgers(varargin{:});

  end

  function mori = ShojiNishiyama(csGamma,csEps)
    %
    % Syntax:
    %   mori = orientation.ShojiNishiyama(csGamma,csEps)
    %
    % Input
    %  csGamma - parent @crystalSymmetry
    %  csEps  - child @crystalSymmetry
    %

    mori = orientation.map(Miller(1, 1, 1,csGamma),Miller(0, 0, 0, 1,csEps),...
      Miller(1, 0, -1,csGamma,'uvw'),Miller(2, -1, -1, 0,csEps,'uvw'));

  end

end

methods (Static = true, Hidden = true)

  function check_dot(cs)

    % first setting
    cs = crystalSymmetry('m-3m');
    n = 10000000;
    ori1 = orientation.rand(n,cs);
    ori2 = orientation.rand(n,cs);

    tic
    mori = inv(ori1) .* ori2;

    d1 = max(dot_outer(mori,cs.rot,'noSymmetry'),[],2);
    toc

    tic
    d2 = dot(ori1,ori2);
    toc

    norm(d1-d2)

    %second setting
    n = 1000000;
    ori1 = orientation.rand(n,cs,cs);
    ori2 = orientation.rand(cs,cs);

    tic
    d1 = dot(ori1,ori2);
    toc

    tic
    d2 = dot(ori2,ori1);
    toc

    norm(d1-d2)

  end

end

end


function [secInMin, secInHr, secInDay, secInYear] = getSecondsInVariousTimeUnits()
%getSecondsInVariousTimeUnits Summary of this function goes here
%   Detailed explanation goes here
    global options_UseEarthTimeSystem;

    secInMin = 60;
    secInHr = 60*secInMin;
	if(options_UseEarthTimeSystem == true)
        secInDay = 24*secInHr;
        secInYear = 365*secInDay;
    else
        secInDay = 6*secInHr;
        secInYear = 426*secInDay;
	end
end


function [Eft, Varft, lpyt, Eyt, Varyt] = gpsvi_predgrad(gp,x,y,varargin)
%GPSVI_PREDGRAD  Make predictions with SVI GP
%
%  Description
%    [EFT, VARFT] = GPSVI_PREDGRAD(GP, X, Y, XT, OPTIONS)
%    takes a GP structure together with matrix X of training inputs and
%    vector Y of training targets, and evaluates the predictive
%    distribution at test inputs XT. Returns a posterior mean EFT and
%    variance VARFT of latent variables. Each row of X corresponds to one
%    input vector and each row of Y corresponds to one output vector.
%
%    [EFT, VARFT, LPYT] = GPSVI_PREDGRAD(GP, X, Y, XT, 'yt', YT, OPTIONS)
%    returns also logarithm of the predictive density LPYT of the
%    observations YT at test input locations XT. This can be used
%    for example in the cross-validation. Here Y has to be a vector.
%
%    [EFT, VARFT, LPYT, EYT, VARYT] = GPSVI_PREDGRAD(GP, X, Y, XT, OPTIONS)
%    returns also the posterior predictive mean EYT and variance VARYT.
%
%    [EF, VARF, LPY, EY, VARY] = GPSVI_PREDGRAD(GP, X, Y, OPTIONS)
%    evaluates the predictive distribution at training inputs X
%    and logarithm of the predictive density LPY of the training
%    observations Y.
%
%    OPTIONS is optional parameter-value pair
%      predcf - an index vector telling which covariance functions are
%               used for prediction. Default is all (1:gpcfn).
%               See additional information below.
%      yt     - optional observed yt in test points (see below)
%      z      - optional observed quantity in triplet (x_i,y_i,z_i)
%               Some likelihoods may use this. For example, in case of
%               Poisson likelihood we have z_i=E_i, that is, expected value
%               for ith case.
%      zt     - optional observed quantity in triplet (xt_i,yt_i,zt_i)
%               Some likelihoods may use this. For example, in case of
%               Poisson likelihood we have z_i=E_i, that is, the expected
%               value for the ith case.
%
%  See also
%    GPSVI_PRED, SVIGP, DEMO_SVI*
%

% Copyright (c) 2014 Ville Tolvanen

% This software is distributed under the GNU General Public
% License (version 3 or later); please refer to the file
% License.txt, included with the software, for details.

ip=inputParser;
ip.FunctionName = 'GP_PRED';
ip.addRequired('gp',@(x) isstruct(x) || iscell(x));
ip.addRequired('x', @(x) ~isempty(x) && isreal(x) && all(isfinite(x(:))))
ip.addRequired('y', @(x) ~isempty(x) && isreal(x) && all(isfinite(x(:))))
ip.addOptional('xt', [], @(x) isempty(x) || (isreal(x) && all(isfinite(x(:)))))
ip.addParamValue('yt', [], @(x) isreal(x) && all(isfinite(x(:))))
ip.addParamValue('zt', [], @(x) isreal(x) && all(isfinite(x(:))))
ip.addParamValue('z', [], @(x) isreal(x) && all(isfinite(x(:))))
ip.addParamValue('predcf', [], @(x) isempty(x) || ...
                 isvector(x) && isreal(x) && all(isfinite(x)&x>=0))
if numel(varargin)==0 || isnumeric(varargin{1})
  % inputParser should handle this, but it doesn't
  ip.parse(gp, x, y, varargin{:});
else
  ip.parse(gp, x, y, [], varargin{:});
end
xt=ip.Results.xt;
yt=ip.Results.yt;
zt=ip.Results.zt;
z=ip.Results.z;
predcf=ip.Results.predcf;
if isempty(xt)
  xt=x;
  if isempty(yt)
    yt=y;
  end
  if isempty(zt)
    zt=z;
  end
end

tn = size(x,1);
if nargout > 2 && isempty(yt)
  lpyt=[];
end

%     [tmp,tmp,tmp,param]=gpsvi_e(gp_pak(gp),gp,x,y);

% Check if the variational parameters has been set
if ~isfield(gp, 'm') || ~isfield(gp, 'S')
  error('Variational parameters has not been set. Call SVIGP first.')
end

u = gp.X_u;
m=gp.m;
S=gp.S;
if size(u,2) ~= size(x,2)
  % Turn the inducing vector on right direction
  u=u';
end
% Calculate some help matrices
K_uu = gp_trcov(gp, u);     % u x u, noiseles covariance K_uu
K_nu = gp_dcov2(gp, u, [], xt, xt);
K_nu = (K_nu(:,size(xt,1)+1:end))';
%     K_nu = gp_cov(gp,xt,u);       % n x u
[Luu, notpositivedefinite] = chol(K_uu,'lower');
if notpositivedefinite
  Eft=NaN; Varft=NaN; lpyt=NaN; Eyt=NaN; Varyt=NaN;
  return
end

Eft = K_nu*(Luu'\(Luu\m));

if nargout > 1
  [Knn_v, Cnn_v] = gp_trvar(gp,xt,predcf);
  B2=Luu\(K_nu');
  B3=K_uu\(K_nu');

  Varft = Knn_v - sum(B2.*B2)' + sum(B3.*(S*B3))';
end
s2=gp.lik.sigma2;

if nargout > 2
  if isequal(gp.lik.type, 'Gaussian')
    Eyt = Eft;
    Varyt = Varft + Cnn_v - Knn_v;
    if ~isempty(yt)
      lpyt = norm_lpdf(yt, Eyt, sqrt(Varyt));
    end
  else
    if nargout>3
      [lpyt, Eyt, Varyt] = gp.lik.fh.predy(gp.lik, Eft, Varft+s2, yt, zt);
    else
      lpyt = gp.lik.fh.predy(gp.lik, Eft, Varft+s2, yt, zt);
    end
  end
end
end

function [C, Cinv] = gp_dcov2(gp, x1, xv1, x2, xv2, predcf)
% Replacement for GP_DCOV where virtual inputs are given as arguments.

% Split the training data for normal latent input and gradient inputs
x12=x1;
%x11=gp.xv;
x11=xv1;
x3=xv2;

% Derivative observations
[n,m]=size(x1);
[n4,m4]=size(x2);
ncf=length(gp.cf);
if isfield(gp, 'nvd')
  % Only specific dimensions
  ii1=abs(gp.nvd);
else
  % All dimensions
  ii1=1:m;
end
for i1=1:ncf
  gpcf = gp.cf{i1};
  if m==1
    Gset2 = gpcf.fh.ginput4(gpcf, x3, x12);
    Kff = gpcf.fh.cov(gpcf, x12, x2);
    if ~isempty(x11)
      Gset1 = gpcf.fh.ginput4(gpcf, x11, x2);
      Kdd = gpcf.fh.ginput2(gpcf, x11, x3);
      Kdf=Gset1{1};
    else
      Kdd={[]};
      Kdf=[];
    end

    Kfd=Gset2{1};
    C = [Kff Kfd'; Kdf Kdd{1}];

    % Input dimension is >1
  else
    [n,m]=size(x11);
    [n2,m2]=size(x3);

    Kff = gpcf.fh.cov(gpcf, x12, x2);
    Gset2 = gpcf.fh.ginput4(gpcf, x3, x12);

    %Gather matrices from Gset (d k(x1,x2) /d x1)
    Kfd22=cat(2,Gset2{ii1});
    Kdf22=cat(1,Gset2{ii1})';

    if ~isempty(x11)
      Gset1 = gpcf.fh.ginput4(gpcf, x11,x2);
      Kfd=cat(2,Gset1{ii1});
      Kdf=cat(1,Gset1{ii1});
      % both x derivatives, same dimension (to diagonal blocks)
      D = gpcf.fh.ginput2(gpcf, x11, x3);
      % both x derivatives, different dimension (non-diagonal blocks)
      Kdf2 = gpcf.fh.ginput3(gpcf, x11 ,x3);

      % Now build up Kdd m*n x m*n2 matrix, which contains all the
      % both partial derivative" -matrices

      % Add the diagonal matrices
      Kdd=blkdiag(D{1:m});
      % Add the non-diagonal matrices to Kdd
      ii3=0;
      for j=0:m-2
        for i=1+j:m-1
          ii3=ii3+1;
          Kdd(i*n+1:(i+1)*n,j*n2+1:j*n2+n2) = Kdf2{ii3};
          Kdd(j*n+1:j*n+n,i*n2+1:(i+1)*n2) = Kdf2{ii3};
        end
      end
      if isfield(gp, 'nvd')
        % Collect the correct gradient dimensions, i.e. select the blocks
        % that correspond to the input dimensions for which we want the
        % gradients to be monotonic
        Kddtmp=[];
        for ii2=1:length(ii1)
          for ii3=ii2:length(ii1)
            Kddtmp((ii2-1)*n+1:ii2*n, (ii3-1)*n2+1:ii3*n2) = ...
              Kdd((ii1(ii2)-1)*n+1:ii1(ii2)*n,(ii1(ii3)-1)*n2+1:ii1(ii3)*n2);
            if ii2~=ii3
              Kddtmp((ii3-1)*n+1:ii3*n, (ii2-1)*n2+1:ii2*n2) = ...
                Kdd((ii1(ii3)-1)*n+1:ii1(ii3)*n,(ii1(ii2)-1)*n2+1:ii1(ii2)*n2);
            end
          end
        end
        Kdd=Kddtmp;
      end
    else
      Kdf=[];
      Kdd=[];
    end

    % Gather all the matrices into one final matrix K which is the
    % training covariance matrix
    C = [Kff Kdf22; Kdf Kdd];
    %   C = [Kff; Kdf];
  end
  if i1==1
    CC=C;
  else
    CC=CC+C;
  end
end
C=CC;
end


function h = assignColumns(f, colIdx, g)
%ASSIGNCOLUMNS   Extract columns (or rows) of an array-valued TRIGTECH.
%   G = ASSIGNCOLUMNS(F, COLIDX, G) assigns the columns specified by the row
%   vector COLIDX from the FUN F so that F(:, COLIDX) = G. COLIDX need not be
%   increasing in order or unique, but must contain only integers in the range
%   [1, M] (where F has M columns) and satisfy LENGTH(COLIDX) = SIZE(G, 2) or
%   ISEMPTY(G).
%
% See also EXTRACTCOLUMNS, MAT2CELL.

% Copyright 2017 by The University of Oxford and The Chebfun Developers.
% See http://www.chebfun.org/ for Chebfun information.

% g is empty - remove columns:
if ( isempty(g) )
    h = f;
    h.values(:,colIdx) = [];
    h.coeffs(:,colIdx) = [];
    h.isReal(:,colIdx) = [];
    return
end

% Prolong so that f and g have the same length:
if ( length(f) > length(g) )
    g = prolong(g, length(f));
elseif ( length(g) > length(f) )
    f = prolong(f, length(g));
end

% Assign the columns of h.values and h.coeffs:
h = f;
h.values(:, colIdx) = g.values;
h.coeffs(:, colIdx) = g.coeffs;

% Update ishappy:
h.ishappy = f.ishappy && g.ishappy;
h.isReal(colIdx) = g.isReal;

end