%
% This code is part of the supplementary material to the CVPR 2013 paper
% "From N to N+1: Multiclass Transfer Incremental Learning",
% Ilja Kuzborskij, Francesco Orabona and Barbara Caputo
%
% Copyright (C) 2012-2013, Ilja Kuzborskij
%
% 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/>.
%
% Contact the author: ilja.kuzborskij [at] gmail.com
%
classdef MultiKT < GenericClassifier

    properties
        prior_pred_test = [];
    end

    methods

        function obj = MultiKT(parameters)
            obj@GenericClassifier(parameters);
        end

        function [pred, margins_zita] = predict(obj, test_x)
        % This code is excerpt from the supplementary material to the CVPR 2010 paper
        % "Safety in Numbers: Learning Categories from Few Examples with Multi
        % Model Knowledge Trasfer", T. Tommasi, F. Orabona, B. Caputo.

            cue = cellfun(@(x) struct('p_test', x), test_x, 'UniformOutput', 0); % compatibility
            if ~isfield(obj.parameters, 'Ktest')
                Ktest = GenericClassifier.compute_mean_kernel_combinations_eff_(obj.model.hyperparameters, obj.model.XX, test_x)';
            else
                Ktest = obj.parameters.Ktest;
            end
            [dummy, vals_multi]=k_predict_K(Ktest, obj.model);

            % Predicting using priors on the test set
            % [junk, margins_test] = obj.parameters.priors.predict(test_x);

            margins_zita = vals_multi+(obj.parameters.priors_margins_test * obj.model.prior_betas')';
            pred=sign(margins_zita);
        end

        function train_model(obj, hyperparameters, data_obj)
        % This code is excerpt from the supplementary material to the CVPR 2010 paper
        % "Safety in Numbers: Learning Categories from Few Examples with Multi
        % Model Knowledge Trasfer", T. Tommasi, F. Orabona, B. Caputo.
        %
        % parameters.kernel_param
        % parameters.c_val
        % parameters.train_x - cell array with row-sample cues or simply row-sample array
        % parameters.train_y - same format as in train_x, but labels
        % parameters.test_x - same format as in train_x
        % parameters.test_y - same format as in train_y

        % parameters.max_kernel_memory - optional, default = 50 (MB)


            unpack_locals(hyperparameters);
            unpack_locals(data_obj);

            prior_pred_train = {};

            %[junk, margins_train] = obj.parameters.priors.predict(data_obj.train_x);

            for i=1:size(obj.parameters.priors_margins_train, 2)
                prior_pred_train{i} = obj.parameters.priors_margins_train(:, i);
            end

            if ~isfield(obj.parameters, 'Ktrain')
                K = GenericClassifier.compute_mean_kernel_combinations_eff_(hyperparameters, data_obj.train_x, data_obj.train_x);
            else
                K = obj.parameters.Ktrain;
            end
            model=k_init(@compute_kernel, hyperparameters, hyperparameters.c_val);
            model.K = K;
            model.Y=data_obj.train_y';
            model.S=[1:numel(data_obj.train_y)];
            model.XX=data_obj.train_x;

            [obj.model, prior_betas, LOSS_cl2] = kls_train_adapt_zita_Multi(model, prior_pred_train);
            prior_betas

            obj.model.prior_betas = prior_betas;
            obj.model.hyperparameters = hyperparameters;
        end
    end

    methods (Static)

        function weights = estimate_prior_weights(parameters, data_obj)
            model = MultiKT(parameters);
            %hyper = model.find_best_model_hyperparams(data_obj);
            hyper.svm_best_C = 1;
            hyper.svm_best_gamma = 1;
            model.train_model(catstruct(hyper, parameters), data_obj);
            weights = model.model.prior_betas;
        end
    end
end
