text: """Sequential model class.
  """
  from __future__ import absolute_import
  from __future__ import division
  from __future__ import print_function
  
  import warnings
  import copy
  
  from . import network
  from .training import Model
  from .base_layer import Layer
  from .input_layer import Input
  from .input_layer import InputLayer
  from .. import backend as K
  from .. import layers as layer_module
  
  try:
      import h5py
  except ImportError:
      h5py = None
  
  
  class Sequential(Model):
      """Linear stack of layers.
  
      # Arguments
          layers: list of layers to add to the model.
          name: Name given to the model
  
      # Example
  
      ```python
      # Optionally, the first layer can receive an `input_shape` argument:
      model = Sequential()
      model.add(Dense(32, input_shape=(500,)))
  
      # Afterwards, we do automatic shape inference:
      model.add(Dense(32))
  
      # This is identical to the following:
      model = Sequential()
      model.add(Dense(32, input_dim=500))
  
      # And to the following:
      model = Sequential()
      model.add(Dense(32, batch_input_shape=(None, 500)))
  
      # Note that you can also omit the `input_shape` argument:
      # In that case the model gets built the first time you call `fit` (or other
      # training and evaluation methods).
      model = Sequential()
      model.add(Dense(32))
      model.add(Dense(32))
      model.compile(optimizer=optimizer, loss=loss)
  
      # This builds the model for the first time:
      model.fit(x, y, batch_size=32, epochs=10)
  
      # Note that when using this delayed-build pattern
      # (no input shape specified),
      # the model doesn't have any weights until the first call
      # to a training/evaluation method (since it isn't yet built):
      model = Sequential()
      model.add(Dense(32))
      model.add(Dense(32))
      model.weights  # returns []
  
      # Whereas if you specify the input shape, the model gets built continuously
      # as you are adding layers:
      model = Sequential()
      model.add(Dense(32, input_shape=(500,)))
      model.add(Dense(32))
      model.weights  # returns list of length 4
  
      # When using the delayed-build pattern (no input shape specified), you can
      # choose to manually build your model by calling
      # `build(batch_input_shape)`:
      model = Sequential()
      model.add(Dense(32))
      model.add(Dense(32))
      model.build((None, 500))
      model.weights  # returns list of length 4
      ```
      """
  
      def __init__(self, layers=None, name=None):
          super(Sequential, self).__init__(name=name)
          self._build_input_shape = None
  
          # Add to the model any layers passed to the constructor.
          if layers:
              for layer in layers:
                  self.add(layer)
  
      @property
      def layers(self):
          # Historically, `sequential.layers` only returns layers that were added
          # via `add`, and omits the auto-generated `InputLayer`
          # that comes at the bottom of the stack.
          if self._layers and isinstance(self._layers[0], InputLayer):
              return self._layers[1:]
          return self._layers
  
      @property
      def model(self):
          # Historically, `Sequential` was once
          # implemented as a wrapper for `Model` which maintained
          # its underlying `Model` as the `model` property.
          # We keep it for compatibility reasons.
          warnings.warn('`Sequential.model` is deprecated. '
                        '`Sequential` is a subclass of `Model`, you can '
                        'just use your `Sequential` instance directly.')
          return self
  
      def add(self, layer):
          """Adds a layer instance on top of the layer stack.
  
          # Arguments
              layer: layer instance.
  
          # Raises
              TypeError: If `layer` is not a layer instance.
              ValueError: In case the `layer` argument does not
                  know its input shape.
              ValueError: In case the `layer` argument has
                  multiple output tensors, or is already connected
                  somewhere else (forbidden in `Sequential` models).
          """
          if not isinstance(layer, Layer):
              raise TypeError('The added layer must be '
                              'an instance of class Layer. '
                              'Found: ' + str(layer))
          self.built = False
          if not self._layers:
              set_inputs = False
              # First layer in model: check that it is an input layer.
              if not isinstance(layer, InputLayer):
                  # Create an input tensor and call `layer` on the input tensor.
                  # First, we need to infer the expected input shape and dtype.
                  first_layer = layer
                  if isinstance(layer, (Model, Sequential)):
                      # We were passed a model as first layer.
                      # This requires a specific way to figure out the
                      # input shape and dtype.
                      if not layer.layers:
                          raise ValueError('Cannot add an empty model '
                                           'to a `Sequential` model.')
                      # In case of nested models: recover the first layer
                      # of the deepest model to infer input shape and dtype.
                      first_layer = layer.layers[0]
                      while isinstance(first_layer, (Model, Sequential)):
                          first_layer = first_layer.layers[0]
  
                  if hasattr(first_layer, 'batch_input_shape'):
                      batch_shape = first_layer.batch_input_shape
                      dtype = first_layer.dtype
                      # Instantiate the input layer.
                      x = Input(
                          batch_shape=batch_shape,
                          dtype=dtype,
                          name=layer.name + '_input')
                      # This will build the current layer
                      # and create the node connecting the current layer
                      # to the input layer we just created.
                      layer(x)
                      set_inputs = True
              else:
                  # Corner case where the user passes an InputLayer via `add`.
                  assert len(layer._inbound_nodes[-1].output_tensors) == 1
                  set_inputs = True
  
              if set_inputs:
                  if len(layer._inbound_nodes[-1].output_tensors) != 1:
                      raise ValueError('All layers in a Sequential model '
                                       'should have a single output tensor. '
                                       'For multi-output layers, '
                                       'use the functional API.')
                  self.outputs = [layer._inbound_nodes[-1].output_tensors[0]]
                  self.inputs = network.get_source_inputs(self.outputs[0])
          elif self.outputs:
              output_tensor = layer(self.outputs[0])
              if isinstance(output_tensor, list):
                  raise TypeError('All layers in a Sequential model '
                                  'should have a single output tensor. '
                                  'For multi-output layers, '
                                  'use the functional API.')
              self.outputs = [output_tensor]
          if self.inputs:
              self.build()
          else:
              self._layers.append(layer)
  
      def pop(self):
          """Removes the last layer in the model.
  
          # Raises
              TypeError: if there are no layers in the model.
          """
          if not self.layers:
              raise TypeError('There are no layers in the model.')
  
          self._layers.pop()
          self.built = False
          if not self.layers:
              self.outputs = None
              self.inputs = None
          elif self.outputs:
              self.layers[-1]._outbound_nodes = []
              self.outputs = [self.layers[-1].output]
              self.build()
  
      def build(self, input_shape=None):
          if input_shape and not self.inputs:
              batch_shape = tuple(input_shape)
              dtype = K.floatx()
              x = Input(batch_shape=batch_shape,
                        dtype=dtype,
                        name=self.name + '_input')
              self.inputs = [x]
              for layer in self._layers:
                  x = layer(x)
              self.outputs = [x]
              self._build_input_shape = input_shape
  
          if self.inputs:
              self._init_graph_network(self.inputs,
                                       self.outputs,
                                       name=self.name)
              self.built = True
  
      def predict_proba(self, x, batch_size=32, verbose=0):
          """Generates class probability predictions for the input samples.
  
          The input samples are processed batch by batch.
  
          # Arguments
              x: input data, as a Numpy array or list of Numpy arrays
                  (if the model has multiple inputs).
              batch_size: integer.
              verbose: verbosity mode, 0 or 1.
  
          # Returns
              A Numpy array of probability predictions.
          """
          preds = self.predict(x, batch_size, verbose)
          if preds.min() < 0. or preds.max() > 1.:
              warnings.warn('Network returning invalid probability values. '
                            'The last layer might not normalize predictions '
                            'into probabilities '
                            '(like softmax or sigmoid would).')
          return preds
  
      def predict_classes(self, x, batch_size=32, verbose=0):
          """Generate class predictions for the input samples.
  
          The input samples are processed batch by batch.
  
          # Arguments
              x: input data, as a Numpy array or list of Numpy arrays
                  (if the model has multiple inputs).
              batch_size: integer.
              verbose: verbosity mode, 0 or 1.
  
          # Returns
              A numpy array of class predictions.
          """
          proba = self.predict(x, batch_size=batch_size, verbose=verbose)
          if proba.shape[-1] > 1:
              return proba.argmax(axis=-1)
          else:
              return (proba > 0.5).astype('int32')
  
      def get_config(self):
          layer_configs = []
          for layer in self.layers:
              layer_configs.append({
                  'class_name': layer.__class__.__name__,
                  'config': layer.get_config()
              })
          config = {
              'name': self.name,
              'layers': copy.deepcopy(layer_configs)
          }
          if self._build_input_shape:
              config['build_input_shape'] = self._build_input_shape
          return config
  
      @classmethod
      def from_config(cls, config, custom_objects=None):
          if 'name' in config:
              name = config['name']
              build_input_shape = config.get('build_input_shape')
              layer_configs = config['layers']
          else:  # legacy config file
              name = build_input_shape = None
              layer_configs = config
          model = cls(name=name)
          for conf in layer_configs:
              layer = layer_module.deserialize(conf,
                                               custom_objects=custom_objects)
              model.add(layer)
          if not model.inputs and build_input_shape:
              model.build(build_input_shape)
          return model
  