classdef (Sealed) Surrogateopt < optim.options.SingleAlgorithm
%SURROGATEOPT Options for SURROGATEOPT function.
%
%   The OPTIM.OPTIONS.SURROGATEOPT class allows the user to create a set
%   of options for the SURROGATEOPT solver. For a list of options that can
%   be set, see the documentation for SURROGATEOPT.
%
%   OPTS = OPTIM.OPTIONS.SURROGATEOPT creates a set of options for
%   SURROGATEOPT with the options set to their default values.
%
%   OPTS = OPTIM.OPTIONS.SURROGATEOPT(PARAM, VAL, ...) creates a set of
%   options for SURROGATEOPT with the named parameters altered with the
%   specified values.
%
%   OPTS = OPTIM.OPTIONS.SURROGATEOPT(OLDOPTS, PARAM, VAL, ...) creates a
%   copy of OLDOPTS with the named parameters altered with the specified
%   values.
%
%   See also OPTIM.OPTIONS.SINGLEALGORITHM, OPTIM.OPTIONS.SOLVEROPTIONS

%   Copyright 2018-2020 The MathWorks, Inc.
    
    properties (Dependent)
    
%DISPLAY Level of display 
        Display
        
%MAXFUNCTIONEVALUATIONS Maximum number of objective function evaluations
        MaxFunctionEvaluations

%MAXTIME Total time (in seconds) allowed for optimization
        MaxTime 

%OBJECTIVELIMIT Minimum objective function value desired
        ObjectiveLimit
        
%OUTPUTFCN Functions that get iterative data and can change options
        OutputFcn
        
%PLOTFCN Plots various measures of progress while the algorithm executes
        PlotFcn
        
%USEPARALLEL Compute the objective function of particles in parallel
        UseParallel

%INITIALPOINTS Specify X and/or Fval
        InitialPoints

%MinSurrogatePoints Specify the number of points to use for initial surrogate (on
%top of InitialPoints.X
        MinSurrogatePoints

%MINSAMPLEDISTANCE Adaptive samples with minimum distance (from existing
%points) less than this tolerance will not be considered.
        MinSampleDistance

%CheckpointFile File name where checkpoint data is saved.
        CheckpointFile

%CONSTRAINTTOLERANCE Tolerance on the constraints
        ConstraintTolerance
        
    end        

    % Hidden properties
    properties (Hidden, Dependent)

%ObjectiveImprovementThreshold Tolerance used for restart of the search
        ObjectiveImprovementThreshold        
        
%MAXSTALLITERATIONS
        MaxStallIterations

%AdaptiveSampleBatchSize controls how many samples to get from surrogate before next update
        AdaptiveSampleBatchSize
    end
    
    properties (Hidden, Dependent)
        Verbosity
        PlotFcns
        OutputFcns
    end
    
    properties (Hidden, Access = protected)
        
        %OPTIONSSTORE Contains the option values and meta-data for the class
        %
        OptionsStore = createOptionsStore;
    end
    
    properties (Hidden)
        
        %SOLVERNAME Name of the solver that the options are intended for
        SolverName = 'surrogateopt';
    end
    
    properties (SetAccess = private, GetAccess = private)
        
        % Version number for the optim.options.Surrogateopt class.
        % We do not change the Version property (base class).
        surrogateoptVersion = 3
    end
    
    properties(Hidden, Constant, GetAccess=public)
        
        % Constant, globally visible metadata about this class.
        % This data is used to spec the options in this class for internal clients
        % such as: tab-complete, and the options validation
        % Properties
        PropertyMetaInfo = genPropInfo();    
    end
    
        % Load old objects
        methods (Static = true)
            function obj = loadobj(obj)
                
                if obj.surrogateoptVersion < 3
                    % Change the default in the OptionsStore
                    % @todo: Any thing to do for compatibility?
                    obj.OptionsStore.Defaults.AdaptiveSampleBatchSize = 1;
                    obj.OptionsStore.SetByUser.AdaptiveSampleBatchSize = false;
                    obj.OptionsStore.Options.AdaptiveSampleBatchSize = 1;
                end
            end
        end
    
% -------------------------------------------------------

    methods (Hidden)
        
        function obj = Surrogateopt(varargin)
%SURROGATEOPT Options for SURROGATEOPT
%
%   The OPTIM.OPTIONS.SURROGATEOPT class allows the user to create a set
%   of options for the SURROGATEOPT solver. For a list of options that can
%   be set, see the documentation for SURROGATEOPT.
%
%   OPTS = OPTIM.OPTIONS.SURROGATEOPT creates a set of options for
%   SURROGATEOPT with the options set to their default values.
%
%   OPTS = OPTIM.OPTIONS.SURROGATEOPT(PARAM, VAL, ...) creates a set of
%   options for SURROGATEOPT with the named parameters altered with the
%   specified values.
%
%   OPTS = OPTIM.OPTIONS.SURROGATEOPT(OLDOPTS, PARAM, VAL, ...) creates a
%   copy of OLDOPTS with the named parameters altered with the specified
%   values.
%
%   See also OPTIM.OPTIONS.SINGLEALGORITHM, OPTIM.OPTIONS.SOLVEROPTIONS
            
            % Call the superclass constructor
            obj = obj@optim.options.SingleAlgorithm(varargin{:});
            
        end
        
    end
    
    % Set/get methods
    methods
        
        function obj = set.AdaptiveSampleBatchSize(obj, value)
            obj = setProperty(obj, 'AdaptiveSampleBatchSize', value);
        end      
        function val = get.AdaptiveSampleBatchSize(obj)
            val = obj.OptionsStore.Options.AdaptiveSampleBatchSize;
        end
        
        function obj = set.ObjectiveImprovementThreshold(obj, value)
            obj = setProperty(obj, 'ObjectiveImprovementThreshold', value);
        end      
        function val = get.ObjectiveImprovementThreshold(obj)
            val = obj.OptionsStore.Options.ObjectiveImprovementThreshold;
        end        
        
        function obj = set.ConstraintTolerance(obj, value)
            obj = setProperty(obj, 'ConstraintTolerance', value);
        end        
        function val = get.ConstraintTolerance(obj)
            val = obj.OptionsStore.Options.ConstraintTolerance;
        end

        function obj = set.MinSampleDistance(obj, value)
            obj = setProperty(obj, 'MinSampleDistance', value);
        end        
        function val = get.MinSampleDistance(obj)
            val = obj.OptionsStore.Options.MinSampleDistance;
        end
        
        function obj = set.Display(obj, value)
            if strcmpi(value, 'testing')
                % Set Display to the undocumented value, 'testing'.
                obj = setPropertyNoChecks(obj, 'Display', 'testing');
            else
                % Pass the possible values that the Display option can take via
                % the fourth input of setProperty.
                obj = setProperty(obj, 'Display', value);
            end          
            
            % Define verbosity here (Later we can use options structure)
            switch value
                case {'off','none'}
                    verbosity_ = 0;
                case 'final'
                    verbosity_ = 1;
                case 'iter'
                    verbosity_ = 2;
                case 'testing'
                    verbosity_ = 3;
                otherwise
                    verbosity_ = 1;
            end
            obj = setPropertyNoChecks(obj, 'Verbosity', verbosity_);
        end
        
        function val = get.Display(obj)
            val = obj.OptionsStore.Options.Display;
        end
        
        function obj = set.MaxFunctionEvaluations(obj, value)
            obj = setProperty(obj, 'MaxFunctionEvaluations', value);
        end         
        function val = get.MaxFunctionEvaluations(obj)
            val = obj.OptionsStore.Options.MaxFunctionEvaluations;
        end                
        
        function obj = set.InitialPoints(obj, value)
            obj = setProperty(obj, 'InitialPoints', value);
        end        
        function val = get.InitialPoints(obj)
            val = obj.OptionsStore.Options.InitialPoints;
        end
        
        function obj = set.MinSurrogatePoints(obj, value)
            obj = setProperty(obj, 'MinSurrogatePoints', value);
        end        
        function val = get.MinSurrogatePoints(obj)
            val = obj.OptionsStore.Options.MinSurrogatePoints;
        end        
        
        function obj = set.MaxStallIterations(obj, value)
            obj = setProperty(obj, 'MaxStallIterations', value);
        end           
        function val = get.MaxStallIterations(obj)
            val = obj.OptionsStore.Options.MaxStallIterations;
        end         
        
        function obj = set.MaxTime(obj, value)
            obj = setProperty(obj, 'MaxTime',  value);
        end
        function val = get.MaxTime(obj)
            val = obj.OptionsStore.Options.MaxTime;
        end
        
        function obj = set.ObjectiveLimit(obj, value)
            obj = setProperty(obj, 'ObjectiveLimit', value);
        end        
        function val = get.ObjectiveLimit(obj)
            val = obj.OptionsStore.Options.ObjectiveLimit;
        end
        
        function obj = set.OutputFcn(obj, value)
            obj = setProperty(obj, 'OutputFcn', value);
            obj = setPropertyNoChecks(obj, 'OutputFcns', value);
        end        
        function val = get.OutputFcn(obj)
            val = obj.OptionsStore.Options.OutputFcn;
        end        
        function val = get.OutputFcns(obj)
            val = obj.OptionsStore.Options.OutputFcns;
        end
        
        function obj = set.PlotFcn(obj, value)
            obj = setProperty(obj, 'PlotFcn',value);
            obj = setPropertyNoChecks(obj, 'PlotFcns', value);
        end          
        function val = get.PlotFcn(obj)
            val = obj.OptionsStore.Options.PlotFcn;
        end           
        function val = get.PlotFcns(obj)
            val = obj.OptionsStore.Options.PlotFcns;
        end

        function obj = set.UseParallel(obj, value)
            obj = setProperty(obj, 'UseParallel', value);
        end
        function val = get.UseParallel(obj)
            val = obj.OptionsStore.Options.UseParallel;
        end        

        function obj = set.CheckpointFile(obj, value)
            obj = setProperty(obj, 'CheckpointFile', value);
        end
        function val = get.CheckpointFile(obj)
            val = obj.OptionsStore.Options.CheckpointFile;
        end        
        
    end
    
end

function OS = createOptionsStore
%CREATEOPTIONSSTORE Create the OptionsStore
%
%   OS = createOptionsStore creates the OptionsStore structure. This
%   structure contains the options and meta-data for option display, e.g.
%   data determining whether an option has been set by the user. This
%   function is only called when the class is first instantiated to create
%   the OptionsStore structure in its default state. Subsequent
%   instantiations of this class pick up the default OptionsStore from the
%   MCOS class definition.
%
%   Class authors must create a structure containing all the options in a
%   field of OS called Defaults. This structure must then be passed to the
%   optim.options.generateSingleAlgorithmOptionsStore function to create
%   the full OptionsStore. 

% Define the option defaults 


OS.Defaults.ObjectiveLimit = -Inf;
OS.Defaults.InitialPoints = [];
OS.Defaults.Display = 'final';
OS.Defaults.MinSampleDistance = 1e-3;
OS.Defaults.MinSurrogatePoints     = 'max(20,2*numberOfVariables)';
OS.Defaults.MaxFunctionEvaluations = 'max(200,50*numberOfVariables)';
OS.Defaults.MaxTime = Inf;
OS.Defaults.OutputFcn = [];
OS.Defaults.PlotFcn = @optimplotfvalconstr;
OS.Defaults.UseParallel = false;
OS.Defaults.CheckpointFile = [];
OS.Defaults.ConstraintTolerance = 1e-3;

% Hidden
OS.Defaults.ObjectiveImprovementThreshold = 1e-6;
OS.Defaults.MaxStallIterations = '10*numberOfVariables';
OS.Defaults.AdaptiveSampleBatchSize = 1;
OS.Defaults.Verbosity = 1;
OS.Defaults.PlotFcns = OS.Defaults.PlotFcn;
OS.Defaults.OutputFcns = OS.Defaults.OutputFcn;
% Call the package function to generate the OptionsStore
OS = optim.options.generateSingleAlgorithmOptionsStore(OS);
end

function propInfo = genPropInfo()
% Helper function to generate constant property metadata for the options.

import optim.options.meta.Factory
import optim.options.meta.category
import optim.options.meta.label

propInfo.ObjectiveLimit = Factory.ObjectiveLimitType;
propInfo.InitialPoints = Factory.matrixStructType(label('InitPoints'),category('Algorithm'));
propInfo.Display = Factory.displayType({'none','off','iter','final'});
propInfo.MinSampleDistance = Factory.toleranceType(label('MinSampleDist'));
propInfo.MinSurrogatePoints = Factory.integerType([0 Inf],label('MinSurrogatePts'),category('Algorithm'));
propInfo.MaxFunctionEvaluations = Factory.MaxFunEvalsType;
propInfo.MaxTime = Factory.MaxTimeType;
propInfo.OutputFcn = Factory.OutputFcnType;
propInfo.PlotFcn = Factory.plotFcnType({'surrogateoptplot', ...
                                        'optimplotfvalconstr', ... 
                                        'optimplotfval', ... 
                                        'optimplotconstrviolation', ... 
                                        'optimplotx'});
propInfo.UseParallel = Factory.logicalWithEnumType(label('UseParallel'),category('Algorithm'),{'always','never'});
propInfo.CheckpointFile = Factory.fileType('.mat',label('Checkpoint'),category('Algorithm'));
propInfo.ConstraintTolerance = Factory.ConstraintToleranceType;

% Hidden options - no need for labels, categories, etc. Reuse where possible.
propInfo.AdaptiveSampleBatchSize = Factory.resourceLimitType('');
propInfo.ObjectiveImprovementThreshold = Factory.numericType('scalar',[0 1],[true true],'','');
propInfo.MaxStallIterations = Factory.resourceLimitType('');
propInfo.Verbosity = Factory.integerType([0 3],'','');
propInfo.PlotFcns = propInfo.PlotFcn;
propInfo.OutputFcns = propInfo.OutputFcn;
end
