class_docstr: Return a sparse matrix having random values from a multinomial
  density having number of experiment `n` and probability of success
  `p`.
  
  WARNING: This Op is NOT deterministic, as calling it twice with the
  same inputs will NOT give the same result. This is a violation of
  PyTensor's contract for Ops
  
  :param n: Tensor type vector or scalar representing the number of
            experiment for each row. If `n` is a scalar, it will be
            used for each row.
  :param p: Sparse matrix of probability where each row is a probability
            vector representing the probability of success. N.B. Each row
            must sum to one.
  
  :return: A sparse matrix of random integers from a multinomial density
           for each row.
  
  :note: It will works only if `p` have csr format.
class_docstr_tok: ["Return","a","sparse","matrix","having","random","values","from","a","multinomial","density","having","number","of","experiment","`","n","`","and","probability","of","success","`","p","`",".","WARNING",":","This","Op","is","NOT","deterministic",",","as","calling","it","twice","with","the","same","inputs","will","NOT","give","the","same","result",".","This","is","a","violation","of","PyTensor","'s","contract","for","Ops",":","param","n",":","Tensor","type","vector","or","scalar","representing","the","number","of","experiment","for","each","row",".","If","`","n","`","is","a","scalar",",","it","will","be","used","for","each","row",".",":","param","p",":","Sparse","matrix","of","probability","where","each","row","is","a","probability","vector","representing","the","probability","of","success",".","N.B",".","Each","row","must","sum","to","one",".",":","return",":","A","sparse","matrix","of","random","integers","from","a","multinomial","density","for","each","row",".",":","note",":","It","will","works","only","if","`","p","`","have","csr","format","."]
code: def perform(self, node, inputs, outputs):
      (n, p) = inputs
      (out,) = outputs
      assert _is_sparse(p)
  
      if p.format != "csr":
          raise NotImplementedError
  
      out[0] = p.copy()
  
      if n.ndim == 0:
          for i in range(p.shape[0]):
              k, l = p.indptr[i], p.indptr[i + 1]
              out[0].data[k:l] = np.random.multinomial(n, p.data[k:l])
      elif n.ndim == 1:
          if n.shape[0] != p.shape[0]:
              raise ValueError(
                  "The number of element of n must be "
                  "the same as the number of row of p."
              )
          for i in range(p.shape[0]):
              k, l = p.indptr[i], p.indptr[i + 1]
              out[0].data[k:l] = np.random.multinomial(
                  n[i], p.data[k:l]
              )
  
imports: import numpy
  import scipy.sparse
  import pytensor
  from pytensor import tensor
  from pytensor.graph.basic import Apply
  from pytensor.graph.op import Op
  from pytensor.sparse.basic import Remove0, SparseTensorType, _is_sparse, as_sparse_variable, remove0
  from pytensor.tensor.type import discrete_dtypes, float_dtypes
label_desc: Use image node_id 2 for calling the Multinomial obj's underlying member method code with example usage: obj.perform(node, inputs, outputs) without return types