classdef RegressionSVM < ...
        classreg.learning.regr.FullRegressionModel & classreg.learning.regr.CompactRegressionSVM
%RegressionSVM Support Vector Machine model for regression.
%   RegressionSVM is a SVM model for regression. This model can predict
%   response for new data. This model also stores data used for training
%   and can compute resubstitution predictions.
%
%   An object of this class cannot be created by calling the constructor.
%   Use FITRSVM to create a RegressionSVM object by fitting an SVM model
%   to training data.
%
%   This class is derived from CompactRegressionSVM.
%
%   RegressionSVM properties:
%       NumObservations       - Number of observations.
%       X                     - Matrix of predictors used to train this model.
%       Y                     - Observed response used to train this model.
%       W                     - Weights of observations used to train this model.
%       ModelParameters       - SVM parameters.
%       PredictorNames        - Names of predictors used for this model.
%       ExpandedPredictorNames - Names of expanded predictors.
%       ResponseName          - Name of the response variable.
%       ResponseTransform     - Transformation applied to predicted regression response.
%       Alpha                 - Coefficients obtained by solving the dual problem.
%       Beta                  - Coefficients for the primal linear problem.
%       Bias                  - Bias term.
%       KernelParameters      - Kernel parameters.
%       Mu                    - Predictor means.
%       Sigma                 - Predictor standard deviations.
%       SupportVectors        - Support vectors.
%       BoxConstraints        - Box constraints.
%       Epsilon               - Half of the width of the epsilon-insensitive band.
%       CacheInfo             - Cache information.
%       ConvergenceInfo       - Convergence information.
%       Gradient              - Gradient values in the training data.
%       IsSupportVector       - Indices of support vectors in the training data.
%       NumIterations         - Number of iterations taken by optimization.
%       OutlierFraction       - Expected fraction of outliers in the training data.
%       ShrinkagePeriod       - Number of iterations between reductions of the active set.
%       Solver                - Name of the used solver.
%       RowsUsed              - Logical index for rows used in fit. 
%
%   RegressionSVM methods:
%       compact               - Compact this model.
%       crossval              - Cross-validate this model.
%       discardSupportVectors - Discard support vectors for linear SVM.
%       incrementalLearner    - Return an incremental regression linear model.
%       loss                  - Regression loss.
%       partialDependence     - Partial Dependence values.
%       plotPartialDependence - Plot Partial Dependence.
%       predict               - Predicted response of this model.
%       resubLoss             - Resubstitution regression loss.
%       resubPredict          - Resubstitution predicted response.
%       resume                - Resume training.
%
%   Example: Train an SVM regression model
%       load hald
%       svr = fitrsvm(ingredients,heat,'epsilon',0.1,'Standardize',true);
%
%   See also fitrsvm, classreg.learning.regr.CompactRegressionSVM.
    
%   Copyright 2015-2023 The MathWorks, Inc.


     properties(SetAccess=protected,GetAccess=public,Dependent=true)
        %BOXCONSTRAINTS Box constraints.
        %   The BoxConstraints property is a vector with NumObservations
        %   elements. The absolute value of the dual coefficient alpha for
        %   observation I cannot exceed BoxConstraints(I).
        %
        %   See also fitrsvm, RegressionSVM, Alpha, NumObservations.
        BoxConstraints;
        
        %CACHEINFO Cache information.
        %   The CacheInfo property is a struct with 2 fields:
        %       Size      - Size of memory used for caching entries of the
        %                   Gram matrix in megabytes.
        %       Algorithm - Name of the algorithm used for removing entries
        %                   from the cache when its capacity is exceeded.
        %
        %   See also fitrsvm, RegressionSVM.
        CacheInfo;
        
        %CONVERGENCEINFO Convergence information.
        %   The ConvergenceInfo property is a struct with 10 fields:
        %       Converged                - true if optimization converged
        %                                  to the desired tolerance and
        %                                  false otherwise.
        %       ReasonForConvergence     - Name of the criterion used to
        %                                  detect convergence.
        %       Gap                      - Value of the attained  
        %                                  feasibility gap between the dual
        %                                  and primal objectives.
        %       GapTolerance             - Tolerance for the feasibility
        %                                  gap.
        %       DeltaGradient            - Value of the attained gradient
        %                                  difference between upper and
        %                                  lower violators.
        %       DeltaGradientTolerance   - Tolerance for gradient
        %                                  difference between upper and
        %                                  lower violators.
        %       LargestKKTViolation      - Value of the attained largest
        %                                  (by magnitude) Karush-Kuhn-Tucker
        %                                  (KKT) violation.
        %       KKTTolerance             - Tolerance for largest KKT
        %                                  violation.
        %       History                  - Struct with 6 fields:
        %           NumIterations            * Array of iteration indices
        %                                      at which convergence
        %                                      criteria were recorded.
        %           Gap                      * Gap values at these iterations.
        %           DeltaGradient            * DeltaGradient values at these
        %                                      iterations.
        %           LargestKKTViolation      * LargestKKTViolation values
        %                                      at these iterations.
        %           NumSupportVectors        * Numbers of support vectors
        %                                      at these iterations.
        %           Objective                * Objective values at these
        %                                      iterations.
        %       Objective                - Value of the dual objective.
        %
        %   See also fitrsvm, RegressionSVM.
        ConvergenceInfo;
        
        %EPSILON Half of the width of the epsilon-insensitive band.
        %   The Epsilon property is a scalar specifying half the width of
        %   the epsilon-insensitive band.
        %
        %   See also fitrsvm, RegressionSVM, NumObservations.
        Epsilon;
        
        %GRADIENT Gradient values in the training data.
        %   The Gradient property is a vector with 2*NumObservations
        %   elements. Element I of this vector is the value of the gradient
        %   for Alpha_up at observation I at the end of the optimization.
        %   Element I+NumObservations of this vector is the value of the
        %   gradient for Alpha_low at observation I at the end of the
        %   optimization, where Alpha_up is the Alpha coefficient
        %   corresponding to the upper boundary of the band, and Alpha_low
        %   is the Alpha coefficient corresponding to the lower boundary of
        %   the band.
        %
        %   See also fitrsvm, RegressionSVM, NumObservations.
        Gradient;
        
        %ISSUPPORTVECTOR Indices of support vectors in the training data.
        %   The IsSupportVector property is a logical vector with
        %   NumObservations elements. IsSupportVector(I) is true if
        %   observation I is a support vector and false otherwise.
        %
        %   See also fitrsvm, RegressionSVM, NumObservations.
        IsSupportVector;
        
       
        %NUMITERATIONS Number of iterations taken by optimization.
        %   The NumIterations property is an integer showing how many
        %   iterations were performed by optimization.
        %
        %   See also fitrsvm, RegressionSVM.
        NumIterations;
        
        %OUTLIERFRACTION Expected fraction of outliers in the training data.
        %   The OutlierFraction property is a numeric scalar between 0 and 1
        %   specifying the expected fraction of outliers in the training data.
        %
        %   See also fitrsvm, RegressionSVM.
        OutlierFraction;
        
        %SHRINKAGEPERIOD Number of iterations between reductions of the active set.
        %   The ShrinkagePeriod property is a non-negative integer
        %   specifying how often the active set was shrunk during
        %   optimization.
        %
        %   See also fitrsvm, RegressionSVM.
        ShrinkagePeriod;
        
        %SOLVER Name of the used solver.
        %   The Solver property is a string specifying the algorithm used
        %   to solve the SVM problem.
        %
        %   See also fitrsvm, RegressionSVM.
        Solver;
     end
    
     methods
        function a = get.BoxConstraints(this)
            bad = this.HasNaNX;
            if isempty(bad)
                a = this.Impl.C;
            else
                a = NaN(size(this.PrivX,1),1,'like',this.Impl.C);
                a(~bad) = this.Impl.C;
            end
        end
        
        function a = get.CacheInfo(this)
            a = this.Impl.CacheInfo;
        end
        
        function a = get.ConvergenceInfo(this)
            a = this.Impl.ConvergenceInfo;
            if isfield(a,'OutlierHistory')
                a = rmfield(a,'OutlierHistory');
            end
            if isfield(a,'ChangeSetHistory')
                a = rmfield(a,'ChangeSetHistory');
            end
        end
        
        function a = get.OutlierFraction(this)
            a = this.Impl.FractionToExclude;
        end
        
        function a = get.Epsilon(this)
            a = this.ModelParams.Epsilon;
        end

        function a = get.Gradient(this)
            bad = this.HasNaNX;
            if isempty(bad)
                a = this.Impl.Gradient;
            else
                a = NaN(2*size(this.PrivX,1),1,'like',this.Impl.Gradient);
                bad = repmat(bad,2,1);
                a(~bad) = this.Impl.Gradient;
            end
        end
        
        function a = get.Solver(this)
            a = this.ModelParams.Solver;
        end
                
        function a = get.NumIterations(this)
            a = this.Impl.NumIterations;
        end
                
        function a = get.IsSupportVector(this)
            bad = this.HasNaNX;
            if isempty(bad)
                a = this.Impl.IsSupportVector;
            else
                a = false(size(this.PrivX,1),1,'like',this.Impl.IsSupportVector);
                a(~bad) = this.Impl.IsSupportVector;
            end
        end
        
        function a = get.ShrinkagePeriod(this)
            a = this.Impl.Shrinkage.Period;
        end
     end
    
    methods(Hidden)
        function this = RegressionSVM(X,Y,W,modelParams,dataSummary,responseTransform)
            if nargin~=6 || ischar(W)
                 error(message('stats:RegressionSVM:RegressionSVM:DoNotUseConstructor'));
            end
            
            %Check whether X is sparse or complex here instead of in
            %FullRegressionModel/prepareData, because the base class may be
            %extended to support sparse matrix in the future.
            internal.stats.checkSupportedNumeric('X',X,true, false, false, true);
            this = this@classreg.learning.regr.FullRegressionModel(...
                X,Y,W,modelParams,dataSummary,responseTransform);
            this = this@classreg.learning.regr.CompactRegressionSVM(...
                dataSummary,responseTransform,[],[]);
            
            % Compute default response values
            this.DefaultResponse.Median = classreg.learning.internal.wnanmedian(this.PrivY,this.W);
            this.DefaultResponse.Mean   = classreg.learning.internal.wnanmean(this.PrivY,this.W);

            % Find rows with NaNs and don't use them for training.
            [this,X,Y,W,nanX] = processNaNX(this,'stats:ClassificationSVM:ClassificationSVM:NoDataAfterNaNsRemoved');
            
            % Check for alphas. The size of alphas matches the size of X. This
            % is ensured by SVMParams.
            alphas = this.ModelParams.Alpha;
            if ~isempty(alphas)
                alphas(nanX) = [];
            end
            
            s = [];
            %Set Epsilon value if it's not provided 
            %Setting Epsilon needs to be done here instead of SVMPARAMS. In
            %the case of cross-validation, different training sets set
            %Epsilon based on its own response.
            if isempty(this.ModelParams.Epsilon)
                s = iqr(Y)/1.349;
                if s == 0
                    s = ones("like", this.PrivY);
                end
                this.ModelParams.Epsilon = s/10;
            end
            
            %Set box constraint value for Gaussian kernel if it's not
            %provided. Setting box constraint for Gaussian kernel
            %needs to be done here instead of SVMPARAMS. In the
            %case of cross-validation, different training sets set this
            %value based on it's own response.
            if isempty (this.ModelParams.BoxConstraint) &&...
                    strcmpi(this.ModelParams.KernelFunction, 'gaussian')
                if isempty(s)
                    s = iqr(Y)/1.349;
                    if s == 0
                        s = ones("like", this.PrivY);
                    end
                end
                this.ModelParams.BoxConstraint = s;

                if ~isempty(alphas)
                    if any(abs(alphas) > this.ModelParams.BoxConstraint)
                        maxAlpha = max(abs(alphas));
                        alphas = alphas*this.ModelParams.BoxConstraint/maxAlpha;
                    end
                end
            end
            
            doclass = 0; %regression

            if isa(this.PrivX, "gpuArray") || isa(this.PrivY, "gpuArray")
                iCheckGpuArrayLimitations(this.PrivX, this.ModelParams);
            end
            
            this.Impl = classreg.learning.impl.SVMImpl.make(...
                X,Y,W,...
                alphas,this.ModelParams.ClipAlphas,...
                this.ModelParams.KernelFunction,...
                this.ModelParams.KernelPolynomialOrder,[],...
                this.ModelParams.KernelScale,this.ModelParams.KernelOffset,...
                this.ModelParams.StandardizeData,...
                doclass,...
                this.ModelParams.Solver,...
                this.ModelParams.BoxConstraint,...
                this.ModelParams.Nu,...
                this.ModelParams.IterationLimit,...
                this.ModelParams.KKTTolerance,...
                this.ModelParams.GapTolerance,...
                this.ModelParams.DeltaGradientTolerance,...
                this.ModelParams.CacheSize,...
                this.ModelParams.CachingMethod,...
                this.ModelParams.ShrinkagePeriod,...
                this.ModelParams.OutlierFraction,...
                this.ModelParams.VerbosityLevel,...
                this.ModelParams.NumPrint,...
                this.ModelParams.Epsilon,...
                this.CategoricalPredictors,...
                this.OrdinalPredictors,...
                this.VariableRange,...
                this.ModelParams.RemoveDuplicates);
        end
        
        function newobj = update(this,varargin)
            % dispatch to compact model method
            newobj = update@classreg.learning.regr.CompactRegressionSVM(this,varargin{:});
        end
    end

    methods(Static,Hidden)
        function this = fit(X,Y,varargin)
            temp = RegressionSVM.template(varargin{:});
            this = fit(temp,X,Y);
        end
        
        function temp = template(varargin)
            classreg.learning.FitTemplate.catchType(varargin{:});
            temp = classreg.learning.FitTemplate.make('SVM','type','regression',varargin{:});
        end
    end

    methods(Access=protected)
       
        function s = propsForDisp(this,s)
            s = propsForDisp@classreg.learning.regr.CompactRegressionSVM(this,s);
            s = propsForDisp@classreg.learning.regr.FullRegressionModel(this,s);
            if isfield(s,'SupportVectors')
                s = rmfield(s,'SupportVectors');
            end     
            s.BoxConstraints               = this.BoxConstraints;
            s.ConvergenceInfo              = this.ConvergenceInfo;
            s.IsSupportVector              = this.IsSupportVector;
            s.Solver                       = this.Solver;
        end

        function model = gatherOne(model)
            % Gathers a single RegressionSVM. Used by
            % RegressionSVM/gather
            model = gatherOne@classreg.learning.regr.CompactRegressionSVM(model);
            [...
                model.PrivX, ...
                model.PrivY, ...
                model.W, ...
                model.DataSummary.RowsUsed, ...
                model.ModelParams, ...
                model.HasNaNX ...
                ] = gather(...
                model.PrivX, ...
                model.PrivY, ...
                model.W, ...
                model.DataSummary.RowsUsed, ...
                model.ModelParams, ...
                model.HasNaNX ...
                );
        end
    end
       
    methods
        function cmp = compact(this,varargin)
        %COMPACT Compact SVM model.
        %   CMP=COMPACT(MODEL) returns an object of class
        %   CompactRegressionSVM that holds the trained SVM regression
        %   model. The compact object does not contain X and Y used for
        %   training.
        %
        %   See also RegressionSVM,
        %   classreg.learning.regr.CompactRegressionSVM.
        
            dataSummary = this.DataSummary;
            dataSummary.RowsUsed = [];
            cmp = classreg.learning.regr.CompactRegressionSVM(...
                dataSummary,this.PrivResponseTransform,this.DefaultResponse,...
            compact(this.Impl,this.ModelParams.SaveSupportVectors));
        end
 
            
        function [varargout] = resubPredict(this,varargin)
        %RESUBPREDICT Predict resubstitution response of the FITRSVM.
        %   YFIT=RESUBPREDICT(MDL) returns predicted response YFIT for SVM
        %   regression model MDL and training data MDL.X. YFIT is a vector
        %   of type double with NumObservations elements.
        %  
        %   YFIT = resubPredict(MDL,'NAME1',VALUE1,'NAME2',VALUE2,...)
        %   specifies optional name/value arguments:
        %       'PredictionForMissingValue' - Numeric scalar, character vector, or string scalar. 
        %                                     If a character vector or string scalar, must be one of: 
        %                                     'median' or 'mean'. If a numeric scalar, resubPredict uses
        %                                     this response for observations with missing predictor 
        %                                     values. If 'mean' or 'median', resubPredict uses the mean 
        %                                     or median of observed response values in the training data. 
        %                                     Default: 'median'
        %
        %
        %   See also fitrsvm, RegressionSVM, predict.
            
            [varargout{1:nargout}] = ...
                resubPredict@classreg.learning.regr.FullRegressionModel(this,varargin{:});
       end
        
        function [varargout] = resubLoss(this,varargin)
        %RESUBLOSS Regression error by resubstitution.
        %   L=RESUBLOSS(MDL) returns the mean squared error for SVM
        %   regression model MDL, computed using the training data MDL.X
        %   and MDL.Y.
        %
        %   L=RESUBLOSS(FITRSVM,'PARAM1',val1,'PARAM2',val2,...) specifies
        %   optional name/value arguments:
        %       'LossFun'          - Function handle for loss function, or
        %                            string representing a built-in loss
        %                            function. Available loss functions for
        %                            SVM regression: 'mse', and
        %                            'epsiloninsensitive'. If you pass a
        %                            function handle FUN, LOSS calls it as
        %                            shown below:
        %                               FUN(Y,Yfit,W)
        %                            where Y, Yfit and W are numeric
        %                            vectors of length N. Y is observed
        %                            response, Yfit is predicted response,
        %                            and W is observation weights. Default:
        %                            'mse'
        %       'PredictionForMissingValue' - Numeric scalar, character vector, or
        %                                     string scalar. If a character vector or
        %                                     string scalar, must be one of:
        %                                     'median', 'mean', or 'omitted'. If a
        %                                     numeric scalar, resubLoss uses this response 
        %                                     for observations with missing predictor 
        %                                     values. If 'mean' or 'median', resubLoss uses 
        %                                     the mean or median of observed response 
        %                                     values in the training data. If 'omitted',
        %                                     resubLoss excludes observations with missing 
        %                                     predictor values from the computation. 
        %                                     Default: 'median'
        %
        %   See also RegressionSVM, loss.
        
            [varargout{1:nargout}] = ...
                resubLoss@classreg.learning.regr.FullRegressionModel(this,varargin{:});
        end
        
        function this = resume(this,numIter,varargin)
        %RESUME Resume training this SVM model.
        %   MODEL=RESUME(MDL,NUMITER) trains the SVM regression model MDL
        %   for an additional number of iterations as specified by NUMITER,
        %   and returns an updated model. You can resume training an
        %   SVM model if optimization has not converged and if 'Solver' is
        %   set to 'SMO' or 'ISDA'.
        %
        %   MODEL=RESUME(MDL,NUMITER,'PARAM1',val1,'PARAM2',val2,...)
        %   specifies optional name/value arguments:
        %       'Verbose'       - Verbosity level, one of: 0, 1, or 2.
        %                         Default: Value passed to FITRSVM.
        %       'NumPrint'      - Number of iterations between consecutive
        %                         diagnostic print-outs, specified as a
        %                         non-negative integer. RESUME uses this
        %                         parameter only if you pass 1 for the
        %                         'Verbose'
        %                         parameter. Default: Value passed to
        %                         FITRSVM.
        %
        %   See also fitrsvm, RegressionSVM, Solver.
            [varargin{:}] = convertStringsToChars(varargin{:});
            % Decode input args
            args = {                      'verbose'                'numprint'};
            defs = {this.ModelParams.VerbosityLevel this.ModelParams.NumPrint};
            [verbose,nprint,~] = ...
                internal.stats.parseArgs(args,defs,varargin{:});
            
            if ~isnumeric(numIter) || ~isscalar(numIter) ...
                    || isnan(numIter) || isinf(numIter) || numIter<=0
                error(message('stats:ClassificationSVM:resume:BadNumIter'));
            else
                numIter = ceil(numIter);
            end
            this.ModelParams.IterationLimit = ...
                this.ModelParams.IterationLimit + numIter;
            
            if verbose<=0
                nprint = 0;
            end
            
            bad = this.HasNaNX;

            doclass = 0;

            %calls the resume method of SVMImpl            
            if isempty(bad)
                this.Impl = resume(this.Impl,this.PrivX,this.PrivY,numIter,...
                    doclass,verbose,nprint,...
                    this.CategoricalPredictors,this.OrdinalPredictors,...
                    this.VariableRange);
            else
                this.Impl = resume(this.Impl,this.PrivX(~bad,:),this.PrivY(~bad,:),numIter,...
                    doclass,verbose,nprint,...
                    this.CategoricalPredictors,this.OrdinalPredictors,...
                    this.VariableRange);
            end
         end
         
        function partModel = crossval(this,varargin)
        %CROSSVAL Cross-validate this model.
        %   CVMODEL=CROSSVAL(MDL) builds a partitioned model CVMODEL from
        %   model MDL represented by a full object for regression. You can
        %   then assess the predictive performance of this model on
        %   cross-validated data using methods and properties of CVMODEL.
        %   By default, CVMODEL is built using 10-fold cross-validation on
        %   the training data. CVMODEL is of class
        %   RegressionPartitionedModel.
        %
        %   CVMODEL=CROSSVAL(MODEL,'PARAM1',val1,'PARAM2',val2,...)
        %   specifies optional name/value arguments:
        %      'KFold'       - Number of folds for cross-validation, a
        %                      numeric positive scalar; 10 by default.
        %      'Holdout'     - Holdout validation uses the specified
        %                      fraction of the data for test, and uses the
        %                      rest of the data for training. Specify a
        %                      numeric scalar between 0 and 1.
        %      'Leaveout'    - If 'on', use leave-one-out cross-validation.
        %      'CVPartition' - An object of class CVPARTITION; empty by
        %                      default. If a CVPARTITION object is
        %                      supplied, it is used for splitting the data
        %                      into subsets.
        %
        %   See also fitrsvm, RegressionSVM, cvpartition,
        %   classreg.learning.partition.RegressionPartitionedSVM.
            [varargin{:}] = convertStringsToChars(varargin{:});
            idxBaseArg = find(ismember(varargin(1:2:end),...
                classreg.learning.FitTemplate.AllowedBaseFitObjectArgs));
            if ~isempty(idxBaseArg)
                error(message('stats:classreg:learning:regr:FullRegressionModel:crossval:NoBaseArgs', varargin{ 2*idxBaseArg - 1 }));
            end
            %override crossval so that VerbosityLevel is always zero when
            %performing crossval
            modelParams = this.ModelParams;
            modelParams.VerbosityLevel = 0;
            temp = classreg.learning.FitTemplate.make(this.ModelParams.Method,...
                'type','regression','responsetransform',this.PrivResponseTransform,...
                'modelparams',modelParams,'CrossVal','on',varargin{:});
            partModel = fit(temp,this.X,this.Y,'Weights',this.W,...
                'predictornames',this.PredictorNames,'categoricalpredictors',this.CategoricalPredictors,...
                'responsename',this.ResponseName);
         end    
    
   
    end
    
     methods(Static,Hidden)
         function [X,Y,W,dataSummary,responseTransform] = prepareData(X,Y,varargin)
             [X,Y,vrange,wastable,varargin] = classreg.learning.internal.table2FitMatrix(X,Y,varargin{:});

             % Cast X and Y to the same dominant storage type as X
             % (for example, in memory array or gpuArray).
             [X,Y] = internal.stats.castToDominantStorageTypeIfPossible(X,Y);

             % Process input args
             args = {'weights', 'responsetransform'};
             defs = {       [],                  []};
             [W,transformer,~,crArgs] = ...
                 internal.stats.parseArgs(args,defs,varargin{:});

             % Pre-process
             if ~isfloat(X)
                 error(message('stats:classreg:learning:regr:FullRegressionModel:prepareData:BadXType'));
             end
             internal.stats.checkSupportedNumeric('X',X,true, false, false, true);

             % Check Y type
             if ~isfloat(Y) || ~isvector(Y)
                 error(message('stats:classreg:learning:regr:FullRegressionModel:prepareData:BadYType'));
             end
             internal.stats.checkSupportedNumeric('Y',Y,true, false, false, true);
             Y = Y(:);

             % Process Weights before arguments are passed to
             % FullClassificationRegressionModel.prepareData to ensure it
             % has the desired precision (single or double) and storage type
             % (for example, gpuArray).  It should have the same storage
             % type as the dominant type of X and Y, and if weights was not
             % specified, it should default to having the same precision as
             % Y
             if isempty(W)
                 prototypeW = full(internal.stats.dominantType(double(internal.stats.numericPrototype(X)), internal.stats.numericPrototype(Y)));
                 W = ones(length(Y), 1, "like", prototypeW);
             else
                 % If weights is supplied ensure that the storage type of
                 % the weights input (for example, gpuArray) does not
                 % influence where the model is fitted.
                 prototypeW = full(internal.stats.dominantType(internal.stats.numericPrototype(X), internal.stats.numericPrototype(Y)));
                 W = internal.stats.castToMatchingStorageTypeIfPossible(W,prototypeW);
             end

             % Remove all-NaN observations in X and NaN weights
             [X,Y,W,dataSummary] = ...
                 classreg.learning.FullClassificationRegressionModel.prepareDataCR(X,Y,'Weights',W,crArgs{:},'VariableRange',vrange,'TableInput',wastable);
             if ~dataSummary.TableInput
                 X = classreg.learning.internal.encodeCategorical(X,dataSummary.VariableRange);
             end

             % Remove NaNs in Y
             [X,Y,W,dataSummary] = ...
                classreg.learning.regr.FullRegressionModel.removeNaNs(X,Y,W,dataSummary);

             % Renormalize weights
             W = W./sum(W);

             % Make output response transformation
             responseTransform = ...
                 classreg.learning.regr.FullRegressionModel.processResponseTransform(transformer);
         end
    end
end

function iCheckGpuArrayLimitations(x, modelParams)
% Error if necessary for functionality not supported for GPU data.

% Inf predictor values are not supported
if any(isinf(x), "all")
    throwAsCaller(MException(message('stats:RegressionSVM:RegressionSVM:GpuArrayInfPredictorsNotSupported')));
end

% The 'OutlierFraction' option is not supported.
if modelParams.OutlierFraction ~= 0
    throwAsCaller(MException(message('stats:RegressionSVM:RegressionSVM:GpuArrayOutlierFractionNotSupported')));
end

% The 'KernelFunction' option must not specify a custom kernel
supportedKernelFunctions = ["gaussian", "rbf", "linear", 'polynomial'];
if ~isempty(modelParams.KernelFunction) && ~any(modelParams.KernelFunction == supportedKernelFunctions)
    throwAsCaller(MException(message('stats:RegressionSVM:RegressionSVM:GpuArrayKernelFunctionNotSupported')));
end

% The 'Solver' option can be set to only 'SMO'.
if ~strcmpi(modelParams.Solver, 'SMO')
    throwAsCaller(MException(message('stats:RegressionSVM:RegressionSVM:GpuArraySolverNotSupported')));
end

% The ShrinkagePeriod option is not supported
if modelParams.ShrinkagePeriod ~= 0
    throwAsCaller(MException(message('stats:RegressionSVM:RegressionSVM:GpuArrayShrinkagePeriodNotSupported')));
end
end

% LocalWords:  SVMPARAMS
