classdef(Sealed, SupportExtensionMethods=true, InferiorClasses={?gpuArray, ...
        ?matlab.graphics.animation.AnimatedLine, ...
        ?matlab.graphics.axis.Axes, ...
        ?matlab.ui.control.UIAxes, ...
        ?matlab.ui.Figure, ...
        ?matlab.ui.container.GridLayout, ...
        ?matlab.ui.container.Panel, ...
        ?matlab.ui.container.Tab, ...
        ?categorical, ...
        ?datetime, ...
        ?duration}) dlarray < ...
        matlab.mixin.internal.indexing.Paren & ...
        matlab.mixin.internal.indexing.ParenAssign & ...
        matlab.mixin.CustomDisplay & ...
        deep.internal.dlarray.PlottableUsingExtractdata
    %DLARRAY Deep learning array
    %   Y = DLARRAY(X) creates a dlarray object Y containing the input data
    %   X. X can be of data type double, single or logical. X can be a
    %   standard numeric array, a gpuArray, or a dlarray. If X is a
    %   dlarray, Y is a copy of X. Sparse data is not supported.
    %
    %   Y = DLARRAY(X,DIMS) also takes a character vector or string scalar
    %   DIMS that specifies the data format of X and assigns a label to
    %   each dimension of the input data. Each character in DIMS must be
    %   one of the following labels
    %
    %       - 'S' - spatial
    %       - 'C' - channel
    %       - 'B' - batch (for example, samples and observations)
    %       - 'T' - time (for example, sequences)
    %       - 'U' - unspecified
    %
    %   Applying a data format aids in passing deep learning data between
    %   functions. If X is a formatted dlarray, then DIMS replaces the
    %   existing format. The default value of DIMS is the empty string. A
    %   dlarray can have multiple dimensions labeled 'S' or 'U'. Labels
    %   'C', 'B', and 'T' can be used at most once. Specify at least as
    %   many dimension labels as there are dimensions of the input data. If
    %   DIMS specifies more labels than the number of dimensions of the
    %   input data, a singleton dimension is created in the output dlarray
    %   Y for each entry in DIMS beyond the last dimension of the input
    %   data.
    %
    %   A dlarray accepts formatted data with no restriction on the order
    %   of the dimension labels. Both DIMS and the underlying data are
    %   reordered to match the following fixed order - all dimensions with
    %   label 'S', then dimensions with labels 'C', 'B', and 'T', then all
    %   dimensions with label 'U'.
    %
    %   When the input data is a vector and DIMS is a single character, the
    %   output dlarray is a vector with the first dimension labeled
    %   DIMS, and the second (singleton) dimension labeled 'U'.
    %
    %   DLARRAY functions:
    %     Dimension labels and data manipulation
    %       DIMS           - Data format of a dlarray
    %       FINDDIM        - Find dimensions with a given label
    %       STRIPDIMS      - Strip data format from a dlarray
    %       EXTRACTDATA    - Extract data represented by dlarray
    %       GPUARRAY       - Convert underlying data to gpuArray
    %
    %     Deep learning operations
    %       AVGPOOL        - Pool data to average value
    %       ATTENTION      - Dot product attention
    %       BATCHNORM      - Normalize each channel of input data
    %       CROSSCHANNELNORM - Cross channel square-normalize using local responses
    %       CROSSENTROPY   - Categorical cross-entropy loss
    %       CTC            - Connectionist temporal classification loss
    %       DLCONV         - Deep learning convolution
    %       DLODE45        - Solve ordinary differential equation (ODE) for neural ODE
    %       DLTRANSPCONV   - Deep learning transposed convolution
    %       EMBED          - Embed discrete data
    %       FULLYCONNECT   - Sum all weighted input data and apply a bias
    %       GELU           - Apply gaussian error linear unit activation
    %       GROUPNORM      - Normalize across groups of channels
    %       GRU            - Gated recurrent unit
    %       HUBER          - Huber loss
    %       INSTANCENORM   - Normalize each channel dimension
    %       L1LOSS         - L1 loss
    %       L2LOSS         - L2 loss
    %       LAYERNORM      - Normalize across channel dimension
    %       LEAKYRELU      - Apply leaky rectified linear unit activation
    %       LSTM           - Long short-term memory
    %       MAXPOOL        - Pool data to maximum value
    %       MAXUNPOOL      - Unpool the output of maxpool
    %       MSE            - Half mean squared error
    %       RELU           - Apply rectified linear unit activation
    %       SIGMOID        - Apply sigmoid activation
    %       SOFTMAX        - Apply softmax operation to input channel dimension
    %
    %     Automatic differentiation
    %       DLGRADIENT     - Compute gradient of dlarray object
    %
    %   For a complete list of dlarray functions and specific math
    %   operators behaviors, see <a href="matlab:helpview('deeplearning','DLARRAY_FUNCTIONS')">List of Functions with dlarray Support</a>
    %
    %   Example 1:
    %       % Create a dlarray for input data X with no format.
    %       X = randn(10,10,3,5);
    %       Z = dlarray(X);
    %
    %   Example 2:
    %       % Create a dlarray with random data containing two spatial
    %       % dimensions of size 10, one channel dimension of size 3, and
    %       % five observations.
    %       Z = dlarray(randn(10,10,3,5),'SSCB');
    %
    %   Example 3:
    %       % Create a dlarray with random data and no format and then a
    %       % copy of it with format 'BCSS'.
    %       Z1 = dlarray(randn(32,3,24,24));
    %       Z2 = dlarray(Z1,'BCSS');
    %       % Output: Z2 = 24(S) x 24(S) x 3(C) x 32(B) dlarray double
    %
    %   Example 4:
    %       % Create a vector dlarray from a vector of ones with six
    %       % elements and with format 'T'.
    %       Z = dlarray(ones(1,6),'T');
    %
    %   Example 5:
    %       % Create a 2-dimensional gpuArray with size 128.
    %       X = gpuArray(randn(128));
    %       % Define a dlarray containing the gpuArray data and assign
    %       % 'SSCB' format to it.
    %       Z = dlarray(X,'SSCB');
    %
    %   See also DLFEVAL, DLGRADIENT, DLNETWORK.

    %   Copyright 2018-2023 The MathWorks, Inc.

    properties(Access=private, Transient)
        FormattedData = deep.internal.format.FormattedData([])

        % The following properties always have the same length (unless they
        % are empty). Their length is equal to that of dims (unless it is
        % empty), and is >= ndims of the stored data.
        %
        % Usage: The names are chosen to be intuitive when translating a
        % single dimension index, which makes them counterintuitive when
        % used as input to permute.
        % Examples:
        % ViewToDataMap(viewDim) == dataDim
        % permute(underlyingData, ViewToDataMap) == viewData
        ViewToDataMap = 1:0
    end

    properties(Constant, Hidden, Transient)
        OrderOfDims = 'SCBTU'
        ClassVersion = 3;
    end

    properties(Constant, Transient, Access=private)
        % Reusable copy of an empty formatted data that is used to replace real
        % data when it needs to be extracted for a function to operate on.
        EmptyFormattedData = deep.internal.format.FormattedData([]);
    end

    methods
        function obj = dlarray(data, fmt)
            %

            if nargin==0
                return
            end

            % Check the format input
            if nargin < 2
                fmt = '';
            else
                deep.internal.dlarray.mustBeDataFormatType(fmt, true);
                fmt = char(fmt);
                deep.internal.format.validateDataFormat(fmt);
            end

            inputIsDlarray = isa(data, 'dlarray');
            if inputIsDlarray
                % Use the input data as the output.
                data = iEnforceEmptyRowPermutations(data);
                obj = data;
            else
                deep.internal.dlarray.validateNonDlarray(data);
            end

            if ~isempty(fmt)
                if inputIsDlarray
                    % Extract the underlying data and permutation
                    data = obj.FormattedData.Data;
                    obj.FormattedData.Data = [];

                    perm = obj.ViewToDataMap;
                    if ~isempty(perm)
                        % The permutation might not be needed after the new format is applied
                        obj.ViewToDataMap = 1:0;
                    end
                else
                    % Non-dlarray data does not have a permutation
                    perm = 1:0;
                end

                [data, perm, fmt] = iCheckFormatLength(data, perm, fmt);
                obj.FormattedData.Data = data;
                obj = iSetLayout(obj, perm, fmt);

            elseif ~inputIsDlarray
                % No format, just store the data
                obj.FormattedData.Data = data;
            else
                % Copy-construction - no changes to the input dlarray are required
            end
        end
    end

    methods(Static, Hidden)
        function obj = blank(sz,fmt)
            %BLANK Create a blank dlarray
            %
            %   This function is for internal use only. It may be removed in the future.

            %   X = DLARRAY.BLANK(SZ) creates a blank dlarray X of size SZ. Blank dlarray
            %   objects contain no values. They specify size, data format, and datatype
            %   information only.
            %
            %   X = DLARRAY.BLANK(SZ, FMT) creates a blank dlarray X of size SZ with data
            %   format FMT.
            %
            %   To cast a blank dlarray to a different datatype, use the CAST function.
            %
            %   The following functions support blank dlarray input:
            %     - Functions that query the size, data format, or datatype of dlarray objects.
            %     - Functions that modify the data format or datatype of dlarray objects.

            if nargin == 0
                data = deep.internal.blank;
            else
                data = deep.internal.blank(sz);
            end

            % Check the format input
            if nargin < 2
                fmt = '';
            else
                deep.internal.dlarray.mustBeDataFormatType(fmt, true);
                fmt = char(fmt);
                deep.internal.format.validateDataFormat(fmt);
            end

            obj = dlarray;
            if ~isempty(fmt)
                [data, perm, fmt] = iCheckFormatLength(data, 1:0, fmt);
                obj.FormattedData.Data = data;
                obj = iSetLayout(obj, perm, fmt);
            else
                obj.FormattedData.Data = data;
            end
        end
    end

    methods(Hidden)
        obj = parenReference(obj,varargin)
        out = parenAssign(obj,rhs,varargin)

        savedata = saveobj(obj)

        tf = isblank(obj)
        z = stridedadd(x,stridex,y,stridey);
        obj = repmat(obj,varargin);
    end

    methods(Static, Hidden)
        obj = loadobj(savedata)
        obj = empty(varargin)
        obj = createArray(dims, F)
    end

    methods(Access=private)
        function obj = clearInternalPermutation(obj)
            obj.ViewToDataMap = 1:0;
        end

        function DataDim = getDataDim(obj, ViewDim)
            viewToData = obj.ViewToDataMap;
            if isempty(viewToData)
                DataDim = ViewDim;
            else
                DataDim = ViewDim;
                ind = ViewDim <= length(viewToData);
                DataDim(ind) = viewToData(ViewDim(ind));
            end
        end
    end

    % Methods that support transforming a public dlarray into an internal
    % representation
    methods(Access=?deep.internal.format.FormattedData)
        function fd = copyInternalData(obj, permute)
            %copyInternalData Take a copy of the current FormattedData
            %
            %   FD = copyInternalData(X, PERMUTE) returns the internal FormattedData
            %   object from X. When PERMUTE is true, any stored implicit permutations
            %   are applied before returning FD. When PERMUTE is false no outstanding
            %   permutations are applied.

            if permute
                obj = permuteExplicitly(obj);
            end
            fd = obj.FormattedData;
        end

        function fd = copyInternalDataWithLayout(obj, permute)
            %copyInternalDataWithLayout Take a copy of the current FormattedData with a DataLayout
            %
            %   FD = copyInternalDataWithLayout(X, PERMUTE) returns the internal
            %   FormattedData object from X, and ensures that it contains a correct
            %   DataLayout in its Layout property. When PERMUTE is true, any stored
            %   implicit permutations are applied before returning FD, and in this case
            %   the Permutation property of the DataLayout is guaranteed to be empty.  When
            %   PERMUTE is false no outstanding permutations are applied, and the
            %   DataLayout may have a non-empty Permutation.

            if permute
                obj = permuteExplicitly(obj);
            end
            fd = obj.FormattedData;

            % Change the FormattedData so that it only contains the new DataLayout
            % representation.
            fd = iCreateDataLayout(fd, obj.ViewToDataMap);
        end

        function [obj, fd] = extractInternalData(obj, permute)
            %extractInternalData Extract the current FormattedData
            %
            %   [X, FD] = extractInternalData(X, PERMUTE) returns the internal
            %   FormattedData object from X and sets X to contain a default empty
            %   FormattedData . When PERMUTE is true, any stored implicit permutations
            %   are applied before returning FD. When PERMUTE is false no outstanding
            %   permutations are applied.
            %
            %   Use this method as part of workflows that want to enable in-place
            %   operations on the data that is contained by X.

            if permute
                obj = permuteExplicitly(obj);
            end
            fd = obj.FormattedData;
            obj.FormattedData = dlarray.EmptyFormattedData;
        end

        function [obj, fd] = extractInternalDataWithLayout(obj, permute)
            %extractInternalDataWithLayout Extract the current FormattedData with a DataLayout
            %
            %   [X, FD] = extractInternalDataWithLayout(X, PERMUTE) returns the
            %   internal FormattedData object from X, and ensures that it contains a
            %   correct DataLayout in its Layout property. When PERMUTE is true, any
            %   stored implicit permutations are applied before returning FD, and in
            %   this case the Permutation property of the DataLayout is guaranteed to
            %   be empty.  When PERMUTE is false no outstanding permutations are
            %   applied, and the DataLayout may have a non-empty Permutation.
            %
            %   Use this method as part of workflows that want to enable in-place
            %   operations on the data that is contained by X.

            [obj, fd] = extractInternalData(obj, permute);

            % Change the FormattedData so that it only contains the new DataLayout
            % representation.
            fd = iCreateDataLayout(fd, obj.ViewToDataMap);
        end

        function obj = restoreInternalData(obj, fd)

            layout = fd.Layout;
            if ~isempty(layout)

                % Clear the layout copy from the FormattedData
                fd.Layout = [];

                if ~isCompatibleWithOriginalDlarray(layout)
                    % Convert the DataLayout representation into a storage format that dlarray
                    % uses. Dlarray does not currently support unknown format labels in the
                    % stored data.

                    % Lock any non-trailing dot-labels into a permanent "U", since the user can
                    % now see that they exist.
                    layout = freezeViewFormat(layout);

                    % Ensure that the new data's view is ordered correctly for dlarray
                    layout = orderViewFormat(layout);

                    % The existing dlarray storage approach does not allow singleton unlabelled
                    % dimensions to be kept around.  We need to convert the data and storage layout
                    % information to the "minimal" view which has them at the end to satisfy dlarray.
                    dlarrayStorageLayout = minimalView(layout);
                    fd.Data = storageToView(dlarrayStorageLayout, fd.Data);
                    fd.DimensionMetadata = viewFormat(dlarrayStorageLayout);

                    % Generate a layout which maps between the new storage and the old view
                    layout = changeStorage(layout, dlarrayStorageLayout);
                    perm = layout.Permutation;

                    % Dot-label dimensions are dropped in the new storage.  They are all
                    % guaranteed to be trailing because we used minimalView.  Any permutation
                    % entries for those trailing dimensions need to be trimmed away too.
                    if ~isempty(perm)
                        perm = perm(1:numel(fd.DimensionMetadata));
                    end
                else
                    % Use the permutation and format directly from the layout
                    fd.DimensionMetadata = layout.StorageFormat;

                    % When there is no format we just accept the permutation as it is.
                    perm = layout.Permutation;
                end

                if isempty(perm)
                    perm = 1:0;
                end

                % Update current permutations in the dlarray
                obj.ViewToDataMap = perm;
            end

            obj.FormattedData = fd;

            % Ensure that we have not been given trailing singleton U dimensions.
            obj = dropTrailingODims(obj);
        end
    end


    % Methods that support gradient calculations
    methods(Hidden)
        x = independent(x)
        x = identity(x)
        function obj = record(obj)
            % Turn on call graph recording
            obj.FormattedData = record(obj.FormattedData);
        end

        function obj = stop(obj)
            % stop call graph recording
            obj.FormattedData = stop(obj.FormattedData);
        end

        function rec = getRecorded(obj)
            % Get the recorded tree if there is one
            rec = getRecorded(obj.FormattedData);
        end

        function [dataToView,viewToData] = getRecordingPermutations(obj)
            viewToData = obj.ViewToDataMap;
            dataToView(viewToData) = 1:length(viewToData);
        end
    end

    methods(Static, Hidden)
        function name = matlabCodegenRedirect(~)
            % Redirect dlarray class to the class below for
            % code-generation
            name = 'deep.internal.coder.dlarray';
        end
    end

    % Display methods
    methods(Hidden, Access = protected)

        displayObject(obj);

        function displayNonScalarObject(obj)
            displayObject(obj);
        end

        function displayScalarObject(obj)
            displayObject(obj);
        end

        function displayEmptyObject(obj)
            displayObject(obj);
        end
    end

    % Standard array info/manipulation methods.  These are methods that
    % only provide inspection of the properties of the data and do not
    % return a new dlarray.
    %
    % All methods that need to alter the contained FormattedData need to
    % pass through a data access strategy so that we can later implement
    % interception, tracing, lazy evaluation etc.
    methods
        [varargout] = size(obj, viewdim, varargin)

        nd = ndims(obj)

        function n = numel(obj)
            n = numel(obj.FormattedData.Data);
        end

        function n = length(obj)
            n = length(obj.FormattedData.Data);
        end

        function empty = isempty(obj)
            empty = isempty(obj.FormattedData.Data);
        end

        idx = subsindex(obj)
        ind = end(obj,k,n)
    end

    % Methods that convert a dlarray to a string or char vector
    methods
        [str, errrmsg] = sprintf(varargin)
        str = compose(varargin)
        count = fprintf(varargin)
        str = num2str(varargin)
        str = int2str(obj)
        str = mat2str(varargin)
        str = string(varargin)
    end

    % Array size related methods
    methods
        function tf = ismatrix(obj)
            viewToData = obj.ViewToDataMap;
            data = obj.FormattedData.Data;
            if isempty(viewToData)
                tf = ismatrix(data);
            elseif length(viewToData) == 2
                tf = true;
            else
                tf = all(size(data, viewToData(3:end)) == 1);
            end
        end

        function tf = isscalar(obj)
            tf = isscalar(obj.FormattedData.Data);
        end

        function tf = isvector(obj)
            viewToData = obj.ViewToDataMap;
            data = obj.FormattedData.Data;
            if isempty(viewToData)
                tf = isvector(data);
            elseif length(viewToData) == 2
                tf = any(size(data) == 1);
            else
                sz = size(data, viewToData);
                tf = all(sz(3:end) == 1) && any(sz(1:2) == 1);
            end
        end

        function tf = isrow(obj)
            viewToData = obj.ViewToDataMap;
            data = obj.FormattedData.Data;
            if isempty(viewToData)
                tf = isrow(data);
            elseif length(viewToData) == 2
                tf = size(data, viewToData(1)) == 1;
            else
                sz = size(data, viewToData);
                tf = all(sz([1 3:end]) == 1);
            end
        end

        function tf = iscolumn(obj)
            viewToData = obj.ViewToDataMap;
            data = obj.FormattedData.Data;
            if isempty(viewToData)
                tf = iscolumn(data);
            elseif length(viewToData) == 2
                tf = size(data, viewToData(2)) == 1;
            else
                sz = size(data, viewToData);
                tf = all(sz([2 3:end]) == 1);
            end
        end

        result = cat(dim, varargin)
        result = horzcat(varargin)
        result = vertcat(varargin)

        obj = permute(obj, perm)
        obj = transpose(obj)
        obj = ctranspose(obj)

        obj = reshape(obj,varargin)
        obj = squeeze(obj)

        obj = interp1(varargin)

        %Provided by flow-through (or error if dimensions are formatted):
        %  * ipermute
        %  * shiftdim
        %  * circshift
        %  * repmat/repelem
        %  * flip/fliplr/flipud
        %  * rot90
    end

    methods(Access=private) % Helpers for standard math methods
        function fmt = datadims(obj)
            fmt = obj.FormattedData.DimensionMetadata;
        end

        function ind = finddatadim(obj, dim)
            ind = getDataDim(obj, finddim(obj, dim));
        end

        function z = nextpow2(z)
            % For use in rescale, private method not exposed.
            [z, fdz, zdata] = extractUnaryElwiseData(z);

            zdata = nextpow2(zdata);

            z = restoreUnaryElwiseData(z, fdz, zdata);
        end
    end


    methods(Hidden)
        function tf = hasdims(obj)
            tf = ~isempty(obj.FormattedData.DimensionMetadata);
        end

        function obj = setdims(obj, fmt)
            perm = obj.ViewToDataMap;
            if ~isempty(perm)
                % The permutation might not be needed after the new format is applied
                obj.ViewToDataMap = 1:0;
            end

            if numel(fmt) ~= 1
                % We do not modify data
                data = obj.FormattedData.Data;
                [~, perm, fmt] = iCheckFormatLength(data, perm, fmt);
            else
                data = obj.FormattedData.Data;
                obj.FormattedData.Data = [];
                [data, perm, fmt] = iCheckFormatLength(data, perm, fmt);
                obj.FormattedData.Data = data;
            end
            obj = iSetLayout(obj, perm, fmt);
        end
    end

    % Standard math methods
    methods
        % Reduction operations:
        obj = sum(obj, varargin)
        obj = prod(obj, varargin)
        obj = mean(obj, varargin)
        obj = all(obj, varargin)
        obj = any(obj, varargin)
        obj = vecnorm(obj, varargin)

        % Binary operators:
        z = plus(x, y)
        z = minus(x, y)
        z = power(x, y)
        z = realpow(x, y)
        z = rdivide(x, y)
        z = times(x, y)
        z = atan2(x, y)
        z = complex(x, y)

        % Element-wise methods
        z = abs(z)
        z = conj(z)
        z = real(z)
        z = imag(z)
        z = cosh(z)
        z = cos(z)
        z = cot(z)
        z = csc(z)
        z = exp(z)
        z = log(z)
        z = sec(z)
        z = sign(z)
        z = sinh(z)
        z = sin(z)
        z = sqrt(z)
        z = tanh(z)
        z = tan(z)
        z = uminus(z)
        z = uplus(z)
        z = floor(z)
        z = ceil(z)
        z = round(z, varargin)
        z = fix(z)
        z = asin(z)
        z = acos(z)
        z = asec(z)
        z = acsc(z)
        z = atan(z)
        z = acot(z)
        z = asinh(z)
        z = acosh(z)
        z = atanh(z)
        z = reallog(z)
        z = realsqrt(z)
        z = erf(z)

        % Others
        z = mtimes(x, y)
        z = dlmtimes(x, y)
        z = mrdivide(x, y)
        z = pinv(z, tol)
        [z, ind] = min(x, y, varargin)
        [z, ind] = max(x, y, varargin)
        c = colon(a,d,b)
        a = rescale(a, varargin)
        a = onehotdecode(b, c, featdim, varargin)
        z = eps(z, y)
        [z, idx] = sort(x, varargin)

        % Logical operators (and, not, or)
        z = and(x, y)
        z = or(x, y)
        z = not(z)
        z = xor(x, y)

        % Relational operators (lt, gt, le, ge, ne, eq)
        z = eq(x, y)
        z = ge(x, y)
        z = gt(x, y)
        z = le(x, y)
        z = lt(x, y)
        z = ne(x, y)

        % Comparison
        tf = isequal(x, y)
        tf = isequaln(x, y)
        tf = isnumeric(obj)
        tf = islogical(obj)
        tf = isfloat(obj)
        tf = isreal(obj)

        % Query special values
        tf = isnan(x)
        tf = isinf(x)
        tf = isfinite(x)

        % Conversion
        X = extractdata(obj)
        z = double(z)
        obj = single(obj)
        x = logical(x)
        obj = gpuArray(obj)
        varargout = gather(varargin)
        out = categorical(varargin)

        % ODE solvers
        varargout = ode45(varargin)
        
    end

    methods(Hidden)
        X = createArrayLike(T, dims, F)
        x = zerosLike(obj, varargin)
        x = onesLike(obj, varargin)
        x = infLike(obj, varargin)
        x = nanLike(obj, varargin)
        x = trueLike(obj, varargin)
        x = falseLike(obj, varargin)
        x = randLike(obj, varargin)
        x = randnLike(obj, varargin)
        x = randiLike(obj, varargin)
        z = internal_mask(obj, varargin)
    end

    % Deep learning functions
    methods
        [X,mean,variance] = batchnorm(X,offset,scale,varargin)
        X = groupnorm(X,numGroups,offset,scale,varargin)
        X = relu(X)
        X = dlconv(X,weights,bias,varargin)
        X = embed(X,weights,varargin)
        X = avgpool(X, poolSize, varargin)
        X = crossentropy(X, T, varargin)
        X = ctc(X, T, XMask, TMask, varargin)
        X = fullyconnect(X, W, B, varargin)
        X = huber(X, T, varargin)
        X = mse(X, T, varargin)
        X = dltranspconv(X, W, B, varargin)
        X = leakyrelu(X, varargin)
        X = sigmoid(X)
        X = softmax(X, varargin)
        [X, indices, inputSize] = maxpool(X, poolSize, varargin)
        X = maxunpool(X, indices, outputSize, varargin)
        [X, h0, c0] = lstm(X, h0, c0, W, R, B, varargin)
        X = crosschannelnorm(X, channelWindowSize, varargin)
        [X, h0] = gru(X, h0, W, R, B, varargin)
        X = instancenorm(X, offset, scale, varargin)
        X = layernorm(X, offset, scale, varargin)
        X = l1loss(X, T, varargin)
        X = l2loss(X, T, varargin)
        X = gelu(X, varargin)
        [X, W] = attention(Q, K, V, numHeads, varargin)
    end

    % Auto-diff related methods
    methods
        varargout = dlgradient(y,varargin)
    end

end


function obj = iEnforceEmptyRowPermutations(obj)
% Ensure that empty permutations are always a row.
if isempty(obj.ViewToDataMap)
    if ~isrow(obj.ViewToDataMap)
        obj.ViewToDataMap = 1:0;
    end
end
end


function [data, perm, fmt] = iCheckFormatLength(data, perm, fmt)
% Test that constructor format input is valid and return a matching set of
% updated data, permutation and format.
% setdims() assumes that iCheckFormatLength does not change data input
% unless format is scalar.

numFmtDims = numel(fmt);
permLength = numel(perm);
isPermEmpty = permLength==0;
if isPermEmpty
    numDataDims = ndims(data);
else
    numDataDims = permLength;
end

if numFmtDims==1
    if isPermEmpty
        sz = size(data);
    else
        sz = size(data, 1:permLength);
    end
    isNonSingleton = (sz~=1);
    % Special-case for scalar input
    if nnz(isNonSingleton) > 1
        throwAsCaller(MException(message('deep:dlarray:OneLabelVectorOnly')));
    end

    if ~isNonSingleton(1)
        % Always turn data into a column so that we know where to put the label
        % without creating a permutation vector.
        data = reshape(data, [], 1);
    end
    perm = 1:0;
    fmt = [fmt 'U'];

elseif numFmtDims < numDataDims
    if isPermEmpty
        sz = size(data);
        sizePerm = 1:numel(sz);
    else
        sz = size(data, 1:permLength);
        sizePerm = perm;
    end

    if all(sz(sizePerm(numFmtDims+1:end))==1)
        % The extra data dimensions are trailing singletons.  For now we map these
        % to a U label; the construction process will look for and remove the
        % trailing U dimensions at the end.
        fmt((end+1):numDataDims) = 'U';
    else
        throwAsCaller(MException(message('deep:dlarray:MoreDimsThanLabels')));
    end

elseif ~isPermEmpty && (numFmtDims > numDataDims)
    % We want to label implicit trailing singleton dimensions but there are too
    % few entries in the permutation.  Expand it to match the number of format
    % labels.
    perm((end+1):numFmtDims) = (permLength+1):numFmtDims;
end
end


function obj = iSetLayout(obj, perm, fmt)
% Construct a correctly-permuted dlarray from a set of constructor input
% data.

if isempty(perm)
    % Directly apply new format to stored data
    storageFmt = fmt;
else
    % Inverse-permute the format into the correct storage order
    storageFmt(perm) = fmt;
end
obj.FormattedData.DimensionMetadata = storageFmt;

if ~deep.internal.format.isOrderedFormat(fmt)
    % The supplied format needs to be reordered into the standard dlarray order
    [~,outerPerm] = deep.internal.format.orderFormat(fmt);
    if isempty(perm)
        perm = outerPerm;
    else
        perm = perm(outerPerm);
    end
end

if ~isempty(perm) && ~all(perm==1:numel(perm))
    obj.ViewToDataMap = perm;
end

if numel(fmt)>2
    % Ensure that we have not been given trailing singleton U dimensions.
    obj = dropTrailingODims(obj);
end
end


function fd = iCreateDataLayout(fd, viewToData)

% If the contained data is being viewed as a column vector with a trailing
% U dimension, we want to drop that U in our new format representation.
% Instead it will be represented by a "dot-label".
fmt = fd.DimensionMetadata;
if ~isempty(fmt)
    fd.DimensionMetadata = '';

    if isvector(fd.Data)
        if isempty(viewToData)
            columnIndex = 2;
        else
            columnIndex = viewToData(2);
        end
        if fmt(columnIndex)=='U' && size(fd.Data, columnIndex)==1
            fmt(columnIndex) = '.';
        end
    end
end

% Change the FormattedData so that it only contains the new data layout
% representation.
fd.Layout = deep.internal.format.LayoutFactory.makeLayout(fmt, viewToData);
end
