class_docstr: Parameters
  ----------
  axis
      Can not be None. If you want the array flattened, do it before.
class_docstr_tok: ["Parameters","--","--","--","--","--","axis","Can","not","be","None",".","If","you","want","the","array","flattened",",","do","it","before","."]
code: def c_support_code_struct(self, node, nodename):
      code = (
          """
  
          int cumOp_%(nodename)s(PyGpuArrayObject* input, PyGpuArrayObject* output, int axis, size_t maxThreads, size_t maxGridY, size_t maxGridZ) {
              size_t shape[3] = { 1, 1, 1 };
              ssize_t inputStrides_x;
              ssize_t inputStrides_y;
              ssize_t inputStrides_z;
              ssize_t outputStrides_x;
              ssize_t outputStrides_y;
              ssize_t outputStrides_z;
              switch (PyGpuArray_NDIM(input))
              {
              case 1:
                  shape[0] = PyGpuArray_DIMS(input)[0];
                  inputStrides_x = PyGpuArray_STRIDES(input)[0] / sizeof(float);
                  outputStrides_x = PyGpuArray_STRIDES(output)[0] / sizeof(float);
                  break;
              case 2:
                  shape[0] = PyGpuArray_DIMS(input)[0];
                  shape[1] = PyGpuArray_DIMS(input)[1];
                  inputStrides_x = PyGpuArray_STRIDES(input)[0] / sizeof(float);
                  inputStrides_y = PyGpuArray_STRIDES(input)[1] / sizeof(float);
                  outputStrides_x = PyGpuArray_STRIDES(output)[0] / sizeof(float);
                  outputStrides_y = PyGpuArray_STRIDES(output)[1] / sizeof(float);
                  break;
              case 3:
                  shape[0] = PyGpuArray_DIMS(input)[0];
                  shape[1] = PyGpuArray_DIMS(input)[1];
                  shape[2] = PyGpuArray_DIMS(input)[2];
                  inputStrides_x = PyGpuArray_STRIDES(input)[0] / sizeof(float);
                  inputStrides_y = PyGpuArray_STRIDES(input)[1] / sizeof(float);
                  inputStrides_z = PyGpuArray_STRIDES(input)[2] / sizeof(float);
                  outputStrides_x = PyGpuArray_STRIDES(output)[0] / sizeof(float);
                  outputStrides_y = PyGpuArray_STRIDES(output)[1] / sizeof(float);
                  outputStrides_z = PyGpuArray_STRIDES(output)[2] / sizeof(float);
                  break;
              default:
                  PyErr_SetString(PyExc_RuntimeError, "Unsupported Axis");
                  return -1;
              }
              if (shape[axis] <= 1) {
                  int err = pygpu_move(output, input);
                  return err;
              }
              // Perform cum op on array of even size.
              size_t nbElementsPerCumOp = shape[axis] - (shape[axis] %% 2);
              // Determine how many elements can be processed in one block.
              size_t dimBlockX = ((nbElementsPerCumOp > 2*maxThreads ? 2*maxThreads : nbElementsPerCumOp)+1)/2;
              // Determine how many blocks are needed in total.
              size_t dimGridX = (nbElementsPerCumOp+2*dimBlockX-1) / (2*dimBlockX);  // Nb. of blocks needed per cum op.
              size_t dimGridY;  // Nb. of independent cum ops (width).
              size_t dimGridZ;  // Nb. of independent cum ops (height).
              ssize_t tmp;
              switch (axis)
              {
              case 0:
                  dimGridY = shape[1];
                  dimGridZ = shape[2];
                  break;
              case 1:
                  dimGridY = shape[0];
                  dimGridZ = shape[2];
                  tmp = inputStrides_x;
                  inputStrides_x = inputStrides_y;
                  inputStrides_y = tmp;
                  tmp = outputStrides_x;
                  outputStrides_x = outputStrides_y;
                  outputStrides_y = tmp;
                  break;
              case 2:
                  dimGridY = shape[1];
                  dimGridZ = shape[0];
  
                  tmp = inputStrides_x;
                  inputStrides_x = inputStrides_z;
                  inputStrides_z = tmp;
  
                  tmp = outputStrides_x;
                  outputStrides_x = outputStrides_z;
                  outputStrides_z = tmp;
  
                  break;
              default:
                  PyErr_SetString(PyExc_RuntimeError, "Unsupported Axis");
                  return -1;
              }
  
              const size_t shapeBlockSum[2] = { dimGridX, dimGridY*dimGridZ };
              PyGpuArrayObject* deviceBlockSum = pygpu_empty(2, shapeBlockSum, output->ga.typecode,
                                                             GA_C_ORDER, input->context, Py_None);
              if (deviceBlockSum == NULL){
                  return -1;
              }
              // Perform `maxGridY`*`maxGridZ` cum ops in parallel.
              for (size_t offsetY = 0; offsetY < dimGridY; offsetY += maxGridY){
                  size_t localDimGridY = (dimGridY - offsetY < maxGridY) ? (dimGridY - offsetY) : (maxGridY);
  
                  for (size_t offsetZ = 0; offsetZ < dimGridZ; offsetZ += maxGridZ){
                      size_t localDimGridZ = (dimGridZ - offsetZ < maxGridZ) ? (dimGridZ - offsetZ) : (maxGridZ);
                      size_t dimGrid[3] = {dimGridX, localDimGridY, localDimGridZ};
                      size_t dimBlock[3] = {dimBlockX, 1, 1};  // One cum op per block.
                      size_t sharedBytes = (2*dimBlockX) * sizeof(float);
  
                      int err = k_blockCumOp_call(3, dimGrid, dimBlock, sharedBytes, input->ga.data, input->ga.offset, output->ga.data, output->ga.offset, nbElementsPerCumOp, inputStrides_x, inputStrides_y, inputStrides_z, outputStrides_x, outputStrides_y, outputStrides_z, offsetY, offsetZ, deviceBlockSum->ga.data, deviceBlockSum->ga.offset);
                      if (err != GA_NO_ERROR){
                          PyErr_SetString(PyExc_RuntimeError, "blockCumOp call failed");
                          return -1;
                      }
  
                      if (dimGridX > 1) {
                          // Do a cum op over the blockSum (recursive).
                          if (cumOp_%(nodename)s(deviceBlockSum, deviceBlockSum, 0, maxThreads, maxGridY, maxGridZ) == -1){
                              Py_DECREF(deviceBlockSum);
                              return -1;
                          }
                          // Since there are more than one block (i.e. `dimGridX > 1`)
                          //  report partial cum ops of previous blocks to subsequents ones.
                          size_t dimGrid[3] = {dimGridX, localDimGridY, localDimGridZ};
                          size_t dimBlock[3] = {dimBlockX, 1, 1};
  
                          int err = k_finalCumOp_call(3, dimGrid, dimBlock, sharedBytes, output->ga.data, output->ga.offset, deviceBlockSum->ga.data, deviceBlockSum->ga.offset, nbElementsPerCumOp, outputStrides_x, outputStrides_y, outputStrides_z, offsetY, offsetZ);
                          if (err != GA_NO_ERROR){
                              PyErr_SetString(PyExc_RuntimeError, "finalCumOp call failed");
                              return -1;
                          }
                      }
                      // If shape[axis] is odd, the last element is compute manually
                      if (shape[axis] != nbElementsPerCumOp){
                          size_t dimGrid[3] = {1, localDimGridY, localDimGridZ};
                          size_t dimBlock[3] = {1, 1, 1};
  
                          int err = k_cumadd_call(3, dimGrid, dimBlock, sharedBytes, input->ga.data, input->ga.offset, output->ga.data, output->ga.offset, inputStrides_x, inputStrides_y, inputStrides_z, outputStrides_x, outputStrides_y, outputStrides_z, offsetY, offsetZ, shape[axis] - 2, shape[axis] - 1);
                          if (err != GA_NO_ERROR){
                              PyErr_SetString(PyExc_RuntimeError, "cumadd call failed");
                              return -1;
                          }
  
                      }
                  }
              }
              Py_XDECREF(deviceBlockSum);
              return 0;
          }
          """
          % locals()
      )
      return (
          super(GpuCumOp, self).c_support_code_struct(node, nodename)
          + code
      )
  
imports: from __future__ import absolute_import, print_function, division
  from theano import Apply, Op
  from theano.tensor.extra_ops import CumOp
  from .basic_ops import as_gpuarray_variable, GpuKernelBase, Kernel, GpuReshape, infer_context_name, gpuarray_helper_inc_dir
  from .opt import register_opt, op_lifter, register_opt2
  from .type import gpu_context_type
  from theano.gof import ParamsType
  import theano.scalar
label_desc: Use image node_id 11 for calling the GpuCumOp obj's underlying member method code with example usage: obj.c_support_code_struct(node, nodename) and returns: unknown