docstr: :param image_shape: The constant shape info passed to conv2d.
  :param filter_shape: The constant shape info passed to conv2d.
  
  :param N_image_shape: None(default to image_shape) or tuple of
                        4 elements with the shape of the input image
  
  :param N_filter_shape: None(default to filter_shape) or tuple
                         of 4 elements with the shape of the
                         input filter
code: def validate(
      self,
      image_shape,
      filter_shape,
      border_mode="valid",
      subsample=(1, 1),
      N_image_shape=None,
      N_filter_shape=None,
      input=None,
      filters=None,
      unroll_batch=None,
      unroll_kern=None,
      unroll_patch=None,
      verify_grad=True,
      should_raise=False,
  ):
      """
      :param image_shape: The constant shape info passed to conv2d.
      :param filter_shape: The constant shape info passed to conv2d.
  
      :param N_image_shape: None(default to image_shape) or tuple of
                            4 elements with the shape of the input image
  
      :param N_filter_shape: None(default to filter_shape) or tuple
                             of 4 elements with the shape of the
                             input filter
  
      """
      if N_image_shape is None:
          N_image_shape = [
              T.get_scalar_constant_value(T.as_tensor_variable(x))
              for x in image_shape
          ]
      if N_filter_shape is None:
          N_filter_shape = [
              T.get_scalar_constant_value(T.as_tensor_variable(x))
              for x in filter_shape
          ]
  
      if input is None:
          input = self.input
      if not filters:
          filters = self.filters
  
      # THEANO IMPLEMENTATION
  
      # we create a symbolic function so that verify_grad can work
      def sym_conv2d(input, filters):
          # define theano graph and function
          input.name = "input"
          filters.name = "filters"
          rval = conv.conv2d(
              input,
              filters,
              image_shape,
              filter_shape,
              border_mode,
              subsample,
              unroll_batch=unroll_batch,
              unroll_kern=unroll_kern,
              unroll_patch=unroll_patch,
          )
          rval.name = "conv_output"
          return rval
  
      output = sym_conv2d(input, filters)
      output.name = "conv2d(%s,%s)" % (input.name, filters.name)
      theano_conv = theano.function(
          [input, filters], output, mode=self.mode
      )
  
      # initialize input and compute result
      image_data = np.random.random(N_image_shape).astype(self.dtype)
      filter_data = np.random.random(N_filter_shape).astype(self.dtype)
      try:
          theano_output = theano_conv(image_data, filter_data)
      except ValueError:
          if not should_raise:
              raise
          return
      else:
          if should_raise:
              raise Exception("ConvOp should have generated an error")
  
      # REFERENCE IMPLEMENTATION
      s = 1.0
      orig_image_data = image_data
      if border_mode != "full":
          s = -1.0
      out_shape2d = (
          np.array(N_image_shape[-2:])
          + s * np.array(N_filter_shape[-2:])
          - s
      )
      out_shape2d = np.ceil(out_shape2d / np.array(subsample))
      # avoid numpy deprecation
      out_shape2d = out_shape2d.astype("int32")
      out_shape = (N_image_shape[0], N_filter_shape[0]) + tuple(
          out_shape2d
      )
      ref_output = np.zeros(out_shape)
  
      # loop over output feature maps
      ref_output.fill(0)
      if border_mode == "full":
          image_data2 = np.zeros(
              (
                  N_image_shape[0],
                  N_image_shape[1],
                  N_image_shape[2] + 2 * N_filter_shape[2] - 2,
                  N_image_shape[3] + 2 * N_filter_shape[3] - 2,
              )
          )
          image_data2[
              :,
              :,
              N_filter_shape[2]
              - 1 : N_filter_shape[2]
              - 1
              + N_image_shape[2],
              N_filter_shape[3]
              - 1 : N_filter_shape[3]
              - 1
              + N_image_shape[3],
          ] = image_data
          image_data = image_data2
          N_image_shape = image_data.shape
      for bb in range(N_image_shape[0]):
          for nn in range(N_filter_shape[0]):
              for im0 in range(N_image_shape[1]):
                  filter2d = filter_data[nn, im0, :, :]
                  image2d = image_data[bb, im0, :, :]
                  for row in range(ref_output.shape[2]):
                      irow = row * subsample[0]  # image row
                      for col in range(ref_output.shape[3]):
                          icol = col * subsample[1]  # image col
                          ref_output[bb, nn, row, col] += (
                              image2d[
                                  irow : irow + N_filter_shape[2],
                                  icol : icol + N_filter_shape[3],
                              ]
                              * filter2d[::-1, ::-1]
                          ).sum()
  
      self.assertTrue(_allclose(theano_output, ref_output))
  
      # TEST GRADIENT
      if verify_grad:
          utt.verify_grad(sym_conv2d, [orig_image_data, filter_data])
  
imports: from __future__ import absolute_import, print_function, division
  import time
  from nose.plugins.skip import SkipTest
  import numpy
  import theano
  import theano.tensor
  from theano.tests import unittest_tools
  from theano.tensor.nnet import conv
  from theano.tensor.basic import _allclose, NotScalarConstantError
  from theano.tests.unittest_tools import attr
label_desc: Use image node_id 2 for calling the TestConv2D obj's underlying member method code with example usage: obj.validate(image_shape, filter_shape, border_mode, subsample, N_image_shape, N_filter_shape, input, filters, unroll_batch, unroll_kern, unroll_patch, verify_grad, should_raise) and returns: rval