code: def convolve(
      kerns,
      kshp,
      nkern,
      images,
      imgshp,
      step=(1, 1),
      bias=None,
      mode="valid",
      flatten=True,
  ):
      """Convolution implementation by sparse matrix multiplication.
  
      :note: For best speed, put the matrix which you expect to be
             smaller as the 'kernel' argument
  
      "images" is assumed to be a matrix of shape batch_size x img_size,
      where the second dimension represents each image in raster order
  
      If flatten is "False", the output feature map will have shape:
  
      .. code-block:: python
  
          batch_size x number of kernels x output_size
  
      If flatten is "True", the output feature map will have shape:
  
      .. code-block:: python
  
          batch_size x number of kernels * output_size
  
      .. note::
  
          IMPORTANT: note that this means that each feature map (image
          generate by each kernel) is contiguous in memory. The memory
          layout will therefore be: [ <feature_map_0> <feature_map_1>
          ... <feature_map_n>], where <feature_map> represents a
          "feature map" in raster order
  
      kerns is a 2D tensor of shape nkern x N.prod(kshp)
  
      :param kerns: 2D tensor containing kernels which are applied at every pixel
      :param kshp: tuple containing actual dimensions of kernel (not symbolic)
      :param nkern: number of kernels/filters to apply.
                    nkern=1 will apply one common filter to all input pixels
      :param images: tensor containing images on which to apply convolution
      :param imgshp: tuple containing image dimensions
      :param step: determines number of pixels between adjacent receptive fields
                   (tuple containing dx,dy values)
      :param mode: 'full', 'valid' see CSM.evaluate function for details
      :param sumdims: dimensions over which to sum for the tensordot operation.
                      By default ((2,),(1,)) assumes kerns is a nkern x kernsize
                      matrix and images is a batchsize x imgsize matrix
                      containing flattened images in raster order
      :param flatten: flatten the last 2 dimensions of the output. By default,
                      instead of generating a batchsize x outsize x nkern tensor,
                      will flatten to batchsize x outsize*nkern
  
      :return: out1, symbolic result
      :return: out2, logical shape of the output img (nkern,heigt,width)
  
      :TODO: test for 1D and think of how to do n-d convolutions
      """
      # start by computing output dimensions, size, etc
      kern_size = np.int64(np.prod(kshp))
  
      # inshp contains either 2 entries (height,width) or 3 (nfeatures,h,w)
      # in the first case, default nfeatures to 1
      if np.size(imgshp) == 2:
          imgshp = (1,) + imgshp
  
      # construct indices and index pointers for sparse matrix, which,
      # when multiplied with input images will generate a stack of image
      # patches
      (
          indices,
          indptr,
          spmat_shape,
          sptype,
          outshp,
      ) = convolution_indices.conv_eval(imgshp, kshp, step, mode)
  
      # build sparse matrix, then generate stack of image patches
      csc = theano.sparse.CSM(sptype)(
          np.ones(indices.size), indices, indptr, spmat_shape
      )
      patches = (sparse.structured_dot(csc, images.T)).T
  
      # compute output of linear classifier
      pshape = tensor.stack(
          [
              images.shape[0] * tensor.as_tensor(np.prod(outshp)),
              tensor.as_tensor(imgshp[0] * kern_size),
          ]
      )
      patch_stack = tensor.reshape(patches, pshape, ndim=2)
  
      # kern is of shape: nkern x ksize*number_of_input_features
      # output is thus of shape: bsize*outshp x nkern
      output = tensor.dot(patch_stack, kerns.T)
  
      # add bias across each feature map (more efficient to do it now)
      if bias is not None:
          output += bias
  
      # now to have feature maps in raster order ...
      # go from bsize*outshp x nkern to bsize x nkern*outshp
      newshp = tensor.stack(
          [
              images.shape[0],
              tensor.as_tensor(np.prod(outshp)),
              tensor.as_tensor(nkern),
          ]
      )
      tensout = tensor.reshape(output, newshp, ndim=3)
      output = tensor.DimShuffle((False,) * tensout.ndim, (0, 2, 1))(
          tensout
      )
      if flatten:
          output = tensor.flatten(output, 2)
  
      return output, np.hstack((nkern, outshp))
  
file_path: Theano/theano/sparse/sandbox/sp.py
imports: from __future__ import absolute_import, print_function, division
  import numpy
  import scipy
  from scipy import sparse
  from six.moves import xrange
  import theano
  import theano.sparse
  from theano import sparse, gof, Op, tensor
  from theano.sparse.basic import Remove0, remove0
  from theano.sparse import SpSum, sp_sum, ColScaleCSC, RowScaleCSC, col_scale, row_scale, Diag, diag, SquareDiagonal, square_diagonal, EnsureSortedIndices, ensure_sorted_indices, clean
label_desc: Use image node_id 2 for calling a global function with example usage: convolve(kerns, kshp, nkern, images, imgshp, step, bias, mode, flatten) and returns: output,  np