scripts/matlab/LDA.m
function [subspaceData]=LDA(X,classNo,varargin)
% [subspaceData]=LDA(X,classNo)
%
%   LDA method for learning a subspace that seeks to maximize the Fisher 
%       seperability measure. 'X' is d x n matrix, where d is the feature
%       vector size, and n is the number of instaces. 'classNo' is a n x 1
%       vector that indicates which class/subject each instance belongs to.
%
%   Optional parameters:

%       'EnergyRetain' - percentage of variance (0.0 to 1.0) to retain in the initial  
%                           PCA step. (default = 0.98)
%
%       'do_direct' - Whether or not to perform Direct LDA (default = false)
%
%       'FixedRetain' - Keep a fixed number of eigenvectors in the initial PCA.
%                           If used, then specify number to keep (e.g. 100)
%
%
%       'DominantEig' - whether or not to use the dominant eigenvector method. If 
%                           the d >> n,  then this should be set to true. 
%                           (default = true)
%
%       'SkipPCA'     - whether of not to skip the PCA step. If Sw is believed to 
%                           be non-singular then PCA step can be safely skipped. 
%                           (default = false)                
%
%       'ScaleW'      - the factor by which to scale the within-class scatter matrix. 
%                           This controls the importance of the between and within 
%                           class scatter to each other in the fisher criterion
%
%   The output 'subspaceData' is a struct that contains the following important fields:
%       subspaceData.mean - d x 1 vector containing the mean of the training data 
%       subspaceData.W    - d x k LDA projection matrix, where k is the number of 
%                                subspace dimensions
%
%
%   Algorithm based on Fukanaga's LDA method. This code was orginally written by Zhifeng Li,
%       and has since been modified and improved by Brendan Klare.
%
%

    useFixedEnergy = false;
    doDominantEig = true;
    doPCA = true;
    useFixedEig = false;
    energyPercentage = .98;
    ScaleW = 1;
    doDLDA = false;
    do_null = false;

    cnt = 1;
    while cnt < length(varargin)
        switch varargin{cnt}
            case 'EnergyRetain'
                useFixedEnergy = true;
                energyPercentage = varargin{cnt+1};
            case 'FixedRetain'
                useFixedEig = true;
                fixedEig = varargin{cnt+1};
            case 'DominantEig'
                doDominantEig = varargin{cnt+1};
            case 'SkipPCA'
                doPCA = ~varargin{cnt+1};
            case 'ScaleW'
                ScaleW = varargin{cnt+1};
            case 'do_direct'
                do_null = varargin{cnt+1};
            otherwise
                fprintf('Error, unknown argument %s\n',varargin{cnt});
                return
        end
        cnt = cnt + 2;
    end


    [FeatureLength SampleNumber]=size(X);
    % ClassNum=round(SampleNumber/2);

    [a1 a2 classNo] = unique(classNo); 
    ClassNum = max(classNo);

    %Calculate eigenspace from X
    if doPCA
        if ~doDominantEig
            [eigenvalues, eigenvectors, Mean_Vector]=PCA2(X);
        else
            [eigenvalues, eigenvectors, Mean_Vector, V1]=PCA(X);
        end

        if useFixedEnergy
            d1 = cumsum(eigenvalues)./sum(eigenvalues);
            [a nEigs] = max(d1 > energyPercentage);   
        elseif useFixedEig
            nEigs = fixedEig;
        else
            nEigs = min(FeatureLength,SampleNumber - 1);
        end

        %Select eigenvectors
        Select_eigenvectors=eigenvectors(:,1:nEigs);
        eigenvalues = eigenvalues(1:nEigs);

        %Project the sample data on to the eigenvectors
        W=Select_eigenvectors'*(X-repmat(Mean_Vector,1,SampleNumber));
    else
        W = X;
        Mean_Vector = zeros(size(X,1),1);
        nEigs = size(X,1);
        Select_eigenvectors = eye(size(X,1));
        eigenvalues = ones(size(X,1),1);
    end  

    %Caculate the centers for each class
    ClassCenters = zeros(nEigs,ClassNum);
    for i = 1:ClassNum
        ClassCenters(:,i) = mean(W(:,classNo == i),2);
    end

    for i = 1:ClassNum,
        W(:,classNo == i) = W(:,classNo == i) - repmat(ClassCenters(:,i),1,sum(classNo == i));
    end

    if ScaleW ~= 1
        W = ScaleW .* W;
    end

    [W_val, W_vec, W_m]=PCA2(W);

    if ~do_null
        nDim2 = min(nEigs,SampleNumber - ClassNum);
        SW_val=W_val(1:nDim2);
        SW_vec=W_vec(:,1:nDim2);
        SW_vec=SW_vec./(repmat(SW_val',[size(SW_vec,1) 1]).^0.5);
    else
        nDim2 = nEigs;
        SW_val = W_val;
        SW_vec = W_vec;
        if nEigs > SampleNumber - ClassNum
            SW_val(SampleNumber-ClassNum+1:end) = SW_val(SampleNumber-ClassNum)/2;
        end

        d1 = cumsum(W_val)/sum(W_val);
        [d1 start_idx] = max(d1 > .1);

        SW_vec = SW_vec(:,start_idx:end);
        SW_val = SW_val(start_idx:end);
        nDim2 = size(SW_vec,2);
        SW_vec=SW_vec./(repmat(SW_val',[size(SW_vec,1) 1]).^0.15);
    end


    m = mean(W,2);
    M=repmat(m(:),1,ClassNum);
    mean2 = m;
    B=SW_vec'*(ClassCenters-M);
    % Between_Class_Matrix=B*B';


    [B_val,B_vec,B_m]=PCA2(B);

    nDim3 = min(ClassNum-1,nDim2);
    SB_vec=B_vec(:,1:nDim3);

    subspaceData.mean = Mean_Vector(:);    
    subspaceData.mean2 = mean2(:);
    subspaceData.W1 = Select_eigenvectors;
    subspaceData.D1 = eigenvalues;
    subspaceData.W2 = SW_vec;
    subspaceData.W3 = SB_vec;
    subspaceData.W = (subspaceData.W3' * subspaceData.W2' * subspaceData.W1')';
