function varargout = dlupdate(fun, a, b, c, d, e, varargin)
%DLUPDATE Apply a function to parameter values
%
%   NET = DLUPDATE(FUN,NET) updates the learnable parameters of the
%   dlnetwork NET by evaluating the function FUN with each learnable
%   parameter as an input. FUN is a function handle to a function that
%   takes one parameter array as an input argument and returns an updated
%   parameter array. FUN is evaluated as many times as there are numeric
%   parameter arrays in NET. Use DLUPDATE to update learnable parameters
%   during network training.
%
%   PARAMS = DLUPDATE(FUN,PARAMS) updates the learnable parameters in
%   PARAMS by evaluating the function FUN on each individual parameter.
%   Input PARAMS can be a dlarray, a numeric array, a cell array, a
%   structure, or a table with a Value variable containing the learnable
%   parameters of a network.
%
%   [__] = DLUPDATE(FUN,__,A1,...,An) evaluates the function using
%   A1,...,An as additional input arguments to FUN. When the first input to
%   FUN is a dlnetwork, the inputs A1,...,An must be tables with the same
%   structure as the dlnetwork Learnables property, with a Value variable
%   containing a cell array. When the first input to FUN is a dlarray, a
%   numeric array, a cell array, a structure, or a table, the inputs
%   A1,...,An must have the same datatype, size, and ordering as the first
%   input.
%
%   [__,X1,...,Xn] = DLUPDATE(FUN,__), where FUN is a function handle to a
%   function that returns multiple outputs, also returns X1,...,Xn, each
%   corresponding to one of the additional outputs of FUN. When the first
%   input to FUN is a dlnetwork, the outputs X1,...,Xn are tables with the
%   same structure as the dlnetwork Learnables property, with a Value
%   variable containing a cell array.  When the first input to FUN is a
%   dlarray, a numeric array, a cell array, a structure, or a table, the
%   outputs X1,...,Xn have the same datatype, size, and ordering as the
%   first input.
%
%   Example 1:
%      % Perform L1 regularization on a structure of parameter gradients
%      % using the MATLAB function convGradients as follows:
%      function grads = convGradients(X,params)
%          X = dlconv(X,params.Weights,params.Bias);
%          grads = dlgradient(sum(X,'all'),params);
%      end
%
%      % Regularization factor
%      L1Factor = 0.001;
%
%      % SGDM parameter velocities
%      vel = [];
%
%      % Create sample input data and parameters and run the convolution
%      % gradients function
%      X = dlarray(rand(100, 100, 3), 'SSC');
%      params.Weights = dlarray(rand(10,10,3,50));
%      params.Bias = dlarray(rand(50,1));
%      grads = dlfeval(@convGradients,X,params);
%
%      % Regularize the gradients
%      L1Regularizer = @(g,p) g + L1Factor.*sign(p);
%      grads = dlupdate(L1Regularizer,grads,params);
%
%      % Perform standard parameter update algorithm
%      [params,vel] = sgdmupdate(params,grads,vel);
%
%   Example 2:
%      % Perform global L2 norm clipping on gradients.
%
%      % Threshold to apply clipping
%      threshold = 1;
%
%      % Obtain gradients from a call to dlfeval
%      grads = dlfeval(__);
%
%      % Define a nested function that updates the gradientNorm variable
%      gradientNorm = 0;
%      function g = sumGradientNorms(g)
%          gradientNorm = gradientNorm + sqrt(sum(g.^2,'all'));
%      end
%
%      % Calculate the global L2 norm of the gradients in grads
%      dlupdate(@sumGradientNorms,grads);
%
%      % If the gradients exceed the threshold, rescale them
%      if gradientNorm > threshold
%          scale = threshold/gradientNorm;
%          grads = dlupdate(@(g) scale*g,grads);
%      end
%
%   See also ADAMUPDATE, DLFEVAL, DLGRADIENT, DLNETWORK, RMSPROPUPDATE, SGDMUPDATE

% Copyright 2019-2021 The MathWorks, Inc.

if ~isa(fun, 'function_handle') && ~isa(fun, 'deep.internal.LearnableUpdateFunction')
    error(message('deep:dlupdate:funArgNotHandle'));
end
    
numIn = nargin;
if numIn<3 
    [varargout{1:nargout}] = deep.internal.networkContainerFixedArgsFun(...
        fun, matlab.lang.internal.move(a), {}, {});
else
    % The first 5 arguments are specified in the function declaration as named
    % variables so that Matlab will not need to create us a varargin, which
    % introduces extra overhead and also prevents in-place optimizations.
    %
    % These variables plus the varargin need to be moved into a cell array so
    % that they can then be moved over to the recursion system as a single
    % item.
    args = cell(1, numIn-2);
    args{1} = matlab.lang.internal.move(b);
    if numIn>3
        args{2} = matlab.lang.internal.move(c);
    end
    if numIn>4
        args{3} = matlab.lang.internal.move(d);
    end
    if numIn>5
        args{4} = matlab.lang.internal.move(e);
    end
    if numIn>6
        args(5:end) = matlab.lang.internal.move(varargin);
    end
   
    [varargout{1:nargout}] = deep.internal.networkContainerFixedArgsFun(...
        fun, matlab.lang.internal.move(a), ...
        matlab.lang.internal.move(args), {});
end
end
