def __init__(self,
               units=64,
               mode=modes.Modes.TRAINING,
               inference_batch_size=1,
               return_sequences=False,
               use_peepholes=False,
               num_proj=128,
               unroll=False,
               stateful=False,
               name='LSTM',
               **kwargs):
    super(LSTM, self).__init__(**kwargs)

    self.mode = mode
    self.inference_batch_size = inference_batch_size
    self.units = units
    self.return_sequences = return_sequences
    self.num_proj = num_proj
    self.use_peepholes = use_peepholes
    self.stateful = stateful

    if mode != modes.Modes.TRAINING:  # in any inference mode
      # let's unroll lstm, so there is no symbolic loops / control flow
      unroll = True

    self.unroll = unroll

    if self.mode in (modes.Modes.TRAINING, modes.Modes.NON_STREAM_INFERENCE):
      if use_peepholes:
        self.lstm_cell = tf1.nn.rnn_cell.LSTMCell(
            num_units=units, use_peepholes=True, num_proj=num_proj, name='cell')
        self.lstm = tf.keras.layers.RNN(
            cell=self.lstm_cell,
            return_sequences=return_sequences,
            unroll=self.unroll,
            stateful=self.stateful)
      else:
        self.lstm = tf.keras.layers.LSTM(
            units=units,
            return_sequences=return_sequences,
            name='cell',
            unroll=self.unroll,
            stateful=self.stateful)
    if self.mode == modes.Modes.STREAM_INTERNAL_STATE_INFERENCE:
      # create state varaible for stateful streamable inference
      self.input_state1 = self.add_weight(
          name='input_state1',
          shape=[inference_batch_size, units],
          trainable=False,
          initializer=tf.zeros_initializer)
      if use_peepholes:
        # second state in peepholes LSTM has different dimensions with
        # the first state due to projection layer with dim num_proj
        self.input_state2 = self.add_weight(
            name='input_state2',
            shape=[inference_batch_size, num_proj],
            trainable=False,
            initializer=tf.zeros_initializer)
        self.lstm_cell = tf1.nn.rnn_cell.LSTMCell(
            num_units=units, use_peepholes=True, num_proj=num_proj, name='cell')
      else:
        # second state in the standard LSTM has the same dimensions with
        # the first state
        self.input_state2 = self.add_weight(
            name='input_state2',
            shape=[inference_batch_size, units],
            trainable=False,
            initializer=tf.zeros_initializer)
        self.lstm_cell = tf.keras.layers.LSTMCell(units=units, name='cell')
      self.lstm = None
    elif self.mode == modes.Modes.STREAM_EXTERNAL_STATE_INFERENCE:
      # in streaming mode with external state state,
      # state becomes an input output placeholders
      self.input_state1 = tf.keras.layers.Input(
          shape=(units,),
          batch_size=inference_batch_size,
          name=self.name + 'input_state1')
      if use_peepholes:
        self.input_state2 = tf.keras.layers.Input(
            shape=(num_proj,),
            batch_size=inference_batch_size,
            name=self.name + 'input_state2')
        self.lstm_cell = tf1.nn.rnn_cell.LSTMCell(
            num_units=units, use_peepholes=True, num_proj=num_proj)
      else:
        self.input_state2 = tf.keras.layers.Input(
            shape=(units,),
            batch_size=inference_batch_size,
            name=self.name + 'input_state2')
        self.lstm_cell = tf.keras.layers.LSTMCell(units=units, name='cell')
      self.lstm = None
      self.output_state1 = None
      self.output_state2 = None