% Sample class, definining the L1LR composite problem (see [FV20]), where
%
% [FV20] M. I. Florea and S. A. Vorobyov,
%        "A generalized accelerated composite gradient method:
%        Uniting Nesterov's fast gradient method and FISTA,"
%        IEEE Trans. Signal Process., vol. 68, pp. 3033-3048, 2020.
%
% See also "README.txt" for more information.

%   Author: Mihai I. Florea, Aalto University
%   Version: 10 June 2021

% You can design a custom class based on this sample to model the composite
% problem that you want to solve. The ACGM implementation in "ACGM_demo.m",
% Section 3, will work as-is with the custom class, provided it is properly
% initialized in Section 1.

% ACGM uses the oracle functions: f_val, f_grad, Psi_val, prox
% All must be implemented. See "Problem.m" for syntax.

% The class must inherit the Problem class and must implement its abstract
% methods to ensure the functionality of the ACGM implementation.
classdef Problem_L1LR < Problem

    % Problem specific properties.
    % Replace them with the problem parameters of your application.
    properties
        A;    % data matrix of size m by n
        m, n; % dimensions of the data matrix
        y;    % classification label vector
        l1;   % \lambda_1, see [FV20]
        l2;   % \lambda_2, see [FV20]
    end

    % Methods pertaining to the oracle functions.
    % Replace them with your oracle functions,
    % while maintaining the function syntax of each.
    methods

        function f = f_val(obj, x)
            Ax   = obj.A * x;
            lsxp = sum(log(1 + exp(Ax)));
            f    = -sum(obj.y(:) .* Ax(:)) + lsxp + 0.5 * obj.l2 * sum(x(:).^2);
        end

        function grad = f_grad(obj, x)
            emAx = exp(-obj.A * x);
            c    = -obj.y + 1 ./ (1 + emAx);
            grad = obj.A' * c + obj.l2 * x;
        end

        function Psi = Psi_val(obj, x)
            Psi = obj.l1 * sum(abs(x(:))) + 0.5 * obj.l2 * sum(x(:).^2);
        end

        function y = prox(obj, x, tau)
            scale = 1.0 ./ (1 + tau * obj.l2);
            y = scale * ( sign(x) .* max(abs(x) - (obj.l1 * tau), 0) );
        end
    end
end
