code: def gen_conv_code_unroll_batch_kern(
      d, unroll_bsize=1, unroll_ksize=1
  ):
      """
      c_code for ConvOp that unroll the batch size loop.
  
      """
      assert unroll_bsize > 0 and unroll_ksize > 0
      if (
          "unroll_bsize" in d
          or "unroll_ksize" in d
          or "unroll_iter" in d
          or "unroll_biter" in d
          or "unroll_kiter" in d
      ):
          raise Exception(
              "We can't use this dictionary as we will overwrite some of its containt"
          )
      d = d.copy()
  
      d["unroll_bsize"] = unroll_bsize
      d["unroll_ksize"] = unroll_ksize
  
      def my_dup(st, size):
          s = ""
          for i in xrange(size):
              d["unroll_iter"] = i
              s += st % d
          return s + "\n"
  
      def my_dup2(st):
          s = ""
          iter = 0
          for i in xrange(unroll_bsize):
              d["unroll_biter"] = i
              for j in xrange(unroll_ksize):
                  d["unroll_kiter"] = j
                  d["unroll_iter"] = iter
                  iter += 1
                  s += st % d
          return s + "\n"
  
      ret = (
          """
  const int mode=%(mode)s;
  int typenum=0, typenum_f=0;
  PyArrayObject *ain1=NULL, *ain2=NULL, *filtersflipped_arr=NULL, *img2d_arr=NULL, *z_arr=NULL;;
  const %(type)s fill_value = 0;
  
  int type_im=PyArray_TYPE(%(img2d)s);
  int type_ker=PyArray_TYPE(%(filtersflipped)s);
  
  npy_intp dim_zz[2]={%(self_outshp0)s,%(self_outshp1)s};
  npy_intp dim_im[2]={%(self_imshp1)s,%(self_imshp2)s};
  const npy_intp dim_ker0=%(self_kshp0)s;
  const npy_intp dim_ker1=%(self_kshp1)s;
  
  PyArray_Dims img2d_shape;
  npy_intp img2d_dim[4]={1,1,0,0};
  img2d_shape.ptr=img2d_dim;
  img2d_shape.len=4;
  
  PyArray_Dims kerns_shape;
  npy_intp kerns_dim[4]={1,1,0,0};
  kerns_shape.ptr=kerns_dim;
  kerns_shape.len=4;
  PyObject *img2d=NULL, *contig, *filtersflipped=NULL;
  
  if(PyArray_NDIM(%(img2d)s)==2){
    img2d_dim[3]=PyArray_DIMS(%(img2d)s)[1];
    img2d_dim[2]=PyArray_DIMS(%(img2d)s)[0];
  }else if(PyArray_NDIM(%(img2d)s)==3){
    img2d_dim[3]=PyArray_DIMS(%(img2d)s)[2];
    img2d_dim[2]=PyArray_DIMS(%(img2d)s)[1];
    img2d_dim[0]=PyArray_DIMS(%(img2d)s)[0];
  }else if(PyArray_NDIM(%(img2d)s)==4){
    img2d_dim[3]=PyArray_DIMS(%(img2d)s)[3];
    img2d_dim[2]=PyArray_DIMS(%(img2d)s)[2];
    img2d_dim[1]=PyArray_DIMS(%(img2d)s)[1];
    img2d_dim[0]=PyArray_DIMS(%(img2d)s)[0];
  }else {
      std::stringstream temp;
      temp << "nddim="<<PyArray_NDIM(%(img2d)s);
      std::string param = temp.str();
      PyErr_SetString(PyExc_ValueError,
        ("img don't have a good shape. " + param).c_str());
      %(fail)s;
  }
  
  if(PyArray_NDIM(%(filtersflipped)s)==3){
    kerns_dim[3]=PyArray_DIMS(%(filtersflipped)s)[2];
    kerns_dim[2]=PyArray_DIMS(%(filtersflipped)s)[1];
    kerns_dim[0]=PyArray_DIMS(%(filtersflipped)s)[0];
  }else if(PyArray_NDIM(%(filtersflipped)s)==4){
    kerns_dim[3]=PyArray_DIMS(%(filtersflipped)s)[3];
    kerns_dim[2]=PyArray_DIMS(%(filtersflipped)s)[2];
    kerns_dim[1]=PyArray_DIMS(%(filtersflipped)s)[1];
    kerns_dim[0]=PyArray_DIMS(%(filtersflipped)s)[0];
  }else{
      PyErr_SetString(PyExc_ValueError, "kernel don't have a good shape");
      %(fail)s;
  }
  
  %(assert_size)s
  
  img2d = PyArray_Newshape(%(img2d)s,&img2d_shape, NPY_CORDER);
  img2d_arr = (PyArrayObject*)img2d;
  if ((PyArray_STRIDES(img2d_arr)[3] != (npy_intp)sizeof(%(type)s))
       || (PyArray_STRIDES(img2d_arr)[2] != PyArray_DIMS(img2d_arr)[3]*(npy_intp)sizeof(%(type)s))){
      contig = (PyObject*)(PyArray_GETCONTIGUOUS((PyArrayObject*)img2d));
      Py_DECREF(img2d);
      img2d = contig;
      img2d_arr = (PyArrayObject*)img2d;
      if (!PyArray_ISCONTIGUOUS(img2d_arr)){
          PyErr_SetString(PyExc_ValueError, "img2d isn't contiguous");
          %(fail)s;
      }
  }
  
  filtersflipped = PyArray_Newshape(%(filtersflipped)s,&kerns_shape, NPY_CORDER);
  filtersflipped_arr = (PyArrayObject*)filtersflipped;
  if ((PyArray_STRIDES(filtersflipped_arr)[3] != (npy_intp)sizeof(%(type)s))
       || (PyArray_STRIDES(filtersflipped_arr)[2] != PyArray_DIMS(filtersflipped_arr)[3]*(npy_intp)sizeof(%(type)s))){
      contig = (PyObject*)(PyArray_GETCONTIGUOUS((PyArrayObject*)filtersflipped));
      Py_DECREF(filtersflipped);
      filtersflipped = contig;
      filtersflipped_arr = (PyArrayObject*)filtersflipped;
      if (!PyArray_ISCONTIGUOUS(filtersflipped_arr)){
          PyErr_SetString(PyExc_ValueError, "filtersflipped isn't contiguous");
          %(fail)s;
      }
  }
  
  if(mode != VALID && mode != FULL){
    PyErr_SetString(PyExc_ValueError, "invalid mode, only full and valid are supported"); %(fail)s;
  }
  typenum = PyArray_ObjectType((PyObject*)%(img2d)s, 0);
  typenum_f = PyArray_ObjectType((PyObject*)%(filtersflipped)s, 0);
  if (typenum < 0) {PyErr_SetString(PyExc_ValueError, "Invalid type"); %(fail)s;}
  if (typenum != typenum_f) {PyErr_SetString(PyExc_ValueError, "Input types must match"); %(fail)s;}
  
  if (!img2d)
  {
      PyErr_SetString(PyExc_AssertionError, "!img2d");
      %(fail)s;
  }
  if (!filtersflipped)
  {
      PyErr_SetString(PyExc_AssertionError, "!filtersflipped");
      %(fail)s;
  }
  
  if ((!%(z)s)
    || *PyArray_DIMS(%(z)s)!=4
    ||(PyArray_DIMS(%(z)s)[0] != %(self_bsize)s)
    ||(PyArray_DIMS(%(z)s)[1] != %(self_nkern)s)
    ||(PyArray_DIMS(%(z)s)[2] != dim_zz[0])
    ||(PyArray_DIMS(%(z)s)[3] != dim_zz[1])
    ||!PyArray_ISCONTIGUOUS(%(z)s)
    )
  {
    {Py_XDECREF(%(z)s);}
    npy_intp dims[4] = {0,0,0,0};
    dims[0]=%(self_bsize)s;
    dims[1]=%(self_nkern)s;
    dims[2]=dim_zz[0];
    dims[3]=dim_zz[1];
    %(z)s = (PyArrayObject*) PyArray_ZEROS(4, dims, typenum,0);
  }else{
    //PyArray_FILLWBYTE((PyObject*)%(z)s,0);
  }
  z_arr = (PyArrayObject*) %(z)s;
  
  int Os[2];
  Os[0]=%(self_outshp0)s;
  Os[1]=%(self_outshp1)s;
  
  //assertions
  if (!PyArray_ISCONTIGUOUS(%(z)s))
  {
      PyErr_SetString(PyExc_AssertionError, "Output (%(z)s) not contiguous");
      %(fail)s;
  }
  
  for(int b=0;b< %(self_bsize)s ;b+=%(unroll_bsize)s){
    for(int n_kern=0;n_kern<%(self_nkern)s;n_kern+=%(unroll_ksize)s){
  
  """
          % d
      )
      ret += my_dup2(
          "%(type)s * __restrict__ out%(unroll_iter)s=(%(type)s *)(PyArray_GETPTR2(z_arr,b+%(unroll_biter)s,n_kern+%(unroll_kiter)s));"
      )
      ret += my_dup(
          "for (int i = 0; i < dim_zz[0]*dim_zz[1]; ++i) out%(unroll_iter)s[i] = 0;",
          unroll_bsize * unroll_ksize,
      )
      ret += (
          """
      for(int stack_size=0;stack_size<%(self_imshp0)s;stack_size++){
  """
          % d
      )
      ret += my_dup(
          "const %(type)s * __restrict__ in%(unroll_iter)d=(%(type)s *)(PyArray_GETPTR2(img2d_arr,b+%(unroll_iter)s,stack_size));",
          unroll_bsize,
      )
      ret += my_dup(
          "const %(type)s * __restrict__ hvals%(unroll_iter)s=(%(type)s *)(PyArray_GETPTR2(filtersflipped_arr,n_kern+%(unroll_iter)s,stack_size));",
          unroll_ksize,
      )
      ret += (
          """
  
        int new_m;
  
        for (int iter_m=0; iter_m < Os[0]; iter_m++) {
          // Reposition index into input image based on requested output size
          int pos_m = iter_m*%(self_dx)s;//The position of the patch in the image
          if (mode == FULL) new_m = pos_m ;
          else new_m = (pos_m+dim_ker0-1);
  
          for (int iter_n=0; iter_n < Os[1]; iter_n++) {  // loop over columns
            int pos_n=iter_n*%(self_dy)s;
          """
          % d
      )
      ret += my_dup(
          "%(type)s sum%(unroll_iter)s=0;", unroll_bsize * unroll_ksize
      )
      ret += (
          """
  
            // Sum over kernel, if index into image is out of bounds
            // fill with the value
            for (int j=0; j < dim_ker0; j++) {
              int ind0 = (new_m-j);
  
              if(mode==FULL){
  """
          % d
      )
      ret += my_dup(
          "const %(type)s * idx_hvals%(unroll_iter)s=&hvals%(unroll_iter)s[j*dim_ker1];",
          unroll_ksize,
      )
      ret += (
          """
                if(ind0 < 0 || ind0 >= dim_im[0]){
                  if(fill_value!=0)
                    for (int k=0; k < dim_ker1; k++) {
  """
          % d
      )
      ret += my_dup2(
          "sum%(unroll_iter)s += idx_hvals%(unroll_kiter)s[k] * fill_value;"
      )
      ret += (
          """
                    }
                }else{
                  //do the part where kernel is to the right of the img
  
                  int k=0,max_k=max((int)(pos_n-dim_im[1])+1,0);
                  if(fill_value!=0){
  
                    for(k=0;k<max_k;k++){
  """
          % d
      )
      ret += my_dup2(
          "sum%(unroll_iter)s += idx_hvals%(unroll_kiter)s[k] * fill_value;"
      )
      ret += (
          """
                    }
                  }else {k=max_k;}
  
                  //do the part where the kernel is on the img
                  max_k=min(pos_n+1,(int)dim_ker1);
  """
          % d
      )
      ret += my_dup(
          "const %(type)s * idx_in%(unroll_iter)s=&in%(unroll_iter)s[ind0*dim_im[1]];",
          unroll_bsize,
      )
      ret += (
          """
                  for (int ind1=pos_n-k; k<max_k; k++,ind1--) {
  
  """
          % d
      )
      ret += my_dup2(
          "sum%(unroll_iter)s+= idx_hvals%(unroll_kiter)s[k] * idx_in%(unroll_biter)s[ind1];"
      )
      ret += (
          """
                  }
                  //do the part to the left of the img
                  if(fill_value!=0)
                    for(;k<dim_ker1;k++){
  """
          % d
      )
      ret += my_dup2(
          "sum%(unroll_iter)s += idx_hvals%(unroll_kiter)s[k] * fill_value;"
      )
      ret += (
          """
                    }
                }
              }else{//valid mode
  """
          % d
      )
      ret += my_dup(
          "const %(type)s* idx_in%(unroll_iter)s=&in%(unroll_iter)s[ind0*dim_im[1]];",
          unroll_bsize,
      )
      ret += my_dup(
          "const %(type)s* idx_hvals%(unroll_iter)s=&hvals%(unroll_iter)s[j*dim_ker1];",
          unroll_ksize,
      )
      ret += (
          """
                int new_n = (pos_n+dim_ker1-1);
  
                for (int k=0,last=new_n; k < dim_ker1; k++,last--) {
  """
          % d
      )
      ret += my_dup2(
          "sum%(unroll_iter)s+=idx_hvals%(unroll_kiter)s[k]*idx_in%(unroll_biter)s[last];"
      )
      ret += (
          """
                }
              }
  
            }//for j
  """
          % d
      )
      ret += my_dup(
          "out%(unroll_iter)s[iter_m*dim_zz[1]+iter_n] %(affectation)s sum%(unroll_iter)s;",
          unroll_bsize * unroll_ksize,
      )
      ret += """
          }//for n
        }//for m
      }//for stack_size
    }//for n_kern
  }//for b
  Py_XDECREF(img2d);
  Py_XDECREF(filtersflipped);
  """
      return ret
  
imports: from __future__ import absolute_import, print_function, division
  import logging
  import numpy
  from six.moves import xrange
  import warnings
  import theano
  from theano import OpenMPOp
  from theano.tensor import as_tensor_variable, blas, get_scalar_constant_value, patternbroadcast, NotScalarConstantError
  from theano.gof import Apply
  from theano.tensor.nnet.abstract_conv import get_conv_output_shape, get_conv_shape_1axis
label_desc: Use image node_id 2 for calling a global function with example usage: gen_conv_code_unroll_batch_kern(d, unroll_bsize, unroll_ksize) and returns: ret, unknown, unknown