% Function description %%%%%%%%%%%%%
    % This fuction is used to implement FWM for the prediction of essential genes or other classfiers.
    % The AUC score is used as evaluation function in genetic algorithm.
% Parameter description %%%%%%%%%%%%%
    % TrainingSet, TrainingPredictSet, PredictSet
        % These three parameters must have the same format (Cell array or Matrix).The first column in matrix indicates the species number. The second column indicates the gene number. The third column indicates essentiality number.
    % TXFlag
        % TXFlag determine the complexity of datasets.
        % TXFlag=1 The dataset is a matrix. Each feature column need correspond to a element in PXFlag array.
        % TXFlag=2 The dataset is a Cell array including multiple matrices, each matrix¡¯s features have the same data type(e.g., continuous or disperse), and each matrix corresponds to a element in PXFlag array. 
        % The data structure also requires that each row with the same location in all matrices corresponds to the same sample.
        % TXFlag=3 The dataset is a Cell array including multiple matrices£¬different TXFlag=2, The matrix¡¯ samples may be organized any way you want, also permit some samples absence£®Each matrix corresponds to a element in PXFlag array.
    % PXFlag
        % PXFlag=1 indicates the feature Variable Measurement Level is Scale (continuous). 
        % PXFlag=2  indicates the Measurement Level is Ordinal(disperse). 
        % PXFlag=3 indicates the Measurement Level is Nominal (disperse, it may include multiple values concurrently).
    % WFlag=1
        % WFlag=1 indicates the model NBM, WFlag=2 indicates the model FWM.
% Result Output %%%%%%%%%%%%%
    % PredictEvaluate if PredictSet is testing set, this result indicates the AUC score else, this result indicates 0.5.
    % PredictScore Output the the probability that genes belongs to essential gene set.

%  Main function %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [PredictEvaluate,PredictScore,w]=FWMPredicting(TrainingSet,TrainingPredictSet,PredictSet,TXFlag,PXFlag,WFlag)
if TXFlag == 1
    TrainingSet(:,1)=[]; 
    TrainingPredictSet(:,1)=[];
    PredictSet(:,1)=[];  
    nP=size(TrainingSet,2)-2;
    nColC=0;  % Counting the number of continuous features
    nColD=0;  % Counting the number of disperse features
    for i=1:nP
        if PXFlag(i) == 1   % Recording all continuous features
            nColC=nColC+1;
            T{1}(:,nColC)=TrainingSet(:,i+2);
            TP{1}(:,nColC)=TrainingPredictSet(:,i+2);
            P{1}(:,nColC)=PredictSet(:,i+2);            
        else                % Recording all disperse features
            nColD=nColD+1;
            T{2}(:,nColD)=TrainingSet(:,i+2);
            TP{2}(:,nColD)=TrainingPredictSet(:,i+2);
            P{2}(:,nColD)=PredictSet(:,i+2);            
        end    
    end
    PXFlag=[];
    nT=0;
    if nColC > 0
        T{1}=[TrainingSet(:,2) T{1}];   % Adding essentiality before continuous features
        TP{1}=[TrainingPredictSet(:,2) TP{1}];
        P{1}=[PredictSet(:,2) P{1}];  
        PXFlag(1)=1;
        nT=nT+1;
    end
    if nColD > 0
        T{2}=[TrainingSet(:,2) T{2}];   % Adding essentiality before disperse features
        TP{2}=[TrainingPredictSet(:,2) TP{2}];
        P{2}=[PredictSet(:,2) P{2}]; 
        PXFlag(2)=2;
        nT=nT+1;
        if nColC == 0
            T{1}=T{2}; T(2)=[];
            TP{1}=TP{2}; TP(2)=[];
            P{1}=P{2}; P(2)=[];
            PXFlag(1)=PXFlag(2); PXFlag(2)=[];
        end
    end
    for i=1:nT
        TNum{i}=TrainingSet(:,1:2);
        TPNum{i}=TrainingPredictSet(:,1:2);
        PNum{i}=PredictSet(:,1:2);
    end
else
    nT=length(TrainingSet);
    nP=0;
    for i=1:nT
        if PXFlag(i) <= 2
            nP=nP+size(TrainingSet{i},2)-3;
        else
            nP=nP+1;
        end
        T{i}=TrainingSet{i};T{i}(:,1)=[]; TNum{i}=T{i}(:,1:2); T{i}(:,1)=[];
        TP{i}=TrainingPredictSet{i};TP{i}(:,1)=[]; TPNum{i}=TP{i}(:,1:2); TP{i}(:,1)=[];
        P{i}=PredictSet{i}; P{i}(:,1)=[]; PNum{i}=P{i}(:,1:2); P{i}(:,1)=[];
    end    
end
%%% Obtaining the prior probability score of training and prediction set, 
%%% and initializing the features probability score of training and prediction set.
[AT,PriorScoreT]=GetPriorScore(TNum,TXFlag);
[ATP,PriorScoreTP]=GetPriorScore(TPNum,TXFlag);
[AP,PriorScoreP]=GetPriorScore(PNum,TXFlag);
mAT=size(AT,1); mATP=size(ATP,1);mAP=size(AP,1);
TScore=zeros(mAT,nP+2); TScore(:,1:2)=AT;  % Initializing the features probability score of training set.
TPScore=zeros(mATP,nP+2); TPScore(:,1:2)=ATP;  % initializing the features probability score of training-prediction set.
PScore=zeros(mAP,nP+2); PScore(:,1:2)=AP;  % initializing the features probability score of prediction set.
%%% Obtaining the features probability score matrices of training and prediction set.   
nPi=0;
for i=1:nT
    [TScore1,TPScore1]=GetScore(T{i},TP{i},PXFlag(i));
    [TScore1,PScore1]=GetScore(T{i},P{i},PXFlag(i));        
    TScore2=[TNum{i}(:,1) TScore1];
    TPScore2=[TPNum{i}(:,1) TPScore1];
    PScore2=[PNum{i}(:,1) PScore1];
    TScore=InsertScore(TScore,TScore2,nPi,TXFlag); % Combining different groups of feature score matrices.
    TPScore=InsertScore(TPScore,TPScore2,nPi,TXFlag); 
    PScore=InsertScore(PScore,PScore2,nPi,TXFlag);
    nPi=nPi+size(TScore1,2)-1;
end  
TargetNumT=TScore(:,1); TScore(:,1)=[];    
TargetT=TScore(:,1);    TScore(:,1)=[];    
TargetNumTP=TPScore(:,1); TPScore(:,1)=[];    
TargetTP=TPScore(:,1);    TPScore(:,1)=[];    
TargetNumP=PScore(:,1); PScore(:,1)=[]; 
TargetP=PScore(:,1);    PScore(:,1)=[];
%%% Estimating the weight vector by genetic algorithm 
nP=size(TPScore,2)
if WFlag == 1
    w=ones(nP,1);
else
    lb=zeros(nP,1);
    options = gaoptimset('Generations',200,'TolFun',1e-20); 
    % Using training-prediction set as optimization iteration target.
    [w,fmin]=ga(@(w)myfit(w,TPScore,PriorScoreTP,TargetTP),nP,[],[],[],[],lb,[],[],options);    
    w, w=w';
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FScore=PScore*w;
PredictScore(:,1)=TargetNumP;
PredictScore(:,2)=TargetP;
PredictScore(:,3)=1./(1+exp(-PriorScoreT-FScore));

[PredictEvaluate,Curve] = ROC(PredictScore(:,3),PredictScore(:,2),1,0);
PredictEvaluate
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Obtaining features probability Bayes score of training and prediction set.  
% FlagX=1 indicates the feature Variable Measurement Level is Scale (continuous). 
% FlagX=2 indicates the Measurement Level is Ordinal(disperse).
% FlagX=3 indicates the Measurement Level is Nominal (disperse, it may include multiple values concurrently).
% FlagX=3 just can calculate a feature every time.
%  Sub function %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [TrainingScore,PredictScore]=GetScore(TrainingSet,PredictSet,FlagX)
[mT,nT]=size(TrainingSet);[mP,nP]=size(PredictSet);
[A1,A2,D]=GroupX2({TrainingSet},0.5); % Split the dataset in two parts---essential and non-essential gene sets.
[m0,n0]=size(D{1});[m1,n1]=size(D{2});
TrainingScore(:,1)=TrainingSet(:,1);
PredictScore(:,1)=PredictSet(:,1);
if FlagX == 1         %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Continuous features    
    for j=2:nT
        Score0=ksdensity(A1{j,1},TrainingSet(:,j)); Score0=Score0+0.1; % Using essential gene set to train kernel density function, and estimating the kernel density of training set.
        Score1=ksdensity(A1{j,2},TrainingSet(:,j)); Score1=Score1+0.1; % Using non-essential gene set to train kernel density function, and estimating the kernel density of training set.
        Score2(:,j-1)=log(Score1./(Score0));  % Calculating feature scores with log likelihood ratio.
        Score0=ksdensity(A1{j,1},PredictSet(:,j));  Score0=Score0+0.1; % Using essential gene set to train kernel density function, and estimating the kernel density of prediction set.
        Score1=ksdensity(A1{j,2},PredictSet(:,j));  Score1=Score1+0.1; % Using non-essential gene set to train kernel density function, and estimating the kernel density of prediction set.
        Score3(:,j-1)=log(Score1./(Score0));  % Calculating feature scores with log likelihood ratio.
    end
    TrainingScore(:,2:nT)=Score2; 
    PredictScore(:,2:nT)=Score3; 
elseif FlagX == 2     %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Disperse ordinal features
    MinJ(1)=0; MaxJ(1)=0;   % Recording the maximum and minimum values of training and prediction set.
    ZeroBegin=0; 
    Prior=1;
    for j=2:nT
        MinJ(j)=min([TrainingSet(:,j);PredictSet(:,j)]); % Obtain the minimum integer for each column feature.
        if MinJ(j) == 0
            ZeroBegin=1;
            TrainingSet(:,j)=TrainingSet(:,j)+1; % The integer need at least 1.
            PredictSet(:,j)=PredictSet(:,j)+1;
        end
        MaxJ(j)=max([TrainingSet(:,j);PredictSet(:,j)]);  % Obtain the maximum integer for each column feature.
    end
    if ZeroBegin == 1;
        [A1,A2,D]=GroupX2({TrainingSet},0.5); % If training and prediction set changed, we need regroup the dataset
    end
    MaxD=max(MaxJ);   % Obtaining the maximum and minimum values of training and prediction set.
    DisNum=[1:MaxD]'; % Enumerating all the integer appearing in training and prediction set.    
    Distribution0=zeros(MaxD,nT);   % Initializing the arrry used to record the number of each integer appear in non-essential genes
    Distribution1=zeros(MaxD,nT);   % Initializing the arrry used to record the number of each integer appear in essential genes
    DistributeScore=ones(MaxD,nT);  % Initializing the score arrry of each integer
    Distribution0(:,1)=DisNum; Distribution1(:,1)=DisNum; DistributeScore(:,1)=DisNum;
    for i=1:m0
        for j=2:n0
            Distribution0(D{1}(i,j),j)=Distribution0(D{1}(i,j),j)+1; % Recording the number of each integer appear in non-essential genes
        end
    end
    for i=1:m1
        for j=2:n1
            Distribution1(D{2}(i,j),j)=Distribution1(D{2}(i,j),j)+1; % Recording the number of each integer appear in essential genes
        end
    end
    for j=2:nT
        Contain=[]; iC=0;  % Recording the integer and score included in dataset, related to coherent ingeters.
        Lack=[];    iL=0;  % Recording the integer and score absented in dataset, related to coherent ingeters.
        for i=1:MaxJ(j)
            if Distribution0(i,j) > 0 || Distribution1(i,j) > 0 % The integer included in dataset.
                DistributeScore(i,j)=((Distribution1(i,j)+1)/(m1+2))/((Distribution0(i,j)+1)/(m0+2)); % Likelihood ratio
                iC=iC+1;
                Contain(iC,1)=i;
                Contain(iC,2)=DistributeScore(i,j);
            else                                                % The integer absented in dataset
                iL=iL+1;
                Lack(iL,1)=i;
            end
        end
        if iL > 0
            if iC > 1
                Lack(:,2)=interp1(Contain(:,1),Contain(:,2),Lack(:,1),'linear','extrap'); % Employing linear interpolation to add integer scores not present in the dataset
                for k=1:iL
                    DistributeScore(Lack(k),j)=Lack(k,2);
                end
            else
                for k=1:iL
                    DistributeScore(Lack(k),j)=Prior;
                end
            end             
        end        
        for i=1:mT
            TrainingScore(i,j)=log(DistributeScore(TrainingSet(i,j),j)); % Applying ineger scores to training set
        end
        for i=1:mP
            PredictScore(i,j)=log(DistributeScore(PredictSet(i,j),j));   % Applying ineger scores to prediction set
        end
    end   
else                            %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Disperse nominal features
    MaxD=max(max(TrainingSet));  % Obtaining the maximum and minimum values of training and prediction set.
    DisNum=[1:MaxD]';            % Enumerating all the integer appearing in training and prediction set.  
    E1=m1*ones(MaxD,1);          % Recording the number of essential genes
    E0=m0*ones(MaxD,1);          % Recording the number of non-essential genes
    x1=zeros(MaxD,1);            % Initializing the arrry used to record the number of each integer appear in essential genes
    x0=zeros(MaxD,1);            % Initializing the arrry used to record the number of each integer appear in non-essential genes
    Prior=1;                     % Given prior score to not present integer.
    s0=Prior*ones(MaxD,1);       % Initializing the score arrry of each integer
    Distribution=[DisNum x1 E1 x0 E0 s0];
    for i=1:m1
        for j=2:n1
            if D{2}(i,j)>0
                Distribution(D{2}(i,j),2)=Distribution(D{2}(i,j),2)+1; % Recording the number of each integer appear in essential genes.
            end
        end
    end
    for i=1:m0
        for j=2:n0
            if D{1}(i,j)>0
                Distribution(D{1}(i,j),4)=Distribution(D{1}(i,j),4)+1; % Recording the number of each integer appear in non-essential genes
            end
        end
    end     
    for i=1:MaxD 
        Distribution(i,6)=((Distribution(i,2)+1)/(m1+2))/((Distribution(i,4)+1)/(m0+2));    % Likelihood ratio    
    end    
    for i=1:mT
        for j=2:nT
            if TrainingSet(i,j)>0 && TrainingSet(i,j)<=MaxD            
                TrainingSetScore(i,j-1)=Distribution(TrainingSet(i,j),6); % Applying ineger scores to training set
            else
                TrainingSetScore(i,j-1)=Prior;
            end
        end
    end    
    %[MaxP,iMaxP]=max(abs(TrainingSetScore-Prior),[],2);
    [MaxP,iMaxP]=max(TrainingSetScore,[],2);       % Fetching the maximum score refered to integer and using it as unique integer measuring this gene.   
    for i=1:mT
        TrainingScore(i,2)=log(TrainingSetScore(i,iMaxP(i))); % Logarithm
    end    
    for i=1:mP
        for j=2:nP
            if PredictSet(i,j)>0 && PredictSet(i,j)<=MaxD            
                PredictSetScore(i,j-1)=Distribution(PredictSet(i,j),6);   % Applying ineger scores to prediction set
            else
                PredictSetScore(i,j-1)=Prior;
            end
        end
    end    
    %[MaxP,iMaxP]=max(abs(PredictSetScore-Prior),[],2);
    [MaxP,iMaxP]=max(PredictSetScore,[],2);       % Fetching the maximum score refered to integer and using it as unique integer measuring this gene.
    for i=1:mP
        PredictScore(i,2)=log(PredictSetScore(i,iMaxP(i)));   % Logarithm
    end
end

% Obtain the prior score of training set.PriorScore=log(number of essential geens£¯number of non-essential geens)
% if gene sets vary among features, we should gain the union set of all gene sets, then counting the number of essential and non-essential genes.
% The output A records the union set of all gene sets ,including the essentiality.
%  Sub function %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [A,PriorScore]=GetPriorScore(TNum,TXFlag)
n=length(TNum);
A=TNum{1};
m=size(A,1);
if TXFlag > 2
    for i=2:n
        m2=size(TNum{i},1);
        for j=1:m2
            flag=1;
            for k=1:m
                if A(k,1) == TNum{i}(j,1)
                    flag=0;
                    break;
                end
            end
            if flag == 1
                m=m+1;
                A(m,:)=TNum{i}(j,:);
            end
        end
    end
end
m1=0;m0=0;
for i=1:m
    if A(i,2) == 1
        m1=m1+1;
    else
        m0=m0+1;
    end
end
PriorScore=log(m1/m0);

% Inserting the feature score column of TScore2 to the back of nPi location of TScore1.
%  Sub function %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function Score=InsertScore(TScore1,TScore2,nPi,TXFlag)
%Score=TScore;
[m1,n1]=size(TScore1);
[m2,n2]=size(TScore2);
if TXFlag <= 2
    Score=TScore1;
    TScore2(:,1:2)=[];
    Score(:,(nPi+3):(nPi+n2))=TScore2;
else 
    for i=1:m1
        CScore(TScore1(i,1),:)=TScore1(i,:);
    end
    for i=1:m2
        CScore(TScore2(i,1),(nPi+3):(nPi+n2))=TScore2(i,3:n2);
    end
    m3=size(CScore,1);
    m=1;
    for i=1:m3
        if CScore(i,1) > 0
            Score(m,:)=CScore(i,:);
            m=m+1;
        end
    end
end

% Fitness function
%  Sub function %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function f=myfit(w,LogScore,PriorScore,target)
w=w';
FScore=LogScore*w;
score=1./(1+exp(-PriorScore-FScore));
[auc, curve] = ROC(score, target,1, 0);
f=1-auc;

%  Sub function %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [auc, curve] = ROC(score, target, Lp, Ln)  
% 	This function is to calculat the ordinats of points of ROC curve and the area under ROC curve(AUC). 
%   This program was described in Fawcett's paper "ROC Graphs: notes and practical considerations for researchers".  
% Output: 
%    curve: N*3 matrix.  
%            the 1st column is FP  
%            the 2nd column is TP 
%            the 3rd column is score 
%            note: the last row, etc.the last point is [1,1,0]. if output of this function
%            is applied to roc_av.m to calculate average curve of roc,it should be delete 
%    auc:   scale number, area under ROC curve. 
% Input parameters: 
%    score:  output of classifier. high socre denote the pattern is more likely to be POSITIVE pattern. 
%    target: classlabel of each pattern. 
%    Lp:     label of POSITIVE pattern.   
%    Ln:     label of NEGATIVE pattern. 
len = length(score);                % number of patterns 
if len ~= length(target) 
	error('The length of tow input vectors should be equal\n'); 
end 
P = 0;    % number of Positive pattern 
N = 0;    % number of Negative pattern 
for i = 1:len 
	if target(i) == Lp 
		P = P + 1; 
	elseif target(i) == Ln 
		N = N + 1; 
	else 
		%error('Wrong target value'); 
        'Wrong target value'
        P=floor(len/2);N=len-P;
        break;
	end 
end 
% sort "L"  in decending order by scores 
score = score(:); 
target = target(:); 
L = [score target]; 
L = sortrows(L,1); 
index = len:-1:1; 
index = index';     %' 
L = L(index,:);  
fp = 0;   fp_pre = 0;   % number of False Positive pattern 
tp = 0;   tp_pre = 0;   % number of True Positive pattern. 
score_pre = -10000; 
curve = []; 
auc = 0; 
 
for i = 1:len 
	if L(i,1) ~= score_pre 
		curve = [curve; [fp/N, tp/P, L(i,1)]]; 
		auc = auc + trapezoid(fp, fp_pre, tp, tp_pre); 	 
		score_pre = L(i,1); 		 
		fp_pre = fp; 
		tp_pre = tp; 
	end 	 
	if L(i,2) == Lp 
		tp = tp + 1; 
	else 
		fp = fp + 1; 
	end 
end 
curve = [curve; [1,1,0]]; 
auc = auc / P / N; 
auc = auc + trapezoid(1, fp_pre/N, 1, tp_pre/P); 

% calculat the area of trapezoid 
%  Sub function %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function area = trapezoid(x1,x2,y1,y2) 
a = abs(x1-x2); 
b = abs(y1+y2); 
area = a * b / 2; 

%  Sub function %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [A1,A2,D]=GroupX2(B,group0)
% B=B{i}, Each element represent a matrix£¬group0 is the separation in the first column of every matrices.
% the output of A1is A1{i,group}, A2 is Num[i,group].
nB=length(B);
nG=length(group0);group0(nG+1)=2*group0(nG)+(1e+10);
flag=0;
for i=1:nB
    [m,n]=size(B{i});
    for s=1:n
        for k=1:nG+1
            A1{s+flag,k}=[];
        end
    end
    A2(flag+1:flag+n,:)=zeros(n,nG+1); 
    Row=ones(1,nG+1);C=[];
    for j=1:m
        for k=1:nG+1              
            if B{i}(j,1)<=group0(k)                
                A2(flag+1:flag+n,k)=A2(flag+1:flag+n,k)+1;                
                C{k}(Row(k),:)=B{i}(j,:);
                Row(k)=Row(k)+1;
                break;
            end
        end        
    end
    CLength=length(C);
    D(i,:)=C;
    for k1=1:CLength
        if ~isempty(C{k1})
            for r=1:n
                A1{r+flag,k1}=C{k1}(:,r);
            end
        end
    end
    flag=flag+n;    
end


