%
% SourcePlusOneHingeL (extends MulticlassRLS)
%
% Given some priors (multiclass classifier), trains
% binary model for a new class and makes predictions
% from priors and new trained model.
%
% 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 SourcePlusOneHingeL < GenericClassifier
    properties
    end

    methods
        function obj = SourcePlusOneHingeL(parameters)
        % Constructs SourcePlusOneHingeL
        %
        % parameters.priors             -- prior (N) models; should implement predict method
        % parameters.new_class_label    -- label of a new class (numeric)
        %
            if nargin < 1
                parameters = struct;
            end

            obj@GenericClassifier(parameters);
        end

        function train_model(obj, hyperparameters, data_obj)
            t_data_obj.train_y = data_obj.train_y;
            t_data_obj.train_y(data_obj.train_y~=obj.parameters.new_class_label) = 0;
            t_data_obj.train_x = data_obj.train_x;

            parameters.binary_classifier_class = 'Libsvm';
            obj.model.new_classifier = MulticlassOneVsRest(parameters);
            obj.model.new_classifier.train_model(hyperparameters, t_data_obj);
        end

        function [pred, margins] = predict(obj, test_x)
            new_class_map = obj.model.new_classifier.model.class_map;

            [junk, margins1] = obj.parameters.source.predict(test_x);
            [junk, margins2] = obj.model.new_classifier.predict(test_x);
            margins = [margins1 ; margins2(new_class_map~=0, :)];

            [junk, class_idx] = max(margins, [], 1);
            new_class_map = [obj.parameters.source.model.class_map; ...
                new_class_map(new_class_map~=0)];
            pred = new_class_map(class_idx);
            obj.model.class_map = new_class_map;
        end
    end
end
