classdef SeriesNetwork < nnet.cnn.network.HeterogeneousSeriesNetwork
    % SeriesNetwork   A neural network with layers arranged in a series
    %
    %   A series network is one where the layers are arranged one after the
    %   other. There is a single input and a single output.
    %
    %   SeriesNetwork properties:
    %       Layers                  - The layers of the network
    %       InputNames              - The names of the input layers
    %       OutputNames             - The names of the output layers
    %
    %   SeriesNetwork methods:
    %       predict                 - Run the network on input data
    %       classify                - Classify data with a network
    %       activations             - Compute specific network layer activations
    %       predictAndUpdateState   - Predict on data and update network state
    %       classifyAndUpdateState  - Classify data and update network state
    %       resetState              - Reset network state
    %       plot                    - Plot a diagram of the network
    %       layerGraph              - Convert to a layer graph object
    %
    %   Example:
    %       Train a convolutional neural network on some synthetic images
    %       of handwritten digits. Then run the trained network on a test
    %       set, and calculate the accuracy.
    %
    %       [XTrain, TTrain] = digitTrain4DArrayData;
    %
    %       layers = [ ...
    %           imageInputLayer([28 28 1])
    %           convolution2dLayer(5,20)
    %           reluLayer()
    %           maxPooling2dLayer(2,'Stride',2)
    %           fullyConnectedLayer(10)
    %           softmaxLayer()
    %           classificationLayer()];
    %       options = trainingOptions('sgdm', 'Plots', 'training-progress');
    %       net = trainNetwork(XTrain, TTrain, layers, options);
    %
    %       [XTest, TTest] = digitTest4DArrayData;
    %
    %       YTest = classify(net, XTest);
    %       accuracy = sum(YTest == TTest)/numel(TTest)
    %
    %   See also trainNetwork.

    %   Copyright 2015-2022 The MathWorks, Inc.

    properties(Dependent, SetAccess = private)
        % Layers   The layers of the network
        %   The array of layers for the network. Each layer has different
        %   properties depending on what type of layer it is. The first
        %   layer is always an input layer, and the last layer is always an
        %   output layer.
        Layers

        % InputNames   Names of the input layers of the network
        InputNames

        % OutputNames   Names of the output layers of the network
        OutputNames
    end

    properties(Access = private)
        % UnderlyingDAGNetwork   A DAGNetwork object
        UnderlyingDAGNetwork
    end

    methods
        function layers = get.Layers(this)
            layers = this.UnderlyingDAGNetwork.Layers;
        end

        function val = get.InputNames(this)
            val = this.UnderlyingDAGNetwork.InputNames;
        end

        function val = get.OutputNames(this)
            val = this.UnderlyingDAGNetwork.OutputNames;
        end
    end

    methods(Access = public, Hidden)
        function this = SeriesNetwork(varargin)
            % SeriesNetwork    Constructor for series network

            % Overloaded constructor for different function signatures
            switch nargin
                case 1
                    this = SeriesNetworkFromLayers( this, varargin{:} );
                case 2
                    this = SeriesNetworkFromLayersAndMap( this, varargin{:} );
                otherwise
                    narginchk(1,2)
            end
        end
    end

    methods(Access = private, Hidden)
        function this = SeriesNetworkFromLayers(this, layers)
            % SeriesNetworkFromLayers    Constructor for series network
            %
            % layers is an heterogeneous array of nnet.cnn.layer.Layer

            % Create an internal to external layers map
            layersMap = iLayersMap( layers );

            % Retrieve the internal layers
            internalLayers = nnet.cnn.layer.Layer.getInternalLayers( layers );

            % Create the network
            this = SeriesNetworkFromLayersAndMap(this, internalLayers, layersMap);
        end

        function this = SeriesNetworkFromLayersAndMap(this, internalNetwork, layersMap)
            % SeriesNetworkFromLayersAndMap    Constructor for series
            % network
            %
            % internalNetwork is either a cell array of
            % nnet.internal.cnn.layer.Layer or a
            % nnet.internal.cnn.DAGNetwork
            % layersMap is a nnet.internal.cnn.layer.util.InternalExternalMap

            % Create the internal network

            if iIsLayers(internalNetwork)
                % When internalNetwork is a cell array of
                % nnet.internal.cnn.layer.Layer, construct the internal
                % DAGNetwork.
                internalNetwork = iCreateInternalNetwork( internalNetwork );
            end

            % Create a DAG network
            this.UnderlyingDAGNetwork = DAGNetwork( internalNetwork, layersMap );
        end
    end

    methods(Access = public)
        function Y = predict(this, X, varargin)
            % predict   Make predictions on data with a network
            %
            %   Y = predict(net, X) will compute predictions of the network
            %   net on the data X. The format of X will depend on the input
            %   layer for the network.
            %
            %   For an image input layer, X must be:
            %       - A single image.
            %       - A 4-D array of images, where the first three
            %         dimensions index the height, width and channels of an
            %         image, and the fourth dimension indexes the
            %         individual images.
            %       - A datastore, where the output of the datastore read
            %         function is one image, a cell array of images, or a
            %         table whose first column contains images.
            %       - A table, where the first column contains either image
            %         paths or images.
            %
            %   For a network with a sequence input layer, X must be:
            %       - A single time series represented by a numeric array
            %         of size C-by-S, where C is the number of data points
            %         per timestep, and S is the total number of timesteps.
            %       - A cell array of multiple time series, of size N-by-1
            %         where N is the number of observations, and each
            %         element of the cell array contains a C-by-S time
            %         series.
            %
            %   For a network with a feature input layer, X must be:
            %       - A single observation specified as a row vector.
            %       - A 2-D array of observations, where the first dimension indexes
            %         the observations and the second dimension indexes the features.
            %       - A datastore, where the output of the datastore read function is
            %         a cell array or a table containing feature vectors in the first
            %         column.
            %       - A table, where each column represents a feature of the dataset.
            %
            %   For a network with a point cloud input layer, X must be:
            %       - A single observation of point cloud data specified as
            %         a 2-D or 3-D numeric array.
            %       - A 3-D or 4-D numeric array of point clouds, where the
            %         second to last dimension indexes the channels and the
            %         last dimension indexes the individual observations.
            %       - A datastore, where the output of the datastore read
            %         function is a cell array containing point cloud data
            %         as a numeric array.
            %
            %   For a classification problem, Y will contain the predicted
            %   scores, arranged in an N-by-K matrix, where N is the number
            %   of observations, and K is the number of classes. If the
            %   output of the network is a sequence, scores will be an
            %   N-by-1 cell array and each element of scores will be a
            %   K-by-S numeric array, where S is the sequence length of the
            %   output. If the number of observations in X is one, scores
            %   will be a K-by-S numeric array.
            %
            %   For a regression problem, Y will contain the predicted
            %   responses, arranged in an N-by-R matrix, where N is the
            %   number of observations, and R is the number of responses,
            %   or in an H-by-C-by-N 3-D array, where N is the number of
            %   observations and H-by-C is the size of a single response,
            %   or in an H-by-W-by-C-by-N 4-D array, where N is the number
            %   of observations and H-by-W-by-C is the size of a single
            %   response.
            %
            %   Y = predict(net, X, 'PARAM1', VAL1, ...) will compute
            %   predictions with the following optional name/value pairs:
            %
            %       'MiniBatchSize'     - The size of the mini-batches for
            %                             computing predictions. Larger
            %                             mini-batch sizes lead to faster
            %                             predictions, at the cost of more
            %                             memory. The default is 128.
            %
            %       'ExecutionEnvironment'
            %                           - The execution environment for the
            %                             network. This determines what
            %                             hardware resources will be used
            %                             to run the network.
            %                               - 'auto' - Use a GPU if it is
            %                                 available, otherwise use the
            %                                 CPU.
            %                               - 'gpu' - Use the GPU. To use a
            %                                 GPU, you must have Parallel
            %                                 Computing Toolbox(TM) and a
            %                                 supported GPU device. If a
            %                                 suitable GPU is not available,
            %                                 predict returns an error
            %                                 message.
            %                               - 'cpu' - Use the CPU.
            %                               - 'multi-gpu' - Use multiple GPUs
            %                                 on one machine, using a local
            %                                 parallel pool. If no pool is
            %                                 open, one is opened with one
            %                                 worker per supported GPU
            %                                 device.
            %                               - 'parallel' - Use a compute
            %                                 cluster. If no pool is open,
            %                                 one is opened using the
            %                                 default cluster profile. If
            %                                 the pool has access to GPUs
            %                                 then they will be used and
            %                                 excess workers will be idle.
            %                                 If the pool has no GPUs then
            %                                 prediction will take place on
            %                                 all cluster CPUs.
            %                             The default is 'auto'.
            %
            %       'Acceleration'      - Optimizations that can improve
            %                             performance at the expense of
            %                             some overhead on the first call
            %                             and possible additional memory
            %                             usage.
            %                               - 'auto' - Automatically select
            %                                 optimizations suitable for
            %                                 the input network and
            %                                 environment.
            %                               - 'mex' - (GPU only) Generate
            %                                 and execute a MEX function.
            %                                 Subsequent calls with the
            %                                 same network and options use
            %                                 the MEX function. This option
            %                                 is not supported when
            %                                 'ExecutionEnvironment' value
            %                                 is either 'parallel' or
            %                                 'multi-gpu'.
            %                               - 'none' - Disable all
            %                                 acceleration.
            %                             The default is 'auto'.
            %
            %       'ReturnCategorical' 
            %                           - Option to return outputs of
            %                             classification layers as
            %                             categorical, specified as true or
            %                             false. The default is false.
            %
            %       'SequenceLength'
            %                           - Strategy to determine the length
            %                             of the sequences used per
            %                             mini-batch. Options are:
            %                               - 'longest' to pad all
            %                                 sequences in a batch to the
            %                                 length of the longest
            %                                 sequence.
            %                               - 'shortest' to truncate all
            %                                 sequences in a batch to the
            %                                 length of the shortest
            %                                 sequence.
            %                               - Positive integer - For each
            %                                 mini-batch, pad the sequences
            %                                 to the nearest multiple of
            %                                 the specified length that is
            %                                 greater than the longest
            %                                 sequence length in the
            %                                 mini-batch, and then split
            %                                 the sequences into smaller
            %                                 sequences of the specified
            %                                 length. If splitting occurs,
            %                                 then the software creates
            %                                 extra mini-batches.
            %                             The default is 'longest'.
            %
            %       'SequencePaddingValue'
            %                           - Scalar value used to pad
            %                             sequences where necessary. The
            %                             default is 0.
            %
            %       'SequencePaddingDirection'
            %                           - Direction of padding or
            %                             truncation, specified as:
            %                                - 'right' - Pad or truncate
            %                                   sequences on the right.
            %                                - 'left' - Pad or truncate
            %                                   sequences on the left.
            %                             The default is 'right'.
            %
            %   See also SeriesNetwork/classify, SeriesNetwork/activations.

            if ~isscalar(this)
                error(message('nnet_cnn:DAGNetwork:NonScalarNetwork'));
            end
            Y = this.UnderlyingDAGNetwork.predict(X, varargin{:});

        end

        function [this, Y] = predictAndUpdateState(this, X, varargin)
            % predictAndUpdateState   Make predictions on data with a
            % network and update the network state
            %
            %   [net, Y] = predictAndUpdateState(net, X) computes
            %   predictions of the network net on the data X, and updates
            %   the state of the network net.
            %
            %   This method only supports networks with recurrent layers.
            %
            %   The format of X must be:
            %       - A single time series represented by a numeric array
            %         of size C-by-S, where C is the number of data points
            %         per timestep, and S is the total number of timesteps.
            %       - A cell array of multiple time series, of size N-by-1
            %         where N is the number of observations, and each
            %         element of the cell array contains a C-by-S time
            %         series.
            %
            %   For a classification problem, Y will contain the predicted
            %   scores, arranged in an N-by-K matrix, where N is the number
            %   of observations, and K is the number of classes. If the
            %   output of the network is a sequence, scores will be an
            %   N-by-1 cell array and each element of scores will be a
            %   K-by-S numeric array, where S is the sequence length of the
            %   output. If the number of observations in X is one, scores
            %   will be a K-by-S numeric array.
            %
            %   [net, Y] = predictAndUpdateState(net, X, 'PARAM1', VAL1,
            %   ...) computes predictions with the following optional
            %   name/value pairs:
            %
            %       'MiniBatchSize'     - The size of the mini-batches for
            %                             computing predictions. Larger
            %                             mini-batch sizes lead to faster
            %                             predictions, at the cost of more
            %                             memory. The default is 128.
            %
            %       'ExecutionEnvironment'
            %                           - The execution environment for the
            %                             network. This determines what
            %                             hardware resources will be used
            %                             to run the network.
            %                               - 'auto' - Use a GPU if it is
            %                                 available, otherwise use the
            %                                 CPU.
            %                               - 'gpu' - Use the GPU. To use a
            %                                 GPU, you must have Parallel
            %                                 Computing Toolbox(TM) and a
            %                                 supported GPU device. If a
            %                                 suitable GPU is not available,
            %                                 predictAndUpdateState returns
            %                                 an error message.
            %                               - 'cpu' - Use the CPU.
            %                             The default is 'auto'.
            %
            %       'Acceleration'      - Optimizations that can improve
            %                             performance at the expense of
            %                             some overhead on the first call
            %                             and possible additional memory
            %                             usage.
            %                               - 'auto' - Automatically select
            %                                 optimizations suitable for
            %                                 the input network and
            %                                 environment.
            %                               - 'none' - Disable all
            %                                 acceleration.
            %                             The default is 'auto'.
            %
            %       'ReturnCategorical' 
            %                           - Option to return outputs of
            %                             classification layers as
            %                             categorical, specified as true or
            %                             false. The default is false.
            %
            %       'SequenceLength'
            %                           - Strategy to determine the length
            %                             of the sequences used per
            %                             mini-batch. Options are:
            %                               - 'longest' to pad all
            %                                 sequences in a batch to the
            %                                 length of the longest
            %                                 sequence.
            %                               - 'shortest' to truncate all
            %                                 sequences in a batch to the
            %                                 length of the shortest
            %                                 sequence.
            %                               - Positive integer - For each
            %                                 mini-batch, pad the sequences
            %                                 to the nearest multiple of
            %                                 the specified length that is
            %                                 greater than the longest
            %                                 sequence length in the
            %                                 mini-batch, and then split
            %                                 the sequences into smaller
            %                                 sequences of the specified
            %                                 length. If splitting occurs,
            %                                 then the software creates
            %                                 extra mini-batches.
            %                             The default is 'longest'.
            %
            %       'SequencePaddingValue'
            %                           - Scalar value used to pad
            %                             sequences where necessary. The
            %                             default is 0.
            %
            %       'SequencePaddingDirection'
            %                           - Direction of padding or
            %                             truncation, specified as:
            %                                - 'right' - Pad or truncate
            %                                   sequences on the right.
            %                                - 'left' - Pad or truncate
            %                                   sequences on the left.
            %                             The default is 'right'.
            %
            %   See also SeriesNetwork/classifyAndUpdateState.

            if ~isscalar(this)
                error(message('nnet_cnn:DAGNetwork:NonScalarNetwork'));
            end
            [this.UnderlyingDAGNetwork, Y] = this.UnderlyingDAGNetwork.predictAndUpdateState(X, varargin{:});
        end

        function [labels, scores] = classify(this, X, varargin)
            % classify   Classify data with a network
            %
            %   [labels, scores] = classify(net, X) will classify the data
            %   X using the network net. labels will be an N-by-1
            %   categorical vector where N is the number of observations,
            %   and scores will be an N-by-K matrix where K is the number
            %   of output classes. If the output of the network is a
            %   sequence, labels and scores will be N-by-1 cell arrays.
            %   Each element of labels will be a 1-by-S categorical array
            %   and each element of scores will be a K-by-S numeric array,
            %   where S is the sequence length of the output. The format of
            %   X will depend on the input layer for the network.
            %
            %   For an image input layer, X must be:
            %       - A single image.
            %       - A 4-D array of images, where the first three
            %         dimensions index the height, width and channels of an
            %         image, and the fourth dimension indexes the
            %         individual images.
            %       - A datastore, where the output of the datastore read
            %         function is one image, a cell array of images, or a
            %         table whose first column contains images.
            %       - A table, where the first column contains either image
            %         paths or images.
            %
            %   For a network with a sequence input layer, X must be:
            %       - A single time series represented by a numeric array
            %         of size C-by-S, where C is the number of data points
            %         per timestep, and S is the total number of timesteps.
            %       - A cell array of multiple time series, of size N-by-1
            %         where N is the number of observations, and each
            %         element of the cell array contains a C-by-S time
            %         series.
            %
            %   For a network with a feature input layer, X must be:
            %       - A single observation specified as a row vector.
            %       - A 2-D array of observations, where the first dimension indexes
            %         the observations and the second dimension indexes the features.
            %       - A datastore, where the output of the datastore read function is
            %         a cell array or a table containing feature vectors in the first
            %         column.
            %       - A table, where each column represents a feature of the dataset.
            %
            %   For a network with a point cloud input layer, X must be:
            %       - A single observation of point cloud data specified as
            %         a 2-D or 3-D numeric array.
            %       - A 3-D or 4-D numeric array of point clouds, where the
            %         second to last dimension indexes the channels and the
            %         last dimension indexes the individual observations.
            %       - A datastore, where the output of the datastore read
            %         function is a cell array containing point cloud data
            %         as a numeric array.
            %
            %   [labels, scores] = classify(net, X, 'PARAM1', VAL1, ...)
            %   specifies optional name-value pairs described below:
            %
            %       'MiniBatchSize'     - The size of the mini-batches for
            %                             computing predictions. Larger
            %                             mini-batch sizes lead to faster
            %                             predictions, at the cost of more
            %                             memory. The default is 128.
            %
            %       'ExecutionEnvironment'
            %                           - The execution environment for the
            %                             network. This determines what
            %                             hardware resources will be used
            %                             to run the network.
            %                               - 'auto' - Use a GPU if it is
            %                                 available, otherwise use the
            %                                 CPU.
            %                               - 'gpu' - Use the GPU. To use a
            %                                 GPU, you must have Parallel
            %                                 Computing Toolbox(TM) and a
            %                                 supported GPU device. If a
            %                                 suitable GPU is not available,
            %                                 classify returns an error
            %                                 message.
            %                               - 'cpu' - Use the CPU.
            %                               - 'multi-gpu' - Use multiple GPUs
            %                                 on one machine, using a local
            %                                 parallel pool. If no pool is
            %                                 open, one is opened with one
            %                                 worker per supported GPU
            %                                 device.
            %                               - 'parallel' - Use a compute
            %                                 cluster. If no pool is open,
            %                                 one is opened using the
            %                                 default cluster profile. If
            %                                 the pool has access to GPUs
            %                                 then they will be used and
            %                                 excess workers will be idle.
            %                                 If the pool has no GPUs then
            %                                 prediction will take place on
            %                                 all cluster CPUs.
            %                             The default is 'auto'.
            %
            %       'Acceleration'      - Optimizations that can improve
            %                             performance at the expense of
            %                             some overhead on the first call
            %                             and possible additional memory
            %                             usage.
            %                               - 'auto' - Automatically select
            %                                 optimizations suitable for
            %                                 the input network and
            %                                 environment.
            %                               - 'mex' - (GPU only) Generate
            %                                 and execute a MEX function.
            %                                 Subsequent calls with the
            %                                 same network and options use
            %                                 the MEX function. This option
            %                                 is not supported when
            %                                 'ExecutionEnvironment' value
            %                                 is either 'parallel' or
            %                                 'multi-gpu'.
            %                               - 'none' - Disable all
            %                                 acceleration.
            %                             The default is 'auto'.
            %
            %       'SequenceLength'
            %                           - Strategy to determine the length
            %                             of the sequences used per
            %                             mini-batch. Options are:
            %                               - 'longest' to pad all
            %                                 sequences in a batch to the
            %                                 length of the longest
            %                                 sequence.
            %                               - 'shortest' to truncate all
            %                                 sequences in a batch to the
            %                                 length of the shortest
            %                                 sequence.
            %                               - Positive integer - For each
            %                                 mini-batch, pad the sequences
            %                                 to the nearest multiple of
            %                                 the specified length that is
            %                                 greater than the longest
            %                                 sequence length in the
            %                                 mini-batch, and then split
            %                                 the sequences into smaller
            %                                 sequences of the specified
            %                                 length. If splitting occurs,
            %                                 then the software creates
            %                                 extra mini-batches.
            %                             The default is 'longest'.
            %
            %       'SequencePaddingValue'
            %                           - Scalar value used to pad
            %                             sequences where necessary. The
            %                             default is 0.
            %
            %       'SequencePaddingDirection'
            %                           - Direction of padding or
            %                             truncation, specified as:
            %                                - 'right' - Pad or truncate
            %                                   sequences on the right.
            %                                - 'left' - Pad or truncate
            %                                   sequences on the left.
            %                             The default is 'right'.
            %
            %   See also SeriesNetwork/predict, SeriesNetwork/activations.

            if ~isscalar(this)
                error(message('nnet_cnn:DAGNetwork:NonScalarNetwork'));
            end
            [labels, scores] = this.UnderlyingDAGNetwork.classify(X, varargin{:});
        end

        function [this, labels, scores] = classifyAndUpdateState(this, X, varargin)
            % classifyAndUpdateState   Classify data with a network and
            % update the network state
            %
            %   [net, labels, scores] = classifyAndUpdateState(net, X)
            %   classifies the data X using the network net, and updates
            %   its state. labels is an N-by-1 categorical vector where N
            %   is the number of observations, and scores is an N-by-K
            %   matrix where K is the number of output classes. If the
            %   output of the network is a sequence, labels and scores are
            %   N-by-1 cell arrays. Each element of labels is a 1-by-S
            %   categorical array and each element of scores is a K-by-S
            %   numeric array, where S is the sequence length of the
            %   output.
            %
            %   This method only supports networks with recurrent layers.
            %
            %   The format of X must be:
            %       - A single time series represented by a numeric array
            %         of size C-by-S, where C is the number of data points
            %         per timestep, and S is the total number of timesteps.
            %       - A cell array of multiple time series, of size N-by-1
            %         where N is the number of observations, and each
            %         element of the cell array contains a C-by-S time
            %         series.
            %
            %   [labels, scores] = classifyAndUpdateState(net, X, 'PARAM1',
            %   VAL1, ...) specifies optional name-value pairs described
            %   below:
            %
            %       'MiniBatchSize'     - The size of the mini-batches for
            %                             computing predictions. Larger
            %                             mini-batch sizes lead to faster
            %                             predictions, at the cost of more
            %                             memory. The default is 128.
            %
            %       'ExecutionEnvironment'
            %                           - The execution environment for the
            %                             network. This determines what
            %                             hardware resources will be used
            %                             to run the network.
            %                               - 'auto' - Use a GPU if it is
            %                                 available, otherwise use the
            %                                 CPU.
            %                               - 'gpu' - Use the GPU. To use a
            %                                 GPU, you must have Parallel
            %                                 Computing Toolbox(TM) and a
            %                                 supported GPU device. If a
            %                                 suitable GPU is not available,
            %                                 classifyAndUpdateState
            %                                 returns an error message.
            %                               - 'cpu' - Use the CPU.
            %                             The default is 'auto'.
            %
            %       'Acceleration'      - Optimizations that can improve
            %                             performance at the expense of
            %                             some overhead on the first call
            %                             and possible additional memory
            %                             usage.
            %                               - 'auto' - Automatically select
            %                                 optimizations suitable for
            %                                 the input network and
            %                                 environment.
            %                               - 'none' - Disable all
            %                                 acceleration.
            %                             The default is 'auto'.
            %
            %       'SequenceLength'
            %                           - Strategy to determine the length
            %                             of the sequences used per
            %                             mini-batch. Options are:
            %                               - 'longest' to pad all
            %                                 sequences in a batch to the
            %                                 length of the longest
            %                                 sequence.
            %                               - 'shortest' to truncate all
            %                                 sequences in a batch to the
            %                                 length of the shortest
            %                                 sequence.
            %                               - Positive integer - For each
            %                                 mini-batch, pad the sequences
            %                                 to the nearest multiple of
            %                                 the specified length that is
            %                                 greater than the longest
            %                                 sequence length in the
            %                                 mini-batch, and then split
            %                                 the sequences into smaller
            %                                 sequences of the specified
            %                                 length. If splitting occurs,
            %                                 then the software creates
            %                                 extra mini-batches.
            %                             The default is 'longest'.
            %
            %       'SequencePaddingValue'
            %                           - Scalar value used to pad
            %                             sequences where necessary. The
            %                             default is 0.
            %
            %       'SequencePaddingDirection'
            %                           - Direction of padding or
            %                             truncation, specified as:
            %                                - 'right' - Pad or truncate
            %                                   sequences on the right.
            %                                - 'left' - Pad or truncate
            %                                   sequences on the left.
            %                             The default is 'right'.
            %
            %   See also SeriesNetwork/predictAndUpdateState.
            if ~isscalar(this)
                error(message('nnet_cnn:DAGNetwork:NonScalarNetwork'));
            end
            [this.UnderlyingDAGNetwork, labels, scores] = this.UnderlyingDAGNetwork.classifyAndUpdateState(X, varargin{:});
        end

        function Y = activations(this, X, layerID, varargin)
            % activations   Computes network layer activations
            %
            %   Y = activations(net, X, layer) returns network
            %   activations for a specific layer. Network activations are
            %   computed by forward propagating input X through the network
            %   up to the specified layer. layer must be a numeric index
            %   or a character vector corresponding to one of the network
            %   layer names.
            %
            %   For a network with an image input layer, X must be:
            %       - A single image.
            %       - A 4-D array of images, where the first three
            %         dimensions index the height, width and channels of an
            %         image, and the fourth dimension indexes the
            %         individual images.
            %       - A 5-D array of images, where the first four
            %         dimensions correspond to the height, width, depth and
            %         channels of an image, and the fifth dimension
            %         indexes the individual images.
            %       - A datastore, where the output of the datastore read
            %         function is one image, a cell array of images, or a
            %         table whose first column contains images.
            %       - A table, where the first column contains either image
            %         paths or images.
            %
            %   For a network with a sequence input layer, X must be:
            %       - A single time series represented by a numeric array
            %         of size C-by-S, where C is the number of data points
            %         per timestep, and S is the total number of timesteps.
            %       - A cell array of multiple time series, of size N-by-1
            %         where N is the number of observations, and each
            %         element of the cell array contains a C-by-S time
            %         series.
            %
            %   For a network with a feature input layer, X must be:
            %       - A single observation specified as a row vector.
            %       - A 2-D array of observations, where the first dimension indexes
            %         the observations and the second dimension indexes the features.
            %       - A datastore, where the output of the datastore read function is
            %         a cell array or a table containing feature vectors in the first
            %         column.
            %       - A table, where each column represents a feature of the dataset.
            %
            %   For a network with a point cloud input layer, X must be:
            %       - A single observation of point cloud data specified as
            %         a 2-D or 3-D numeric array.
            %       - A 3-D or 4-D numeric array of point clouds, where the
            %         second to last dimension indexes the channels and the
            %         last dimension indexes the individual observations.
            %       - A datastore, where the output of the datastore read
            %         function is a cell array containing point cloud data
            %         as a numeric array.
            %
            %   By default for networks with an image input layer, Y will
            %   either be an H-by-W-by-C-by-N array or an
            %   H-by-W-by-D-by-C-by-N array for 2-D or 3-D images repectively,
            %   where H, W, D and C are the height, width, depth and
            %   number of channels for the chosen output as applicable.
            %   Each H-by-W-by-C or H-by-W-by-D-by-C sub-array is the
            %   output for a single observation. See the 'OutputAs'
            %   optional name-value pair for more details.
            %
            %   Y = activations(net, X, layer, 'PARAM1', VAL1, ...)
            %   specifies optional name-value pairs described below:
            %
            %       'OutputAs'    - Format of output activations, specified
            %                       as one of the following:
            %                         - 'channels' - The output is either a
            %                           C-by-N array, H-by-W-by-C-by-N array
            %                           or an H-by-W-by-D-by-C-by-N array,
            %                           where H, W, D and C are the
            %                           height, width, depth and  number of
            %                           channels for the output of the
            %                           chosen layer as applicable, and N
            %                           indexes each observation.
            %                         - 'rows' - The output is an
            %                           N-by-M matrix, where N is the
            %                           number of observations, and M is
            %                           the number of output elements in
            %                           the chosen layer. Each row of the
            %                           matrix is the output for a single
            %                           observation. This option does not
            %                           support layers with sequence
            %                           output.
            %                         - 'columns' - The output is an
            %                           M-by-N matrix, where M is the
            %                           number of output elements from the
            %                           chosen layer, and N is the number
            %                           of observations. Each column of the
            %                           matrix is the output for a single
            %                           observation. This option does not
            %                           support layers with sequence
            %                           output.
            %                       The default is 'channels'.
            %
            %       'MiniBatchSize'
            %                     - The size of the mini-batches for
            %                       computing predictions. Larger
            %                       mini-batch sizes lead to faster
            %                       predictions, at the cost of more
            %                       memory. The default is 128.
            %
            %       'ExecutionEnvironment'
            %                     - The execution environment for the
            %                       network. This determines what hardware
            %                       resources will be used to run the
            %                       network.
            %                         - 'auto' - Use a GPU if it is
            %                           available, otherwise use the CPU.
            %                         - 'gpu' - Use the GPU. To use a
            %                           GPU, you must have Parallel
            %                           Computing Toolbox(TM) and a
            %                           supported GPU device. If a suitable
            %                           GPU is not available, activations
            %                           returns an error
            %                           message.
            %                         - 'cpu' - Use the CPU.
            %                         - 'multi-gpu' - Use multiple GPUs on
            %                            one machine, using a local parallel
            %                            pool. If no pool is open, one is
            %                            opened with one worker per
            %                            supported GPU device.
            %                         - 'parallel' - Use a compute cluster.
            %                            If no pool is open, one is opened
            %                            using the default cluster profile.
            %                            If the pool has access to GPUs
            %                            then they will be used and excess
            %                            workers will be idle. If the pool
            %                            has no GPUs then prediction will
            %                            take place on all cluster CPUs.
            %                       The default is 'auto'.
            %
            %       'Acceleration'
            %                     - Optimizations that can improve
            %                       performance at the expense of some
            %                       overhead on the first call and possible
            %                       additional memory usage.
            %                         - 'auto' - Automatically select
            %                           optimizations suitable for the
            %                           input network and environment.
            %                         - 'mex' - (GPU only) Generate and
            %                           execute a MEX function. Subsequent
            %                           calls with the same network and
            %                           options use the MEX function. This
            %                           option is not supported when
            %                           'ExecutionEnvironment' value is
            %                           either 'parallel' or 'multi-gpu'.
            %                         - 'none' - Disable all acceleration.
            %                       The default is 'auto'.
            %
            %       'SequenceLength'
            %                           - Strategy to determine the length
            %                             of the sequences used per
            %                             mini-batch. Options are:
            %                               - 'longest' to pad all
            %                                 sequences in a batch to the
            %                                 length of the longest
            %                                 sequence.
            %                               - 'shortest' to truncate all
            %                                 sequences in a batch to the
            %                                 length of the shortest
            %                                 sequence.
            %                               - Positive integer - For each
            %                                 mini-batch, pad the sequences
            %                                 to the nearest multiple of
            %                                 the specified length that is
            %                                 greater than the longest
            %                                 sequence length in the
            %                                 mini-batch, and then split
            %                                 the sequences into smaller
            %                                 sequences of the specified
            %                                 length. If splitting occurs,
            %                                 then the software creates
            %                                 extra mini-batches.
            %                             The default is 'longest'.
            %
            %       'SequencePaddingValue'
            %                           - Scalar value used to pad
            %                             sequences where necessary. The
            %                             default is 0.
            %
            %       'SequencePaddingDirection'
            %                           - Direction of padding or
            %                             truncation, specified as:
            %                                - 'right' - Pad or truncate
            %                                   sequences on the right.
            %                                - 'left' - Pad or truncate
            %                                   sequences on the left.
            %                             The default is 'right'.
            %
            %   See also SeriesNetwork/predict, SeriesNetwork/classify.
            if ~isscalar(this)
                error(message('nnet_cnn:DAGNetwork:NonScalarNetwork'));
            end
            layerID = convertStringsToChars(layerID);
            try
                Y = this.UnderlyingDAGNetwork.activationsSeries(X, layerID, varargin{:});
            catch ex
                if ex.identifier == "nnet_cnn:internal:cnn:layer:FullyConnected:InvalidInputData"
                    ex = MException(message(...
                        'nnet_cnn:DAGNetwork:ImageSizeBreaksFullyConnectedLayerForActivations', ...
                        ex.message));
                end
                nnet.internal.cnn.util.rethrowDLExceptions(ex)
            end
        end

        function this = resetState( this )
            % resetState   Reset the state of a recurrent neural network
            %
            %  net = resetState(net) returns the state of a recurrent
            %  neural network to an initial state of zeros.
            arguments
                this(1,1)
            end
            this.UnderlyingDAGNetwork = this.UnderlyingDAGNetwork.resetState();
        end

        function plot(this)
            % plot   Plot a diagram of the network
            %
            %   plot(net) plots a diagram of the network net. Each layer in
            %   the diagram is labelled by its name.
            arguments
                this(1,1)
            end
            this.UnderlyingDAGNetwork.plot();
        end

        function out = saveobj(this)
            out.Version = 1.0;
            out.Layers = this.Layers; % User visible layers
        end

        function lg = layerGraph(this)
            % layerGraph   Convert SeriesNetwork to layer graph
            %
            %   lgraph = layerGraph(net) extracts the layer graph from the SeriesNetwork net.
            arguments
                this(1,1)
            end
            lg = this.UnderlyingDAGNetwork.layerGraph();
        end
    end

    methods(Static)
        function this = loadobj(in)
            this = SeriesNetwork( in.Layers );
        end
    end

    methods(Hidden)
        function layerGraph = getOptimizedLayerGraph(this)
            % getOptimizedLayerGraph  Get the internal optimized layer
            % graph for this network, from the prediction cache if
            % necessary
            layerGraph = this.UnderlyingDAGNetwork.getOptimizedLayerGraph();
        end

        function dlAccelInfo = getdlAccelInfo(this, dlkey)
            % getdlAccelInfo This is a hidden method that returns
            % dlAccel_map information corresponding to the input key
            % TODO Remove dlaccel legacy code g2224358
            dlAccelInfo = this.UnderlyingDAGNetwork.getdlAccelInfo(dlkey);
        end

        % getInternalDAGNetwork This is a hidden method that returns
        % the underlying internal dag representation of the network
        function internalDAGNetwork = getInternalDAGNetwork(this)
            internalDAGNetwork = this.UnderlyingDAGNetwork.getInternalDAGNetwork();
        end

        % getOutputSize   Return the output size for a given layer index
        % layeridx is an index in the topologically sorted array of layers
        % inputSize        - is a size vector for the input layer of the
        %                    network. Set to empty to take the size from
        %                    the input layer.
        % outputAsFeatures - If true, returns sizes and formats for spatial
        % (Default: true)    and channel dimensions only. If false, batch
        %                    and sequence dimensions may be added if not
        %                    provided in the input and may be NaN to
        %                    indicate variable size support.
        %
        % layerOutputSize  - is the output size for layer layeridx. If
        %                    layer layeridx has multiple outputs then
        %                    layerOutputSize is a cell array of output
        %                    sizes for layer layeridx.
        % layerOutputFormat- also provides the semantics of each output
        %                    dimension as a format char vector containing
        %                    the labels 'SCBTU'.
        function [layerOutputSize, layerOutputFormat] = getOutputSize(this, layeridx, inputSize, outputAsFeatures)
            arguments
                this
                layeridx
                inputSize
                outputAsFeatures = true
            end
            [layerOutputSize, layerOutputFormat] = this.UnderlyingDAGNetwork.getOutputSize(...
                layeridx, inputSize, outputAsFeatures);
        end

        function tf = isequal(this, net, varargin)
            % isequal   Check equality of SeriesNetwork objects
            %
            %   tf = isequal(net1,net2) checks equality of the
            %   SeriesNetwork or DAGNetwork objects net1 and net2. The
            %   function returns 1 (true) when the properties and
            %   architectures match and 0 (false) otherwise.
            %
            %   Note that if one input is a SeriesNetwork or DAGNetwork
            %   object, and the other is a LayerGraph object, then the
            %   function returns 0 (false).
            %
            %   tf = isequal(net1,...,netN) checks equality of the N
            %   SeriesNetwork or DAGNetwork objects net1, …, netN.

            if isempty(this)
                tf = builtin('isequal', this, net, varargin{:});
            elseif isscalar(this)
                tf = this.UnderlyingDAGNetwork.isequal(net, varargin{:});
            else
                tf = iCompare(this, net, @isequal, varargin{:});
            end
        end

        function tf = isequaln(this, net, varargin)
            % isequaln   Check equality of SeriesNetwork objects ignoring NaN values
            %
            %   tf = isequaln(net1,net2) checks equality of the
            %   SeriesNetwork or DAGNetwork objects net1 and net2, ignoring
            %   NaN values. The function returns 1 (true) when the
            %   properties and architectures match and 0 (false) otherwise.
            %
            %   Note that if one input is a SeriesNetwork or DAGNetwork
            %   object, and the other is a LayerGraph object, then the
            %   function returns 0 (false).
            %
            %   tf = isequaln(net1,...,netN) checks equality of the N
            %   SeriesNetwork or DAGNetwork object net1, …, netN, ignoring
            %   NaN values.

            if isempty(this)
                tf = builtin('isequaln', this, net, varargin{:});
            elseif isscalar(this)
                tf = this.UnderlyingDAGNetwork.isequaln(net, varargin{:});
            else
                tf = iCompare(this, net, @isequaln, varargin{:});
            end
        end
    end

    methods (Access = {?nnet.internal.cnn.coder.MexNetworkCache})
        function mexNetworkCache = getMexNetworkCache(this)
            % getMexNetworkCache This is a private method that returns the
            % MexNetworkCache object attached to this network
            mexNetworkCache = getMexNetworkCache(this.UnderlyingDAGNetwork);
        end
    end
end

function layersMap = iLayersMap( layers )
layersMap = nnet.internal.cnn.layer.util.InternalExternalMap( layers );
end

function internalNetwork = iCreateInternalNetwork( internalLayers )
internalLayers = nnet.internal.cnn.layer.util.assignUniqueLayerNames(internalLayers);
internalLayerGraph = nnet.internal.cnn.LayerGraph( internalLayers );
[internalLayerGraph, topologicalOrder] = internalLayerGraph.toposort();
internalNetwork = nnet.internal.cnn.DAGNetwork( internalLayerGraph, topologicalOrder );
end

function tf = iIsLayers( internalLayers )
% If internalLayers is a non-empty cell array where the first element is a
% Layer, we assume internalLayers is a suitable cell array of Layers for
% constructing a SeriesNetwork.
tf = iscell(internalLayers) && ~isempty(internalLayers) && isa(internalLayers{1},'nnet.internal.cnn.layer.Layer');
end
function tf = iCompare(net1, net2, fun, varargin)
% Compare net1 and net2.
% Check sizes
if ~isequal(size(net1), size(net2))
    tf = false;
    return
else
    % Ensure arrays are flattened
    firstNet = net1(:);
    net2 = net2(:);

    for i = 1:numel(net1)
        if isequal(fun, @isequal)
            tf = firstNet(i).UnderlyingDAGNetwork.isequal(net2(i));
        else
            tf = firstNet(i).UnderlyingDAGNetwork.isequaln(net2(i));
        end
        if ~tf
            return
        end
    end
end

if nargin > 3
    % Deal with the case of more than two inputs by comparing net1
    % with each input separately.
    ii = 1;
    while tf && ii <= nargin-3
        tf = iCompare(net1, varargin{ii}, fun);
        ii = ii+1;
    end
    return;
end
end

