code: def conv3d(
      signals,
      filters,
      signals_shape=None,
      filters_shape=None,
      border_mode="valid",
  ):
      """
      Convolve spatio-temporal filters with a movie.
  
      It flips the filters.
  
      Parameters
      ----------
      signals
          Timeseries of images whose pixels have color channels.
          Shape: [Ns, Ts, C, Hs, Ws].
      filters
          Spatio-temporal filters.
          Shape: [Nf, Tf, C, Hf, Wf].
      signals_shape
          None or a tuple/list with the shape of signals.
      filters_shape
          None or a tuple/list with the shape of filters.
      border_mode
          One of 'valid', 'full' or 'half'.
  
      Notes
      -----
      Another way to define signals: (batch,  time, in channel, row, column)
      Another way to define filters: (out channel,time,in channel, row, column)
  
      For the GPU, use nnet.conv3d.
  
      See Also
      --------
      Someone made a script that shows how to swap the axes between
      both 3d convolution implementations in Theano. See the last
      `attachment <https://groups.google.com/d/msg/theano-users/1S9_bZgHxVw/0cQR9a4riFUJ>`_
  
      """
  
      if isinstance(border_mode, str):
          border_mode = (border_mode, border_mode, border_mode)
  
      if signals_shape is None:
          _signals_shape_5d = signals.shape
      else:
          _signals_shape_5d = signals_shape
  
      if filters_shape is None:
          _filters_shape_5d = filters.shape
      else:
          _filters_shape_5d = filters_shape
  
      Ns, Ts, C, Hs, Ws = _signals_shape_5d
      Nf, Tf, C, Hf, Wf = _filters_shape_5d
  
      _signals_shape_4d = (Ns * Ts, C, Hs, Ws)
      _filters_shape_4d = (Nf * Tf, C, Hf, Wf)
  
      if border_mode[1] != border_mode[2]:
          raise NotImplementedError(
              "height and width bordermodes must match"
          )
      conv2d_signal_shape = _signals_shape_4d
      conv2d_filter_shape = _filters_shape_4d
      if signals_shape is None:
          conv2d_signal_shape = None
      if filters_shape is None:
          conv2d_filter_shape = None
  
      out_4d = tensor.nnet.conv2d(
          signals.reshape(_signals_shape_4d),
          filters.reshape(_filters_shape_4d),
          input_shape=conv2d_signal_shape,
          filter_shape=conv2d_filter_shape,
          border_mode=border_mode[1],
      )  # ignoring border_mode[2]
  
      # compute the intended output size
      if border_mode[1] == "valid":
          Hout = Hs - Hf + 1
          Wout = Ws - Wf + 1
      elif border_mode[1] == "full":
          Hout = Hs + Hf - 1
          Wout = Ws + Wf - 1
      elif border_mode[1] == "half":
          Hout = Hs - (Hf % 2) + 1
          Wout = Ws - (Wf % 2) + 1
      elif border_mode[1] == "same":
          raise NotImplementedError()
      else:
          raise ValueError("invalid border mode", border_mode[1])
  
      # reshape the temporary output to restore its original size
      out_tmp = out_4d.reshape((Ns, Ts, Nf, Tf, Hout, Wout))
  
      # now sum out along the Tf to get the output
      # but we have to sum on a diagonal through the Tf and Ts submatrix.
      if Tf == 1:
          # for Tf==1, no sum along Tf, the Ts-axis of the output is unchanged!
          out_5d = out_tmp.reshape((Ns, Ts, Nf, Hout, Wout))
      else:
          # for some types of convolution, pad out_tmp with zeros
          if border_mode[0] == "valid":
              Tpad = 0
          elif border_mode[0] == "full":
              Tpad = Tf - 1
          elif border_mode[0] == "half":
              Tpad = Tf // 2
          elif border_mode[0] == "same":
              raise NotImplementedError()
          else:
              raise ValueError("invalid border mode", border_mode[0])
  
          if Tpad == 0:
              out_5d = diagonal_subtensor(out_tmp, 1, 3).sum(axis=3)
          else:
              # pad out_tmp with zeros before summing over the diagonal
              out_tmp_padded = tensor.zeros(
                  dtype=out_tmp.dtype,
                  shape=(Ns, Ts + 2 * Tpad, Nf, Tf, Hout, Wout),
              )
              out_tmp_padded = tensor.set_subtensor(
                  out_tmp_padded[:, Tpad : (Ts + Tpad), :, :, :, :],
                  out_tmp,
              )
              out_5d = diagonal_subtensor(out_tmp_padded, 1, 3).sum(
                  axis=3
              )
  
      return out_5d
  
imports: from __future__ import absolute_import, print_function, division
  import theano
  from theano.gradient import DisconnectedType
  from theano.gof import Op, Apply, TopoOptimizer
  from theano.gof.opt import copy_stack_trace
  from theano import tensor
label_desc: Use image node_id 2 for calling a global function with example usage: conv3d(signals, filters, signals_shape, filters_shape, border_mode) and returns: out_5d