class_docstr: Write me
      
code: def call(self, x, mask=None):
      # TODO: validate input shape
  
      assert len(x) == 3
      L_flat = x[0]
      mu = x[1]
      a = x[2]
  
      if self.mode == "full":
          # Create L and L^T matrix, which we use to construct the positive-definite matrix P.
          L = None
          LT = None
          if K.backend() == "theano":
              import theano.tensor as T
              import theano
  
              def fn(x, L_acc, LT_acc):
                  x_ = K.zeros((self.nb_actions, self.nb_actions))
                  x_ = T.set_subtensor(
                      x_[np.tril_indices(self.nb_actions)], x
                  )
                  diag = K.exp(T.diag(x_)) + K.epsilon()
                  x_ = T.set_subtensor(
                      x_[np.diag_indices(self.nb_actions)], diag
                  )
                  return x_, x_.T
  
              outputs_info = [
                  K.zeros((self.nb_actions, self.nb_actions)),
                  K.zeros((self.nb_actions, self.nb_actions)),
              ]
              results, _ = theano.scan(
                  fn=fn, sequences=L_flat, outputs_info=outputs_info
              )
              L, LT = results
          elif K.backend() == "tensorflow":
              import tensorflow as tf
  
              # Number of elements in a triangular matrix.
              nb_elems = (
                  self.nb_actions * self.nb_actions + self.nb_actions
              ) // 2
  
              # Create mask for the diagonal elements in L_flat. This is used to exponentiate
              # only the diagonal elements, which is done before gathering.
              diag_indeces = [0]
              for row in range(1, self.nb_actions):
                  diag_indeces.append(diag_indeces[-1] + (row + 1))
              diag_mask = np.zeros(
                  1 + nb_elems
              )  # +1 for the leading zero
              diag_mask[np.array(diag_indeces) + 1] = 1
              diag_mask = K.variable(diag_mask)
  
              # Add leading zero element to each element in the L_flat. We use this zero
              # element when gathering L_flat into a lower triangular matrix L.
              nb_rows = tf.shape(L_flat)[0]
              zeros = tf.expand_dims(
                  tf.tile(K.zeros((1,)), [nb_rows]), 1
              )
              try:
                  # Old TF behavior.
                  L_flat = tf.concat(1, [zeros, L_flat])
              except (TypeError, ValueError):
                  # New TF behavior
                  L_flat = tf.concat([zeros, L_flat], 1)
  
              # Create mask that can be used to gather elements from L_flat and put them
              # into a lower triangular matrix.
              tril_mask = np.zeros(
                  (self.nb_actions, self.nb_actions), dtype="int32"
              )
              tril_mask[np.tril_indices(self.nb_actions)] = range(
                  1, nb_elems + 1
              )
  
              # Finally, process each element of the batch.
              init = [
                  K.zeros((self.nb_actions, self.nb_actions)),
                  K.zeros((self.nb_actions, self.nb_actions)),
              ]
  
              def fn(a, x):
                  # Exponentiate everything. This is much easier than only exponentiating
                  # the diagonal elements, and, usually, the action space is relatively low.
                  x_ = K.exp(x) + K.epsilon()
                  # Only keep the diagonal elements.
                  x_ *= diag_mask
                  # Add the original, non-diagonal elements.
                  x_ += x * (1.0 - diag_mask)
                  # Finally, gather everything into a lower triangular matrix.
                  L_ = tf.gather(x_, tril_mask)
                  return [L_, tf.transpose(L_)]
  
              tmp = tf.scan(fn, L_flat, initializer=init)
              if isinstance(tmp, (list, tuple)):
                  # TensorFlow 0.10 now returns a tuple of tensors.
                  L, LT = tmp
              else:
                  # Old TensorFlow < 0.10 returns a shared tensor.
                  L = tmp[:, 0, :, :]
                  LT = tmp[:, 1, :, :]
          else:
              raise RuntimeError(
                  'Unknown Keras backend "{}".'.format(K.backend())
              )
          assert L is not None
          assert LT is not None
          P = K.batch_dot(L, LT)
      elif self.mode == "diag":
          if K.backend() == "theano":
              import theano.tensor as T
              import theano
  
              def fn(x, P_acc):
                  x_ = K.zeros((self.nb_actions, self.nb_actions))
                  x_ = T.set_subtensor(
                      x_[np.diag_indices(self.nb_actions)], x
                  )
                  return x_
  
              outputs_info = [
                  K.zeros((self.nb_actions, self.nb_actions)),
              ]
              P, _ = theano.scan(
                  fn=fn, sequences=L_flat, outputs_info=outputs_info
              )
          elif K.backend() == "tensorflow":
              import tensorflow as tf
  
              # Create mask that can be used to gather elements from L_flat and put them
              # into a diagonal matrix.
              diag_mask = np.zeros(
                  (self.nb_actions, self.nb_actions), dtype="int32"
              )
              diag_mask[np.diag_indices(self.nb_actions)] = range(
                  1, self.nb_actions + 1
              )
  
              # Add leading zero element to each element in the L_flat. We use this zero
              # element when gathering L_flat into a lower triangular matrix L.
              nb_rows = tf.shape(L_flat)[0]
              zeros = tf.expand_dims(
                  tf.tile(K.zeros((1,)), [nb_rows]), 1
              )
              try:
                  # Old TF behavior.
                  L_flat = tf.concat(1, [zeros, L_flat])
              except (TypeError, ValueError):
                  # New TF behavior
                  L_flat = tf.concat([zeros, L_flat], 1)
  
              # Finally, process each element of the batch.
              def fn(a, x):
                  x_ = tf.gather(x, diag_mask)
                  return x_
  
              P = tf.scan(
                  fn,
                  L_flat,
                  initializer=K.zeros(
                      (self.nb_actions, self.nb_actions)
                  ),
              )
          else:
              raise RuntimeError(
                  'Unknown Keras backend "{}".'.format(K.backend())
              )
      assert P is not None
      assert K.ndim(P) == 3
  
      # Combine a, mu and P into a scalar (over the batches). What we compute here is
      # -.5 * (a - mu)^T * P * (a - mu), where * denotes the dot-product. Unfortunately
      # TensorFlow handles vector * P slightly suboptimal, hence we convert the vectors to
      # 1xd/dx1 matrices and finally flatten the resulting 1x1 matrix into a scalar. All
      # operations happen over the batch size, which is dimension 0.
      prod = K.batch_dot(K.expand_dims(a - mu, 1), P)
      prod = K.batch_dot(prod, K.expand_dims(a - mu, -1))
      A = -0.5 * K.batch_flatten(prod)
      assert K.ndim(A) == 2
      return A
  
imports: from __future__ import division
  import warnings
  import keras.backend
  from keras.models import Model
  from keras.layers import Lambda, Input, Layer, Dense
  from rl.core import Agent
  from rl.policy import EpsGreedyQPolicy, GreedyQPolicy
  from rl.util import *
label_desc: Use image node_id 2 for calling the NAFLayer obj's underlying member method code with example usage: obj.call(x, mask) and returns: A, x_,  x_, list, x_, x_