class_docstr: The output is transposed compared to MultinomialWOReplacementFromUniform.
  We must insert a Transpose op after it.
  
  The optimization that moves it to the gpu does it.
code: def gpu_kernels(self, node, name):
      replace = int(self.replace)
      code = """#include "cluda.h"
  
  KERNEL void k_multi_warp_multinomial_wor(
      const ga_size nb_multi,
      const ga_size nb_outcomes,
      const ga_size n_samples,
      GLOBAL_MEM float * global_pvals_copy,
      const ga_size global_pvals_offset,
      const ga_ssize pvals_row_stride,
      const ga_ssize pvals_col_stride,
      GLOBAL_MEM float * global_unis,
      const ga_size global_unis_offset,
      const ga_ssize unis_stride,
      GLOBAL_MEM ga_long * global_outs,
      const ga_size global_outs_offset,
      const ga_ssize outs_row_stride,
      const ga_ssize outs_col_stride
  )
  {
      global_pvals_copy = (GLOBAL_MEM float *)(((GLOBAL_MEM char *)global_pvals_copy) + global_pvals_offset);
      global_unis = (GLOBAL_MEM float *)(((GLOBAL_MEM char *)global_unis) + global_unis_offset);
      global_outs = (GLOBAL_MEM ga_long *)(((GLOBAL_MEM char *)global_outs) + global_outs_offset);
      // each thread takes care of one multinomial-wor n_samples-draw
      int n = LDIM_0*GID_0 + LID_0;
  
      if (n < nb_multi)
      {
          // Sum of the remaining p_vals in global_pvals_copy[n]
          float pvals_sum = 1.;
          for (int c = 0; c < n_samples; ++c)
          {
              float cummul = 0.;
              const float unis_n = global_unis[(c * nb_multi + n)*unis_stride] * pvals_sum;
              for (ga_size m = 0; m < nb_outcomes; ++m)
              {
                  float pvals_nm = global_pvals_copy[m * pvals_col_stride + n * pvals_row_stride];
                  cummul += pvals_nm;
  
                  if (unis_n < cummul)
                  {
                      // write out transposed for speed.
                      global_outs[n * outs_col_stride +
                                  c * outs_row_stride] = m;
  
                      if (! %(replace)s )
                      {
                          global_pvals_copy[m * pvals_col_stride + n * pvals_row_stride] = 0.0;
                          pvals_sum -= pvals_nm;
                      }
                      break;
                  }
              }
          }
      }
  }
  """ % {
          "replace": replace
      }
      return [
          Kernel(
              code=code,
              name="k_multi_warp_multinomial_wor",
              params=[
                  pygpu.gpuarray.SIZE,
                  pygpu.gpuarray.SIZE,
                  pygpu.gpuarray.SIZE,
                  pygpu.gpuarray.GpuArray,
                  pygpu.gpuarray.SIZE,
                  pygpu.gpuarray.SSIZE,
                  pygpu.gpuarray.SSIZE,
                  pygpu.gpuarray.GpuArray,
                  pygpu.gpuarray.SIZE,
                  pygpu.gpuarray.SSIZE,
                  pygpu.gpuarray.GpuArray,
                  pygpu.gpuarray.SIZE,
                  pygpu.gpuarray.SSIZE,
                  pygpu.gpuarray.SSIZE,
              ],
              flags=Kernel.get_flags(node.outputs[0].dtype),
              objvar="k_multi_warp_multinomial_wor_" + name,
          )
      ]
  
imports: from __future__ import absolute_import, print_function, division
  import warnings
  import theano
  import theano.sandbox.multinomial
  from theano import Apply
  from theano.gof import Op
  from theano.tensor import NotScalarConstantError, get_scalar_constant_value
  from .basic_ops import as_gpuarray_variable, infer_context_name, GpuKernelBase, Kernel, gpuarray_helper_inc_dir
  from .opt import register_opt, op_lifter, register_opt2
  from .type import GpuArrayType
  from .elemwise import GpuDimShuffle
  from theano.scalar import as_scalar
  from .fp16_help import write_w, load_w, work_dtype
label_desc: Use image node_id 7 for calling the GPUAChoiceFromUniform obj's underlying member method code with example usage: obj.gpu_kernels(node, name) and returns: list