classdef(Sealed) networkDataLayout < matlab.mixin.Scalar
    %networkDataLayout  Deep learning network data layout for parameter initialization
    %   LAYOUT = networkDataLayout(SZ) creates an unformatted network data
    %   layout object for size SZ. SZ must be a row vector of nonnegative
    %   integers or NaN, where NaN elements correspond to unknown dimension
    %   sizes.
    %
    %   LAYOUT = networkDataLayout(SZ, FMT) additionally specifies the
    %   dimension labels FMT. FMT must be a dlarray format string, like
    %   'SSCB'. The default format is ''.
    %
    %   Properties:
    %      Size    - Array size
    %      Format  - Dimension labels
    %
    %   Methods:
    %      finddim - Find dimension with specified label
    %
    %   Example:
    %      layout = networkDataLayout([100 100 1 NaN],'SSCB');
    %
    %   See also: dlarray

    %   Copyright 2022 The MathWorks, Inc.

    properties(SetAccess = private)

        % Size  Array size
        %   A vector of dimension sizes. NaN values represent unknown dimension sizes.
        Size

        % Format  Dimension labels
        %   A character vector of dimension labels or ''. Each character
        %   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
        Format
    end

    methods
        function layout = networkDataLayout(sz, fmt)
            arguments
                sz {iValidateSize}
                fmt {iValidateFormat} = ''
            end
            layout.Size = double(gather(sz));
            layout.Format = char(fmt);
            if ~isempty(layout.Format) && (length(layout.Format) ~= length(sz))
                error(message('deep:networkDataLayout:MismatchedLengths'));
            end
        end

        function dim = finddim(layout,label)
            % FINDDIM  Find dimension with specified label
            %   DIM = FINDDIM(LAYOUT,LABEL) returns the dimensions in LAYOUT
            %   that have the dimension label LABEL. If the data format of
            %   LAYOUT does not contain the dimension label LABEL, DIM is
            %   empty.
            arguments
                layout networkDataLayout
                label {iValidateFormat}
            end
            label = char(label);
            if ~isscalar(label)
                error(message('deep:finddim:InvalidInput'));
            end
            dim = find(layout.Format == label);
        end
    end

    methods(Hidden)
        function out = saveobj(layout)
            out.Version = 1;
            out.Size = layout.Size;
            out.Format = layout.Format;
        end
    end

    methods(Hidden, Static)
        function layout = loadobj(in)
            layout = networkDataLayout(in.Size, in.Format);
        end
    end
end

function iValidateFormat(fmt)
deep.internal.dlarray.mustBeDataFormatType(fmt);
fmt = char(fmt);
deep.internal.format.validateDataFormat(fmt);
end

function iValidateSize(sz)
if isdlarray(sz)
    error(message('deep:networkDataLayout:DlarraySize'));
end
if ~isnumeric(sz) || ~isrow(sz) || numel(sz) < 2
    error(message('deep:networkDataLayout:InvalidSize'));
end
sz = sz(~isnan(sz));
if ~isreal(sz) || any(sz < 0) || any(sz ~= fix(sz)) || any(~isfinite(sz))
    error(message('deep:networkDataLayout:InvalidSizeValues'));
end
end
