classdef optimizationFunctionEvaluator < OptimFunctions
    % This class manages objective and constraint function evaluations for
    % optimization solvers. Provide objective/constraint functions
    % different form.
    %
    % funObj = optimizationFunctionEvaluator(param=value,...)
    %
    % Objective function(s):
    % 1.Objective: A function handle to get objective function value
    %  fval = Objective(x)
    % 2.ObjectiveGradient: A function handle for objective gradient
    %  grad = ObjectiveGradient(x)
    % 3.ObjectiveAndGradient A function handle for objective value and
    % gradient (when (1) and (2) are combined in one function)
    %  [fval, grad] = ObjectiveAndGradient(x)
    %
    % Objective can also be provided using the cell-array style syntax.
    % ObjectiveFunction can be one of the following:
    %   Objective or
    %   ObjectiveAndGradient or 
    %   {Objective, ObjectiveGradient} or
    %   {Objective, ObjectiveGradient, ObjectiveHessian}*
    % *This is the only way to provide Hessian (used only by
    % trust-region-reflective algorithm).
    %
    % Constraint function(s):
    % 1.Constraint: A function handle to get constraints
    %  [cineq, ceq] = Constraint(x)
    % 2.ConstraintGradient: A function handle for constraint gradients
    %  [cineqGrad, ceqGrad] = ConstraintGradient(x)
    % 3.ConstraintAndGradient A function handle for constraint value and
    % gradient (when (1) and (2) are combined in one function)
    % [cineq, ceq, cineqGrad, ceqGrad] = ConstraintAndGradient(x)
    %
    % Constraints can also be provided using the cell-array style syntax.
    % ConstraintFunction can be one of the following:
    %   Constraint or
    %   ConstraintAndGradient or 
    %   {Constraint, ConstraintGradient}
    %
    % Additional parameters can be provided using the
    % 'AdditionalParameters' parameter.
    %
    % You must call the method setup(param=value) before using the
    % constructed object. Parameters to setup are described below.
    %
    % Gradient-based solver settings:
    %   NeedGrads = true (default is false)
    %   X0 is a required input if finite-difference is used for gradients
    %   LB, UB are bounds on X used for finite-difference; assumed unbounded otherwise
    %   Solver: '' (default), 'fmincon', 'fseminf'. Solver is used to
    %           format error messages in the objective/constraint inputs.
    %   Algorithm: '' (default), 'sqp', 'trust-region-reflective', 'interior-point'
    %   options: Options related to gradient availability, parallel,
    %   vectorized, etc. Full list of property-value is listed here:
    %     'DiffMaxChange',Inf, ...
    %     'DiffMinChange',0, ...
    %     'FinDiffRelStep', [], ...
    %     'FinDiffType','forward', ...
    %     'FunValCheck','off', ...
    %     'GradConstr','off', ...
    %     'GradObj','off', ...
    %     'DerivativeCheck', 'off', ...
    %     'TypicalX','ones(numberOfVariables,1)', ...
    %     'Hessian', [], ...
    %     'UseParallel',false, ...
    %     'Vectorized','off'
    %
    % Function scaling can be provided to the object that will be used to
    % scale the function values and gradients automatically. Refer to
    % optimizationFunctionScaling class to construct a scaling object and
    % provide to optimizationFunctionEvaluator vis setScale() method.
    %
    % Evaluate objective and/or constraint function value and/or gradients
    % using these functions (refer to help for these functions):
    %
    % fval = funObj.objective(x) or,
    % [fval, funObj] = funObj.objective(x) % When ObjectiveAndGradient is provided
    % grad = objgrad(x)
    % hess = funObj.objhessian(x) % Only when ObjectiveAndGradient is provided
    %
    % [cin, ceq] = funObj.constraint(x) or,
    % [cin, ceq, funObj] = funObj.constraint(x) % When ConstraintAndGradient is provided
    % [cinGrad, ceqGrad] = constraintgrad(x)
    %
    % Gradients of both objecive and constraints can be evaluated in one
    % method call. Note that output arguments must be initialized and 
    % (fval, cin, ceq) must be provided if  finite-difference is used. 
    % [grad,JacCineqTrans,JacCeqTrans,numEvals,evalOK] = ... 
    %            funObj.computeGradAndJac(X,fval,cin,ceq,grad,... 
    %            JacCineqTrans,JacCeqTrans,findiffOnly)
    %
    % When analytical gradients are available the calling syntax can be:
    % [grad,JacCineqTrans,JacCeqTrans] = funObj.computeGradAndJac(X)
    %
    % Providing initialized outputs help with keeping the size of the
    % outputs in all the gradient method calls.


    %   Copyright 2023 The MathWorks, Inc.

    properties(SetAccess=private,Hidden)
        cleanupObj % PCT cleanup
    end

    methods
        function self = optimizationFunctionEvaluator(PV)
            % Pass parameter-values to this class for different functions

            arguments
                PV.ObjectiveFunction
                PV.ConstraintFunction

                PV.Objective
                PV.ObjectiveGradient
                PV.ObjectiveAndGradient

                PV.Constraint
                PV.ConstraintGradient
                PV.ConstraintAndGradient

                PV.AdditionalParameters
                PV.PreFunctionEvalFcn
                PV.PostFunctionEvalFcn
                PV.OnErrorCallbackFcns
                PV.PreOptimizationFcn
                PV.PostOptimizationFcn
            end
            args = namedargs2cell(PV);
            self = self@OptimFunctions(args{:});

        end
    end

    methods
        function self = setupParallel(self)
            % For parallel finite difference (if needed) we need to send the function
            % handles now to the workers. This avoids sending the function handles in
            % every iteration of the solver. The output from 'setOptimFcnHandleOnWorkers'
            % is a onCleanup object that will perform cleanup task on the workers.

            ProblemdefOptions = optimget(self.options, 'ProblemdefOptions',self.defaultopt,self.optimgetFlag);
            FromSolve = false;
            if ~isempty(ProblemdefOptions) && isfield(ProblemdefOptions, 'FromSolve')
                FromSolve = ProblemdefOptions.FromSolve;
            end
            self.cleanupObj = setOptimFcnHandleOnWorkers(self.options.UseParallel,...
                self.FunFcn,self.ConFcn,FromSolve);

        end

    end

end