code: def concat(inputs, prev_modules, attrs, outputs):
      # extract attributes
      axis = int(attrs.get("axis"))
      # extract inputs
      _, data_tensor_shape = inputs[0]
      # calc output tensor shape
      dim_rank = 0
      for i in range(len(inputs)):
          _, curr_input_shape = inputs[i]
          for j in range(len(data_tensor_shape)):
              if axis != j:
                  if curr_input_shape[i] != data_tensor_shape[i]:
                      invalidInputError(
                          False,
                          "Input shape mismatch. Expect receive input shape "
                          + data_tensor_shape[i]
                          + " but got "
                          + curr_input_shape[i],
                      )
              else:
                  dim_rank += curr_input_shape[axis]
      out_tensor_shape = list(data_tensor_shape)
      out_tensor_shape[axis] = dim_rank
      out_tensor_shape = tuple(out_tensor_shape)
      # create module node
      module = JoinTable(
          dimension=axis + 1, n_input_dims=len(data_tensor_shape)
      )(prev_modules)
      return module, [out_tensor_shape]
  
file_path: BigDL/python/dllib/src/bigdl/dllib/contrib/onnx/ops_converter.py
imports: from bigdl.dllib.nn.layer import SpatialAveragePooling, SpatialBatchNormalization
  from bigdl.dllib.nn.layer import SpatialConvolution, SpatialMaxPooling, JoinTable
  from bigdl.dllib.nn.layer import ReLU, SoftMax, CAddTable, Unsqueeze
  from bigdl.dllib.nn.onnx.layer import Constant, Gather, Gemm, Shape, Reshape
  from .converter_utils import *
label_desc: Use image node_id 3 for calling a global function with example usage: concat(inputs, prev_modules, attrs, outputs) and returns: module,  list