class_docstr:   An edge-conditioned convolutional layer (ECC) from the paper
  
    > [Dynamic Edge-Conditioned Filters in Convolutional Neural Networks on
  Graphs](https://arxiv.org/abs/1704.02901)<br>
    > Martin Simonovsky and Nikos Komodakis
  
  **Mode**: single, disjoint, batch, mixed.
  
  **In single, disjoint, and mixed mode, this layer expects a sparse adjacency
  matrix. If a dense adjacency is given as input, it will be automatically
  cast to sparse, which might be expensive.**
  
    This layer computes:
    $$
        \x_i' = \x_{i} \W_{\textrm{root}} + \sum\limits_{j \in \mathcal{N}(i)}
        \x_{j} \textrm{MLP}(\e_{j \rightarrow i}) + \b
    $$
    where \(\textrm{MLP}\) is a multi-layer perceptron that outputs an
    edge-specific weight as a function of edge attributes.
  
  **Input**
  
  - Node features of shape `([batch], n_nodes, n_node_features)`;
  - Binary adjacency matrices of shape `([batch], n_nodes, n_nodes)`;
  - Edge features. In single mode, shape `(num_edges, n_edge_features)`; in
  batch mode, shape `(batch, n_nodes, n_nodes, n_edge_features)`.
  
    **Output**
  
    - node features with the same shape of the input, but the last dimension
    changed to `channels`.
  
    **Arguments**
  
    - `channels`: integer, number of output channels;
    - `kernel_network`: a list of integers representing the hidden neurons of
    the kernel-generating network;
    - 'root': if False, the layer will not consider the root node for computing
    the message passing (first term in equation above), but only the neighbours.
    - `activation`: activation function;
    - `use_bias`: bool, add a bias vector to the output;
    - `kernel_initializer`: initializer for the weights;
    - `bias_initializer`: initializer for the bias vector;
    - `kernel_regularizer`: regularization applied to the weights;
    - `bias_regularizer`: regularization applied to the bias vector;
    - `activity_regularizer`: regularization applied to the output;
    - `kernel_constraint`: constraint applied to the weights;
    - `bias_constraint`: constraint applied to the bias vector.
class_docstr_tok: ["An","edge-conditioned","convolutional","layer","(","ECC",")","from","the","paper",">","[","Dynamic","Edge-Conditioned","Filters","in","Convolutional","Neural","Networks","on","Graphs","]","(","https",":","\/\/arxiv.org\/abs\/1704.02901",")","<","br",">",">","Martin","Simonovsky","and","Nikos","Komodakis","*","*","Mode","*","*",":","single",",","disjoint",",","batch",",","mixed",".","*","*","In","single",",","disjoint",",","and","mixed","mode",",","this","layer","expects","a","sparse","adjacency","matrix",".","If","a","dense","adjacency","is","given","as","input",",","it","will","be","automatically","cast","to","sparse",",","which","might","be","expensive",".","*","*","This","layer","computes",":","$","$","\\x_i","'","=","\\x_","{","i","}","\\W_","{","\\textrm","{","root","}","}","+","\\sum\\limits_","{","j","\\in","\\mathcal","{","N","}","(","i",")","}","\\x_","{","j","}","\\textrm","{","MLP","}","(","\\e_","{","j","\\rightarrow","i","}",")","+","\\b","$","$","where","\\","(","\\textrm","{","MLP","}","\\",")","is","a","multi-layer","perceptron","that","outputs","an","edge-specific","weight","as","a","function","of","edge","attributes",".","*","*","Input","*","*","-","Node","features","of","shape","`","(","[","batch","]",",","n_nodes",",","n_node_features",")","`",";","-","Binary","adjacency","matrices","of","shape","`","(","[","batch","]",",","n_nodes",",","n_nodes",")","`",";","-","Edge","features",".","In","single","mode",",","shape","`","(","num_edges",",","n_edge_features",")","`",";","in","batch","mode",",","shape","`","(","batch",",","n_nodes",",","n_nodes",",","n_edge_features",")","`",".","*","*","Output","*","*","-","node","features","with","the","same","shape","of","the","input",",","but","the","last","dimension","changed","to","`","channels","`",".","*","*","Arguments","*","*","-","`","channels","`",":","integer",",","number","of","output","channels",";","-","`","kernel_network","`",":","a","list","of","integers","representing","the","hidden","neurons","of","the","kernel-generating","network",";","-","'root","'",":","if","False",",","the","layer","will","not","consider","the","root","node","for","computing","the","message","passing","(","first","term","in","equation","above",")",",","but","only","the","neighbours",".","-","`","activation","`",":","activation","function",";","-","`","use_bias","`",":","bool",",","add","a","bias","vector","to","the","output",";","-","`","kernel_initializer","`",":","initializer","for","the","weights",";","-","`","bias_initializer","`",":","initializer","for","the","bias","vector",";","-","`","kernel_regularizer","`",":","regularization","applied","to","the","weights",";","-","`","bias_regularizer","`",":","regularization","applied","to","the","bias","vector",";","-","`","activity_regularizer","`",":","regularization","applied","to","the","output",";","-","`","kernel_constraint","`",":","constraint","applied","to","the","weights",";","-","`","bias_constraint","`",":","constraint","applied","to","the","bias","vector","."]
code: def call(self, inputs, mask=None):
      x, a, e = inputs
  
      # Parameters
      N = tf.shape(x)[-2]
      F = tf.shape(x)[-1]
      F_ = self.channels
  
      # Filter network
      kernel_network = e
      for layer in self.kernel_network_layers:
          kernel_network = layer(kernel_network)
  
      # Convolution
      mode = ops.autodetect_mode(x, a)
      if mode == modes.BATCH:
          kernel = K.reshape(kernel_network, (-1, N, N, F_, F))
          output = kernel * a[..., None, None]
          output = tf.einsum("abcde,ace->abd", output, x)
      else:
          # Enforce sparse representation
          if not K.is_sparse(a):
              warnings.warn(
                  "Casting dense adjacency matrix to SparseTensor."
                  "This can be an expensive operation. "
              )
              a = tf.sparse.from_dense(a)
  
          target_shape = (-1, F, F_)
          if mode == modes.MIXED:
              target_shape = (tf.shape(x)[0],) + target_shape
          kernel = tf.reshape(kernel_network, target_shape)
          index_targets = a.indices[:, 1]
          index_sources = a.indices[:, 0]
          messages = tf.gather(x, index_sources, axis=-2)
          messages = tf.einsum("...ab,...abc->...ac", messages, kernel)
          output = ops.scatter_sum(messages, index_targets, N)
  
      if self.root:
          output += K.dot(x, self.root_kernel)
      if self.use_bias:
          output = K.bias_add(output, self.bias)
      if mask is not None:
          output *= mask[0]
      output = self.activation(output)
  
      return output
  
file_path: spektral/spektral/layers/convolutional/ecc_conv.py
imports: import warnings
  import tensorflow
  from tensorflow.keras import backend
  from tensorflow.keras.layers import Dense
  from spektral.layers import ops
  from spektral.layers.convolutional.conv import Conv
  from spektral.layers.ops import modes
label_desc: Use image node_id 3 for calling the ECCConv obj's underlying member method code with example usage: obj.call(inputs, mask) and returns: output