You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mxnet.apache.org by ha...@apache.org on 2018/10/30 17:47:17 UTC

[incubator-mxnet] branch master updated: sample_like operators (#13034)

This is an automated email from the ASF dual-hosted git repository.

haibin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-mxnet.git


The following commit(s) were added to refs/heads/master by this push:
     new afbb72f  sample_like operators (#13034)
afbb72f is described below

commit afbb72fa1ba92ced830459f83186601c57987ac4
Author: Sheng Zha <sz...@users.noreply.github.com>
AuthorDate: Tue Oct 30 10:47:01 2018 -0700

    sample_like operators (#13034)
---
 python/mxnet/base.py                 |  12 +-
 src/operator/random/sample_op.cc     | 179 +++++++++---
 src/operator/random/sample_op.cu     |  47 ++-
 src/operator/random/sample_op.h      | 533 ++++++++++++++++++++++++-----------
 tests/python/unittest/test_random.py | 189 +++++++++----
 5 files changed, 669 insertions(+), 291 deletions(-)

diff --git a/python/mxnet/base.py b/python/mxnet/base.py
index 7276c52..feb4d70 100644
--- a/python/mxnet/base.py
+++ b/python/mxnet/base.py
@@ -560,7 +560,7 @@ def _as_list(obj):
         return [obj]
 
 
-_OP_NAME_PREFIX_LIST = ['_contrib_', '_linalg_', '_sparse_', '_image_']
+_OP_NAME_PREFIX_LIST = ['_contrib_', '_linalg_', '_sparse_', '_image_', '_random_']
 
 
 def _get_op_name_prefix(op_name):
@@ -616,9 +616,13 @@ def _init_op_module(root_namespace, module_name, make_op_func):
         op_name_prefix = _get_op_name_prefix(name)
         module_name_local = module_name
         if len(op_name_prefix) > 0:
-            func_name = name[len(op_name_prefix):]
-            cur_module = submodule_dict[op_name_prefix]
-            module_name_local = "%s.%s.%s" % (root_namespace, module_name, op_name_prefix[1:-1])
+            if op_name_prefix != '_random_' or name.endswith('_like'):
+                func_name = name[len(op_name_prefix):]
+                cur_module = submodule_dict[op_name_prefix]
+                module_name_local = "%s.%s.%s" % (root_namespace, module_name, op_name_prefix[1:-1])
+            else:
+                func_name = name
+                cur_module = module_internal
         elif name.startswith('_'):
             func_name = name
             cur_module = module_internal
diff --git a/src/operator/random/sample_op.cc b/src/operator/random/sample_op.cc
index a2b3324..b18e704 100644
--- a/src/operator/random/sample_op.cc
+++ b/src/operator/random/sample_op.cc
@@ -37,15 +37,44 @@ DMLC_REGISTER_PARAMETER(SamplePoissonParam);
 DMLC_REGISTER_PARAMETER(SampleNegBinomialParam);
 DMLC_REGISTER_PARAMETER(SampleGenNegBinomialParam);
 
-#define MXNET_OPERATOR_REGISTER_SAMPLE(name, ParamType)                 \
-  NNVM_REGISTER_OP(name)                                                \
-  .set_num_inputs(0)                                                    \
-  .set_num_outputs(1)                                                   \
-  .set_attr_parser(ParamParser<ParamType>)                              \
-  .set_attr<nnvm::FInferShape>("FInferShape", InitShape<ParamType>)     \
-  .set_attr<nnvm::FInferType>("FInferType", SampleOpType<ParamType>)    \
-  .set_attr<FResourceRequest>("FResourceRequest", SampleResource)       \
-  .add_arguments(ParamType::__FIELDS__())
+DMLC_REGISTER_PARAMETER(SampleUniformLikeParam);
+DMLC_REGISTER_PARAMETER(SampleNormalLikeParam);
+DMLC_REGISTER_PARAMETER(SampleGammaLikeParam);
+DMLC_REGISTER_PARAMETER(SampleExponentialLikeParam);
+DMLC_REGISTER_PARAMETER(SamplePoissonLikeParam);
+DMLC_REGISTER_PARAMETER(SampleNegBinomialLikeParam);
+DMLC_REGISTER_PARAMETER(SampleGenNegBinomialLikeParam);
+
+#define MXNET_OPERATOR_REGISTER_SAMPLE(name, ParamType)                                      \
+  NNVM_REGISTER_OP(name)                                                                     \
+  .set_num_inputs(0)                                                                         \
+  .set_num_outputs(1)                                                                        \
+  .set_attr_parser(ParamParser<ParamType>)                                                   \
+  .set_attr<nnvm::FInferShape>("FInferShape", InitShape<ParamType>)                          \
+  .set_attr<nnvm::FInferType>("FInferType", SampleOpType<ParamType>)                         \
+  .set_attr<FResourceRequest>("FResourceRequest", SampleResource)                            \
+  .add_arguments(ParamType::__FIELDS__())                                                    \
+  .set_attr<FInferStorageType>("FInferStorageType", InitStorageType<ParamType, true, false>) \
+  .set_attr<FCompute>("FCompute<cpu>", Sample_<cpu, ParamType>)                              \
+  .set_attr<FComputeEx>("FComputeEx<cpu>", SampleEx_<cpu, ParamType>)
+
+#define MXNET_OPERATOR_REGISTER_SAMPLE_LIKE(name, ParamType)                              \
+  NNVM_REGISTER_OP(name)                                                                  \
+  .set_num_inputs(1)                                                                      \
+  .set_num_outputs(1)                                                                     \
+  .set_attr_parser(ParamParser<ParamType>)                                                \
+  .set_attr<nnvm::FInferShape>("FInferShape", ElemwiseShape<1, 1>)                        \
+  .set_attr<nnvm::FInferType>("FInferType", ElemwiseType<1, 1>)                           \
+  .set_attr<FResourceRequest>("FResourceRequest", SampleResource)                         \
+  .set_attr<nnvm::FIgnoreInputs>("FIgnoreInputs",                                         \
+    [](const NodeAttrs& attrs) { return std::vector<uint32_t>(1, 0); })                   \
+  .set_attr<nnvm::FGradient>("FGradient", MakeZeroGradNodes)                              \
+  .add_arguments(ParamType::__FIELDS__())                                                 \
+  .add_argument("data", "NDArray-or-Symbol", "The input")                                 \
+  .set_attr<FInferStorageType>("FInferStorageType",                                       \
+                               ElemwiseStorageType<1, 1, false, true, false>)             \
+  .set_attr<FCompute>("FCompute<cpu>", Sample_<cpu, ParamType>)                           \
+  .set_attr<FComputeEx>("FComputeEx<cpu>", SampleEx_<cpu, ParamType>)
 
 // Add "uniform" alias for backward compatibility
 MXNET_OPERATOR_REGISTER_SAMPLE(_random_uniform, SampleUniformParam)
@@ -63,10 +92,7 @@ Example::
    uniform(low=0, high=1, shape=(2,2)) = [[ 0.60276335,  0.85794562],
                                           [ 0.54488319,  0.84725171]]
 
-)code" ADD_FILELINE)
-.set_attr<FInferStorageType>("FInferStorageType", InitStorageType<SampleUniformParam, true, false>)
-.set_attr<FCompute>("FCompute<cpu>", Sample_<cpu, UniformSampler<cpu>>)
-.set_attr<FComputeEx>("FComputeEx<cpu>", SampleEx_<cpu, UniformSampler<cpu>>);
+)code" ADD_FILELINE);
 
 // Add "normal" alias for backward compatibility
 MXNET_OPERATOR_REGISTER_SAMPLE(_random_normal, SampleNormalParam)
@@ -76,16 +102,14 @@ MXNET_OPERATOR_REGISTER_SAMPLE(_random_normal, SampleNormalParam)
 
 .. note:: The existing alias ``normal`` is deprecated.
 
-Samples are distributed according to a normal distribution parametrized by *loc* (mean) and *scale* (standard deviation).
+Samples are distributed according to a normal distribution parametrized by *loc* (mean) and *scale*
+(standard deviation).
 
 Example::
 
    normal(loc=0, scale=1, shape=(2,2)) = [[ 1.89171135, -1.16881478],
                                           [-1.23474145,  1.55807114]]
-)code" ADD_FILELINE)
-.set_attr<FInferStorageType>("FInferStorageType", InitStorageType<SampleNormalParam, true, false>)
-.set_attr<FCompute>("FCompute<cpu>", Sample_<cpu, NormalSampler<cpu>>)
-.set_attr<FComputeEx>("FComputeEx<cpu>", SampleEx_<cpu, NormalSampler<cpu>>);
+)code" ADD_FILELINE);
 
 MXNET_OPERATOR_REGISTER_SAMPLE(_random_gamma, SampleGammaParam)
 .add_alias("random_gamma")
@@ -97,10 +121,7 @@ Example::
 
    gamma(alpha=9, beta=0.5, shape=(2,2)) = [[ 7.10486984,  3.37695289],
                                             [ 3.91697288,  3.65933681]]
-)code" ADD_FILELINE)
-.set_attr<FInferStorageType>("FInferStorageType", InitStorageType<SampleGammaParam, true, false>)
-.set_attr<FCompute>("FCompute<cpu>", Sample_<cpu, GammaSampler<cpu>>)
-.set_attr<FComputeEx>("FComputeEx<cpu>", SampleEx_<cpu, GammaSampler<cpu>>);
+)code" ADD_FILELINE);
 
 MXNET_OPERATOR_REGISTER_SAMPLE(_random_exponential, SampleExponentialParam)
 .add_alias("random_exponential")
@@ -112,11 +133,7 @@ Example::
 
    exponential(lam=4, shape=(2,2)) = [[ 0.0097189 ,  0.08999364],
                                       [ 0.04146638,  0.31715935]]
-)code" ADD_FILELINE)
-.set_attr<FInferStorageType>("FInferStorageType",
-                             InitStorageType<SampleExponentialParam, true, false>)
-.set_attr<FCompute>("FCompute<cpu>", Sample_<cpu, ExponentialSampler<cpu>>)
-.set_attr<FComputeEx>("FComputeEx<cpu>", SampleEx_<cpu, ExponentialSampler<cpu>>);
+)code" ADD_FILELINE);
 
 MXNET_OPERATOR_REGISTER_SAMPLE(_random_poisson, SamplePoissonParam)
 .add_alias("random_poisson")
@@ -129,10 +146,7 @@ Example::
 
    poisson(lam=4, shape=(2,2)) = [[ 5.,  2.],
                                   [ 4.,  6.]]
-)code" ADD_FILELINE)
-.set_attr<FInferStorageType>("FInferStorageType", InitStorageType<SamplePoissonParam, true, false>)
-.set_attr<FCompute>("FCompute<cpu>", Sample_<cpu, PoissonSampler<cpu>>)
-.set_attr<FComputeEx>("FComputeEx<cpu>", SampleEx_<cpu, PoissonSampler<cpu>>);
+)code" ADD_FILELINE);
 
 MXNET_OPERATOR_REGISTER_SAMPLE(_random_negative_binomial, SampleNegBinomialParam)
 .add_alias("random_negative_binomial")
@@ -146,11 +160,7 @@ Example::
 
    negative_binomial(k=3, p=0.4, shape=(2,2)) = [[ 4.,  7.],
                                                  [ 2.,  5.]]
-)code" ADD_FILELINE)
-.set_attr<FInferStorageType>("FInferStorageType",
-                             InitStorageType<SampleNegBinomialParam, true, false>)
-.set_attr<FCompute>("FCompute<cpu>", Sample_<cpu, NegativeBinomialSampler<cpu>>)
-.set_attr<FComputeEx>("FComputeEx<cpu>", SampleEx_<cpu, NegativeBinomialSampler<cpu>>);
+)code" ADD_FILELINE);
 
 MXNET_OPERATOR_REGISTER_SAMPLE(_random_generalized_negative_binomial, SampleGenNegBinomialParam)
 .add_alias("random_generalized_negative_binomial")
@@ -165,11 +175,98 @@ Example::
 
    generalized_negative_binomial(mu=2.0, alpha=0.3, shape=(2,2)) = [[ 2.,  1.],
                                                                     [ 6.,  4.]]
-)code" ADD_FILELINE)
-.set_attr<FInferStorageType>("FInferStorageType",
-                             InitStorageType<SampleGenNegBinomialParam, true, false>)
-.set_attr<FCompute>("FCompute<cpu>", Sample_<cpu, GeneralizedNegativeBinomialSampler<cpu>>)
-.set_attr<FComputeEx>("FComputeEx<cpu>", SampleEx_<cpu, GeneralizedNegativeBinomialSampler<cpu>>);
+)code" ADD_FILELINE);
+
+
+// *_like operators
+
+MXNET_OPERATOR_REGISTER_SAMPLE_LIKE(_random_uniform_like, SampleUniformLikeParam)
+.describe(R"code(Draw random samples from a uniform distribution according to the input array shape.
+
+Samples are uniformly distributed over the half-open interval *[low, high)*
+(includes *low*, but excludes *high*).
+
+Example::
+
+   uniform(low=0, high=1, data=ones(2,2)) = [[ 0.60276335,  0.85794562],
+                                             [ 0.54488319,  0.84725171]]
+
+)code" ADD_FILELINE);
+
+MXNET_OPERATOR_REGISTER_SAMPLE_LIKE(_random_normal_like, SampleNormalLikeParam)
+.describe(R"code(Draw random samples from a normal (Gaussian) distribution according to the input array shape.
+
+Samples are distributed according to a normal distribution parametrized by *loc* (mean) and *scale*
+(standard deviation).
+
+Example::
+
+   normal(loc=0, scale=1, data=ones(2,2)) = [[ 1.89171135, -1.16881478],
+                                             [-1.23474145,  1.55807114]]
+)code" ADD_FILELINE);
+
+MXNET_OPERATOR_REGISTER_SAMPLE_LIKE(_random_gamma_like, SampleGammaLikeParam)
+.describe(R"code(Draw random samples from a gamma distribution according to the input array shape.
+
+Samples are distributed according to a gamma distribution parametrized by *alpha* (shape) and *beta* (scale).
+
+Example::
+
+   gamma(alpha=9, beta=0.5, data=ones(2,2)) = [[ 7.10486984,  3.37695289],
+                                               [ 3.91697288,  3.65933681]]
+)code" ADD_FILELINE);
+
+MXNET_OPERATOR_REGISTER_SAMPLE_LIKE(_random_exponential_like, SampleExponentialLikeParam)
+.describe(R"code(Draw random samples from an exponential distribution according to the input array shape.
+
+Samples are distributed according to an exponential distribution parametrized by *lambda* (rate).
+
+Example::
+
+   exponential(lam=4, data=ones(2,2)) = [[ 0.0097189 ,  0.08999364],
+                                         [ 0.04146638,  0.31715935]]
+)code" ADD_FILELINE);
+
+MXNET_OPERATOR_REGISTER_SAMPLE_LIKE(_random_poisson_like, SamplePoissonLikeParam)
+.describe(R"code(Draw random samples from a Poisson distribution according to the input array shape.
+
+Samples are distributed according to a Poisson distribution parametrized by *lambda* (rate).
+Samples will always be returned as a floating point data type.
+
+Example::
+
+   poisson(lam=4, data=ones(2,2)) = [[ 5.,  2.],
+                                     [ 4.,  6.]]
+)code" ADD_FILELINE);
+
+MXNET_OPERATOR_REGISTER_SAMPLE_LIKE(_random_negative_binomial_like, SampleNegBinomialLikeParam)
+.describe(R"code(Draw random samples from a negative binomial distribution according to the input array shape.
+
+Samples are distributed according to a negative binomial distribution parametrized by
+*k* (limit of unsuccessful experiments) and *p* (failure probability in each experiment).
+Samples will always be returned as a floating point data type.
+
+Example::
+
+   negative_binomial(k=3, p=0.4, data=ones(2,2)) = [[ 4.,  7.],
+                                                    [ 2.,  5.]]
+)code" ADD_FILELINE);
+
+MXNET_OPERATOR_REGISTER_SAMPLE_LIKE(_random_generalized_negative_binomial_like,
+                                    SampleGenNegBinomialLikeParam)
+.describe(R"code(Draw random samples from a generalized negative binomial distribution according to the
+input array shape.
+
+Samples are distributed according to a generalized negative binomial distribution parametrized by
+*mu* (mean) and *alpha* (dispersion). *alpha* is defined as *1/k* where *k* is the failure limit of the
+number of unsuccessful experiments (generalized to real numbers).
+Samples will always be returned as a floating point data type.
+
+Example::
+
+   generalized_negative_binomial(mu=2.0, alpha=0.3, data=ones(2,2)) = [[ 2.,  1.],
+                                                                       [ 6.,  4.]]
+)code" ADD_FILELINE);
 
 }  // namespace op
 }  // namespace mxnet
diff --git a/src/operator/random/sample_op.cu b/src/operator/random/sample_op.cu
index 7a593d0..55c04a9 100644
--- a/src/operator/random/sample_op.cu
+++ b/src/operator/random/sample_op.cu
@@ -27,33 +27,26 @@
 namespace mxnet {
 namespace op {
 
-NNVM_REGISTER_OP(_random_uniform)
-.set_attr<FCompute>("FCompute<gpu>", Sample_<gpu, UniformSampler<gpu>>)
-.set_attr<FComputeEx>("FComputeEx<gpu>", SampleEx_<gpu, UniformSampler<gpu>>);
-
-NNVM_REGISTER_OP(_random_normal)
-.set_attr<FCompute>("FCompute<gpu>", Sample_<gpu, NormalSampler<gpu>>)
-.set_attr<FComputeEx>("FComputeEx<gpu>", SampleEx_<gpu, NormalSampler<gpu>>);
-
-NNVM_REGISTER_OP(_random_gamma)
-.set_attr<FCompute>("FCompute<gpu>", Sample_<gpu, GammaSampler<gpu>>)
-.set_attr<FComputeEx>("FComputeEx<gpu>", SampleEx_<gpu, GammaSampler<gpu>>);
-
-NNVM_REGISTER_OP(_random_exponential)
-.set_attr<FCompute>("FCompute<gpu>", Sample_<gpu, ExponentialSampler<gpu>>)
-.set_attr<FComputeEx>("FComputeEx<gpu>", SampleEx_<gpu, ExponentialSampler<gpu>>);
-
-NNVM_REGISTER_OP(_random_poisson)
-.set_attr<FCompute>("FCompute<gpu>", Sample_<gpu, PoissonSampler<gpu>>)
-.set_attr<FComputeEx>("FComputeEx<gpu>", SampleEx_<gpu, PoissonSampler<gpu>>);
-
-NNVM_REGISTER_OP(_random_negative_binomial)
-.set_attr<FCompute>("FCompute<gpu>", Sample_<gpu, NegativeBinomialSampler<gpu>>)
-.set_attr<FComputeEx>("FComputeEx<gpu>", SampleEx_<gpu, NegativeBinomialSampler<gpu>>);
-
-NNVM_REGISTER_OP(_random_generalized_negative_binomial)
-.set_attr<FCompute>("FCompute<gpu>", Sample_<gpu, GeneralizedNegativeBinomialSampler<gpu>>)
-.set_attr<FComputeEx>("FComputeEx<gpu>", SampleEx_<gpu, GeneralizedNegativeBinomialSampler<gpu>>);
+#define MXNET_OPERATOR_REGISTER_SAMPLE_GPU(name, ParamType)            \
+  NNVM_REGISTER_OP(name)                                               \
+  .set_attr<FCompute>("FCompute<gpu>", Sample_<gpu, ParamType>)        \
+  .set_attr<FComputeEx>("FComputeEx<gpu>", SampleEx_<gpu, ParamType>); \
+
+MXNET_OPERATOR_REGISTER_SAMPLE_GPU(_random_uniform, SampleUniformParam)
+MXNET_OPERATOR_REGISTER_SAMPLE_GPU(_random_normal, SampleNormalParam)
+MXNET_OPERATOR_REGISTER_SAMPLE_GPU(_random_gamma, SampleGammaParam)
+MXNET_OPERATOR_REGISTER_SAMPLE_GPU(_random_exponential, SampleExponentialParam)
+MXNET_OPERATOR_REGISTER_SAMPLE_GPU(_random_poisson, SamplePoissonParam)
+MXNET_OPERATOR_REGISTER_SAMPLE_GPU(_random_negative_binomial, SampleNegBinomialParam)
+MXNET_OPERATOR_REGISTER_SAMPLE_GPU(_random_generalized_negative_binomial, SampleGenNegBinomialParam)
+MXNET_OPERATOR_REGISTER_SAMPLE_GPU(_random_uniform_like, SampleUniformLikeParam)
+MXNET_OPERATOR_REGISTER_SAMPLE_GPU(_random_normal_like, SampleNormalLikeParam)
+MXNET_OPERATOR_REGISTER_SAMPLE_GPU(_random_gamma_like, SampleGammaLikeParam)
+MXNET_OPERATOR_REGISTER_SAMPLE_GPU(_random_exponential_like, SampleExponentialLikeParam)
+MXNET_OPERATOR_REGISTER_SAMPLE_GPU(_random_poisson_like, SamplePoissonLikeParam)
+MXNET_OPERATOR_REGISTER_SAMPLE_GPU(_random_negative_binomial_like, SampleNegBinomialLikeParam)
+MXNET_OPERATOR_REGISTER_SAMPLE_GPU(_random_generalized_negative_binomial_like,
+                                   SampleGenNegBinomialLikeParam)
 
 }  // namespace op
 }  // namespace mxnet
diff --git a/src/operator/random/sample_op.h b/src/operator/random/sample_op.h
index a81b41a..b4d00e7 100644
--- a/src/operator/random/sample_op.h
+++ b/src/operator/random/sample_op.h
@@ -38,12 +38,48 @@
 namespace mxnet {
 namespace op {
 
-struct SampleUniformParam : public dmlc::Parameter<SampleUniformParam> {
-  float low;
-  float high;
+
+struct SampleOpParam {
   TShape shape;
   std::string ctx;
   int dtype;
+};
+
+struct UniformParam {
+  float low;
+  float high;
+};
+
+struct NormalParam {
+  float loc;
+  float scale;
+};
+
+struct GammaParam {
+  float alpha;
+  float beta;
+};
+
+struct ExponentialParam {
+  float lam;
+};
+
+struct PoissonParam {
+  float lam;
+};
+
+struct NegBinomialParam {
+  int k;
+  float p;
+};
+
+struct GenNegBinomialParam {
+  float mu;
+  float alpha;
+};
+
+struct SampleUniformParam : public dmlc::Parameter<SampleUniformParam>,
+  UniformParam, SampleOpParam {
   DMLC_DECLARE_PARAMETER(SampleUniformParam) {
     DMLC_DECLARE_FIELD(low).set_default(0.0f)
     .describe("Lower bound of the distribution.");
@@ -67,12 +103,8 @@ struct SampleUniformParam : public dmlc::Parameter<SampleUniformParam> {
   }
 };
 
-struct SampleNormalParam : public dmlc::Parameter<SampleNormalParam> {
-  float loc;
-  float scale;
-  TShape shape;
-  std::string ctx;
-  int dtype;
+struct SampleNormalParam : public dmlc::Parameter<SampleNormalParam>,
+  NormalParam, SampleOpParam {
   DMLC_DECLARE_PARAMETER(SampleNormalParam) {
     DMLC_DECLARE_FIELD(loc).set_default(0.0f)
     .describe("Mean of the distribution.");
@@ -96,12 +128,8 @@ struct SampleNormalParam : public dmlc::Parameter<SampleNormalParam> {
   }
 };
 
-struct SampleGammaParam : public dmlc::Parameter<SampleGammaParam> {
-  float alpha;
-  float beta;
-  TShape shape;
-  std::string ctx;
-  int dtype;
+struct SampleGammaParam : public dmlc::Parameter<SampleGammaParam>,
+  GammaParam, SampleOpParam {
   DMLC_DECLARE_PARAMETER(SampleGammaParam) {
     DMLC_DECLARE_FIELD(alpha).set_default(1.0f)
     .describe("Alpha parameter (shape) of the gamma distribution.");
@@ -125,11 +153,8 @@ struct SampleGammaParam : public dmlc::Parameter<SampleGammaParam> {
   }
 };
 
-struct SampleExponentialParam : public dmlc::Parameter<SampleExponentialParam> {
-  float lam;
-  TShape shape;
-  std::string ctx;
-  int dtype;
+struct SampleExponentialParam : public dmlc::Parameter<SampleExponentialParam>,
+  ExponentialParam, SampleOpParam {
   DMLC_DECLARE_PARAMETER(SampleExponentialParam) {
     DMLC_DECLARE_FIELD(lam).set_default(1.0f)
     .describe("Lambda parameter (rate) of the exponential distribution.");
@@ -151,11 +176,8 @@ struct SampleExponentialParam : public dmlc::Parameter<SampleExponentialParam> {
   }
 };
 
-struct SamplePoissonParam : public dmlc::Parameter<SamplePoissonParam> {
-  float lam;
-  TShape shape;
-  std::string ctx;
-  int dtype;
+struct SamplePoissonParam : public dmlc::Parameter<SamplePoissonParam>,
+  PoissonParam, SampleOpParam {
   DMLC_DECLARE_PARAMETER(SamplePoissonParam) {
     DMLC_DECLARE_FIELD(lam).set_default(1.0f)
     .describe("Lambda parameter (rate) of the Poisson distribution.");
@@ -177,12 +199,8 @@ struct SamplePoissonParam : public dmlc::Parameter<SamplePoissonParam> {
   }
 };
 
-struct SampleNegBinomialParam : public dmlc::Parameter<SampleNegBinomialParam> {
-  int k;
-  float p;
-  TShape shape;
-  std::string ctx;
-  int dtype;
+struct SampleNegBinomialParam : public dmlc::Parameter<SampleNegBinomialParam>,
+  NegBinomialParam, SampleOpParam {
   DMLC_DECLARE_PARAMETER(SampleNegBinomialParam) {
     DMLC_DECLARE_FIELD(k).set_default(1)
     .describe("Limit of unsuccessful experiments.");
@@ -206,12 +224,8 @@ struct SampleNegBinomialParam : public dmlc::Parameter<SampleNegBinomialParam> {
   }
 };
 
-struct SampleGenNegBinomialParam : public dmlc::Parameter<SampleGenNegBinomialParam> {
-  float mu;
-  float alpha;
-  TShape shape;
-  std::string ctx;
-  int dtype;
+struct SampleGenNegBinomialParam : public dmlc::Parameter<SampleGenNegBinomialParam>,
+  GenNegBinomialParam, SampleOpParam {
   DMLC_DECLARE_PARAMETER(SampleGenNegBinomialParam) {
     DMLC_DECLARE_FIELD(mu).set_default(1.0f)
     .describe("Mean of the negative binomial distribution.");
@@ -235,6 +249,72 @@ struct SampleGenNegBinomialParam : public dmlc::Parameter<SampleGenNegBinomialPa
   }
 };
 
+struct SampleUniformLikeParam : public dmlc::Parameter<SampleUniformLikeParam>,
+  UniformParam {
+  DMLC_DECLARE_PARAMETER(SampleUniformLikeParam) {
+    DMLC_DECLARE_FIELD(low).set_default(0.0f)
+    .describe("Lower bound of the distribution.");
+    DMLC_DECLARE_FIELD(high).set_default(1.0f)
+    .describe("Upper bound of the distribution.");
+  }
+};
+
+struct SampleNormalLikeParam : public dmlc::Parameter<SampleNormalLikeParam>,
+  NormalParam {
+  DMLC_DECLARE_PARAMETER(SampleNormalLikeParam) {
+    DMLC_DECLARE_FIELD(loc).set_default(0.0f)
+    .describe("Mean of the distribution.");
+    DMLC_DECLARE_FIELD(scale).set_default(1.0f)
+    .describe("Standard deviation of the distribution.");
+  }
+};
+
+struct SampleGammaLikeParam : public dmlc::Parameter<SampleGammaLikeParam>,
+  GammaParam {
+  DMLC_DECLARE_PARAMETER(SampleGammaLikeParam) {
+    DMLC_DECLARE_FIELD(alpha).set_default(1.0f)
+    .describe("Alpha parameter (shape) of the gamma distribution.");
+    DMLC_DECLARE_FIELD(beta).set_default(1.0f)
+    .describe("Beta parameter (scale) of the gamma distribution.");
+  }
+};
+
+struct SampleExponentialLikeParam : public dmlc::Parameter<SampleExponentialLikeParam>,
+  ExponentialParam {
+  DMLC_DECLARE_PARAMETER(SampleExponentialLikeParam) {
+    DMLC_DECLARE_FIELD(lam).set_default(1.0f)
+    .describe("Lambda parameter (rate) of the exponential distribution.");
+  }
+};
+
+struct SamplePoissonLikeParam : public dmlc::Parameter<SamplePoissonLikeParam>,
+  PoissonParam {
+  DMLC_DECLARE_PARAMETER(SamplePoissonLikeParam) {
+    DMLC_DECLARE_FIELD(lam).set_default(1.0f)
+    .describe("Lambda parameter (rate) of the Poisson distribution.");
+  }
+};
+
+struct SampleNegBinomialLikeParam : public dmlc::Parameter<SampleNegBinomialLikeParam>,
+  NegBinomialParam {
+  DMLC_DECLARE_PARAMETER(SampleNegBinomialLikeParam) {
+    DMLC_DECLARE_FIELD(k).set_default(1)
+    .describe("Limit of unsuccessful experiments.");
+    DMLC_DECLARE_FIELD(p).set_default(1.0f)
+    .describe("Failure probability in each experiment.");
+  }
+};
+
+struct SampleGenNegBinomialLikeParam : public dmlc::Parameter<SampleGenNegBinomialLikeParam>,
+  GenNegBinomialParam {
+  DMLC_DECLARE_PARAMETER(SampleGenNegBinomialLikeParam) {
+    DMLC_DECLARE_FIELD(mu).set_default(1.0f)
+    .describe("Mean of the negative binomial distribution.");
+    DMLC_DECLARE_FIELD(alpha).set_default(1.0f)
+    .describe("Alpha (dispersion) parameter of the negative binomial distribution.");
+  }
+};
+
 using FSampleCompute = std::function<void (const nnvm::NodeAttrs& attrs,
                                            const OpContext& ctx,
                                            const OpReqType& req,
@@ -262,162 +342,289 @@ MSHADOW_FORCE_INLINE void GetSamplingTempData(DType p1, DType p2, const OpContex
   Copy(*parm2, Tensor<cpu, 1, DType>(&p2, Shape1(1)), s);
 }
 
-template<typename xpu, typename Sampler>
+template<typename xpu, typename ParamType>
+static inline void uniform_op(const nnvm::NodeAttrs& attrs,
+                              const OpContext& ctx,
+                              const OpReqType& req,
+                              TBlob* outputs) {
+  Stream<xpu> *s = ctx.get_stream<xpu>();
+  const UniformParam& param = nnvm::get<ParamType>(attrs.parsed);
+  CHECK_GE(param.high, param.low) << "low must be less or equal to high in uniform distribution";
+  Tensor<xpu, 1, float> low, high;
+  GetSamplingTempData<xpu, float>(param.low, param.high, ctx,
+                                  &low, &high);
+  UniformSampler<xpu> sampler;
+  MSHADOW_REAL_TYPE_SWITCH(outputs[0].type_flag_, OType, {
+    RandGenerator<xpu, OType> *pgen = ctx.requested[0].get_parallel_random<xpu, OType>();
+    Tensor<xpu, 1, OType> out = outputs->FlatTo1D<xpu, OType>(s);
+    sampler.Sample(low, high, out, pgen, s);
+  });
+}
+
+template<typename xpu, typename ParamType>
+static inline void normal_op(const nnvm::NodeAttrs& attrs,
+                             const OpContext& ctx,
+                             const OpReqType& req,
+                             TBlob* outputs) {
+  Stream<xpu> *s = ctx.get_stream<xpu>();
+  const NormalParam& param = nnvm::get<ParamType>(attrs.parsed);
+  CHECK_GT(param.scale, 0) << "scale parameter in gaussian has to be positive";
+  Tensor<xpu, 1, float> loc, scale;
+  GetSamplingTempData<xpu, float>(param.loc, param.scale, ctx, &loc, &scale);
+  NormalSampler<xpu> sampler;
+  MSHADOW_REAL_TYPE_SWITCH(outputs[0].type_flag_, OType, {
+    RandGenerator<xpu, OType> *pgen = ctx.requested[0].get_parallel_random<xpu, OType>();
+    Tensor<xpu, 1, OType> out = outputs->FlatTo1D<xpu, OType>(s);
+    sampler.Sample(loc, scale, out, pgen, s);
+  });
+}
+
+template<typename xpu, typename ParamType>
+static inline void gamma_op(const nnvm::NodeAttrs& attrs,
+                            const OpContext& ctx,
+                            const OpReqType& req,
+                            TBlob* outputs) {
+  Stream<xpu> *s = ctx.get_stream<xpu>();
+  const GammaParam& param = nnvm::get<ParamType>(attrs.parsed);
+  CHECK_GT(param.alpha, 0) << "alpha parameter in gamma distribution has to be positive";
+  CHECK_GT(param.beta, 0) << "beta parameter in gamma distribution has to be positive";
+  Tensor<xpu, 1, float> alpha, beta;
+  GetSamplingTempData<xpu, float>(param.alpha, param.beta, ctx, &alpha, &beta);
+  GammaSampler<xpu> sampler;
+  MSHADOW_REAL_TYPE_SWITCH(outputs[0].type_flag_, OType, {
+    RandGenerator<xpu, OType> *pgen = ctx.requested[0].get_parallel_random<xpu, OType>();
+    Tensor<xpu, 1, OType> out = outputs->FlatTo1D<xpu, OType>(s);
+    sampler.Sample(alpha, beta, out, pgen, s);
+  });
+}
+
+template<typename xpu, typename ParamType>
+static inline void exponential_op(const nnvm::NodeAttrs& attrs,
+                                  const OpContext& ctx,
+                                  const OpReqType& req,
+                                  TBlob* outputs) {
+  Stream<xpu> *s = ctx.get_stream<xpu>();
+  const ExponentialParam& param = nnvm::get<ParamType>(attrs.parsed);
+  CHECK_GT(param.lam, 0) << "lambda parameter in exponential distribution has to be positive";
+  Tensor<xpu, 1, float> lam, dummy;
+  GetSamplingTempData<xpu, float>(param.lam, 0, ctx, &lam, &dummy);
+  ExponentialSampler<xpu> sampler;
+  MSHADOW_REAL_TYPE_SWITCH(outputs[0].type_flag_, OType, {
+    RandGenerator<xpu, OType> *pgen = ctx.requested[0].get_parallel_random<xpu, OType>();
+    Tensor<xpu, 1, OType> out = outputs->FlatTo1D<xpu, OType>(s);
+    sampler.Sample(lam, out, pgen, s);
+  });
+}
+
+template<typename xpu, typename ParamType>
+static inline void poisson_op(const nnvm::NodeAttrs& attrs,
+                              const OpContext& ctx,
+                              const OpReqType& req,
+                              TBlob* outputs) {
+  Stream<xpu> *s = ctx.get_stream<xpu>();
+  const PoissonParam& param = nnvm::get<ParamType>(attrs.parsed);
+  CHECK_GE(param.lam, 0) << "lambda parameter in poisson distribution has to be non-negative";
+  Tensor<xpu, 1, float> lam, dummy;
+  GetSamplingTempData<xpu, float>(param.lam, 0, ctx, &lam, &dummy);
+  PoissonSampler<xpu> sampler;
+  MSHADOW_REAL_TYPE_SWITCH(outputs[0].type_flag_, OType, {
+    RandGenerator<xpu, OType> *pgen = ctx.requested[0].get_parallel_random<xpu, OType>();
+    Tensor<xpu, 1, OType> out = outputs->FlatTo1D<xpu, OType>(s);
+    sampler.Sample(lam, out, pgen, s);
+  });
+}
+
+template<typename xpu, typename ParamType>
+static inline void neg_binomial_op(const nnvm::NodeAttrs& attrs,
+                                   const OpContext& ctx,
+                                   const OpReqType& req,
+                                   TBlob* outputs) {
+  Stream<xpu> *s = ctx.get_stream<xpu>();
+  const NegBinomialParam& param = nnvm::get<ParamType>(attrs.parsed);
+  CHECK_GE(param.k, 0) << "k parameter in negative binomial distribution has to be non-negative";
+  CHECK_GE(param.p, 0) << "p parameter in negative binomial distribution has to be non-negative";
+  Tensor<xpu, 1, float> k, p;
+  GetSamplingTempData<xpu, float>(param.k, param.p, ctx, &k, &p);
+  NegativeBinomialSampler<xpu> sampler;
+  MSHADOW_REAL_TYPE_SWITCH(outputs[0].type_flag_, OType, {
+    RandGenerator<xpu, OType> *pgen = ctx.requested[0].get_parallel_random<xpu, OType>();
+    Tensor<xpu, 1, OType> out = outputs->FlatTo1D<xpu, OType>(s);
+    sampler.Sample(k, p, out, pgen, s);
+  });
+}
+
+template<typename xpu, typename ParamType>
+static inline void gen_neg_binomial_op(const nnvm::NodeAttrs& attrs,
+                                       const OpContext& ctx,
+                                       const OpReqType& req,
+                                       TBlob* outputs) {
+  Stream<xpu> *s = ctx.get_stream<xpu>();
+  const GenNegBinomialParam& param = nnvm::get<ParamType>(attrs.parsed);
+  CHECK_GE(param.mu, 0)
+    << "mu parameter in generalized negative binomial distribution has to be non-negative";
+  CHECK_GE(param.alpha, 0)
+    << "alpha parameter in generalized negative binomial distribution has to be non-negative";
+  Tensor<xpu, 1, float> mu, alpha;
+  GetSamplingTempData<xpu, float>(param.mu, param.alpha, ctx, &mu, &alpha);
+  GeneralizedNegativeBinomialSampler<xpu> sampler;
+  MSHADOW_REAL_TYPE_SWITCH(outputs[0].type_flag_, OType, {
+    RandGenerator<xpu, OType> *pgen = ctx.requested[0].get_parallel_random<xpu, OType>();
+    Tensor<xpu, 1, OType> out = outputs->FlatTo1D<xpu, OType>(s);
+    sampler.Sample(mu, alpha, out, pgen, s);
+  });
+}
+
+template<typename xpu, typename ParamType>
 struct SampleMaster;
 
 template<typename xpu>
-struct SampleMaster<xpu, UniformSampler<xpu>> {
-  static void op(const nnvm::NodeAttrs& attrs,
-                 const OpContext& ctx,
-                 const OpReqType& req,
-                 TBlob* outputs) {
-    Stream<xpu> *s = ctx.get_stream<xpu>();
-    const SampleUniformParam& param = nnvm::get<SampleUniformParam>(attrs.parsed);
-    CHECK_GE(param.high, param.low) << "low must be less or equal to high in uniform distribution";
-    Tensor<xpu, 1, float> low, high;
-    GetSamplingTempData<xpu, float>(param.low, param.high, ctx,
-                                    &low, &high);
-    UniformSampler<xpu> sampler;
-    MSHADOW_REAL_TYPE_SWITCH(outputs[0].type_flag_, OType, {
-      RandGenerator<xpu, OType> *pgen = ctx.requested[0].get_parallel_random<xpu, OType>();
-      Tensor<xpu, 1, OType> out = outputs->FlatTo1D<xpu, OType>(s);
-      sampler.Sample(low, high, out, pgen, s);
-    });
+struct SampleMaster<xpu, SampleUniformParam> {
+  static inline void op(const nnvm::NodeAttrs& attrs,
+                        const OpContext& ctx,
+                        const OpReqType& req,
+                        TBlob* outputs) {
+    uniform_op<xpu, SampleUniformParam>(attrs, ctx, req, outputs);
   }
 };
 
 template<typename xpu>
-struct SampleMaster<xpu, NormalSampler<xpu>> {
-  static void op(const nnvm::NodeAttrs& attrs,
-                 const OpContext& ctx,
-                 const OpReqType& req,
-                 TBlob* outputs) {
-    Stream<xpu> *s = ctx.get_stream<xpu>();
-    const SampleNormalParam& param = nnvm::get<SampleNormalParam>(attrs.parsed);
-    CHECK_GT(param.scale, 0) << "scale parameter in gaussian has to be positive";
-    Tensor<xpu, 1, float> loc, scale;
-    GetSamplingTempData<xpu, float>(param.loc, param.scale, ctx, &loc, &scale);
-    NormalSampler<xpu> sampler;
-    MSHADOW_REAL_TYPE_SWITCH(outputs[0].type_flag_, OType, {
-      RandGenerator<xpu, OType> *pgen = ctx.requested[0].get_parallel_random<xpu, OType>();
-      Tensor<xpu, 1, OType> out = outputs->FlatTo1D<xpu, OType>(s);
-      sampler.Sample(loc, scale, out, pgen, s);
-    });
+struct SampleMaster<xpu, SampleUniformLikeParam> {
+  static inline void op(const nnvm::NodeAttrs& attrs,
+                        const OpContext& ctx,
+                        const OpReqType& req,
+                        TBlob* outputs) {
+    uniform_op<xpu, SampleUniformLikeParam>(attrs, ctx, req, outputs);
   }
 };
 
 template<typename xpu>
-struct SampleMaster<xpu, GammaSampler<xpu>> {
-  static void op(const nnvm::NodeAttrs& attrs,
-                 const OpContext& ctx,
-                 const OpReqType& req,
-                 TBlob* outputs) {
-    Stream<xpu> *s = ctx.get_stream<xpu>();
-    const SampleGammaParam& param = nnvm::get<SampleGammaParam>(attrs.parsed);
-    CHECK_GT(param.alpha, 0) << "alpha parameter in gamma distribution has to be positive";
-    CHECK_GT(param.beta, 0) << "beta parameter in gamma distribution has to be positive";
-    Tensor<xpu, 1, float> alpha, beta;
-    GetSamplingTempData<xpu, float>(param.alpha, param.beta, ctx, &alpha, &beta);
-    GammaSampler<xpu> sampler;
-    MSHADOW_REAL_TYPE_SWITCH(outputs[0].type_flag_, OType, {
-      RandGenerator<xpu, OType> *pgen = ctx.requested[0].get_parallel_random<xpu, OType>();
-      Tensor<xpu, 1, OType> out = outputs->FlatTo1D<xpu, OType>(s);
-      sampler.Sample(alpha, beta, out, pgen, s);
-    });
+struct SampleMaster<xpu, SampleNormalParam> {
+  static inline void op(const nnvm::NodeAttrs& attrs,
+                        const OpContext& ctx,
+                        const OpReqType& req,
+                        TBlob* outputs) {
+    normal_op<xpu, SampleNormalParam>(attrs, ctx, req, outputs);
   }
 };
 
 template<typename xpu>
-struct SampleMaster<xpu, ExponentialSampler<xpu>> {
-  static void op(const nnvm::NodeAttrs& attrs,
-                 const OpContext& ctx,
-                 const OpReqType& req,
-                 TBlob* outputs) {
-    Stream<xpu> *s = ctx.get_stream<xpu>();
-    const SampleExponentialParam& param = nnvm::get<SampleExponentialParam>(attrs.parsed);
-    CHECK_GT(param.lam, 0) << "lambda parameter in exponential distribution has to be positive";
-    Tensor<xpu, 1, float> lam, dummy;
-    GetSamplingTempData<xpu, float>(param.lam, 0, ctx, &lam, &dummy);
-    ExponentialSampler<xpu> sampler;
-    MSHADOW_REAL_TYPE_SWITCH(outputs[0].type_flag_, OType, {
-      RandGenerator<xpu, OType> *pgen = ctx.requested[0].get_parallel_random<xpu, OType>();
-      Tensor<xpu, 1, OType> out = outputs->FlatTo1D<xpu, OType>(s);
-      sampler.Sample(lam, out, pgen, s);
-    });
+struct SampleMaster<xpu, SampleNormalLikeParam> {
+  static inline void op(const nnvm::NodeAttrs& attrs,
+                        const OpContext& ctx,
+                        const OpReqType& req,
+                        TBlob* outputs) {
+    normal_op<xpu, SampleNormalLikeParam>(attrs, ctx, req, outputs);
   }
 };
 
 template<typename xpu>
-struct SampleMaster<xpu, PoissonSampler<xpu>> {
-  static void op(const nnvm::NodeAttrs& attrs,
-                 const OpContext& ctx,
-                 const OpReqType& req,
-                 TBlob* outputs) {
-    Stream<xpu> *s = ctx.get_stream<xpu>();
-    const SamplePoissonParam& param = nnvm::get<SamplePoissonParam>(attrs.parsed);
-    CHECK_GE(param.lam, 0) << "lambda parameter in poisson distribution has to be non-negative";
-    Tensor<xpu, 1, float> lam, dummy;
-    GetSamplingTempData<xpu, float>(param.lam, 0, ctx, &lam, &dummy);
-    PoissonSampler<xpu> sampler;
-    MSHADOW_REAL_TYPE_SWITCH(outputs[0].type_flag_, OType, {
-      RandGenerator<xpu, OType> *pgen = ctx.requested[0].get_parallel_random<xpu, OType>();
-      Tensor<xpu, 1, OType> out = outputs->FlatTo1D<xpu, OType>(s);
-      sampler.Sample(lam, out, pgen, s);
-    });
+struct SampleMaster<xpu, SampleGammaParam> {
+  static inline void op(const nnvm::NodeAttrs& attrs,
+                        const OpContext& ctx,
+                        const OpReqType& req,
+                        TBlob* outputs) {
+    gamma_op<xpu, SampleGammaParam>(attrs, ctx, req, outputs);
   }
 };
 
 template<typename xpu>
-struct SampleMaster<xpu, NegativeBinomialSampler<xpu>> {
-  static void op(const nnvm::NodeAttrs& attrs,
-                 const OpContext& ctx,
-                 const OpReqType& req,
-                 TBlob* outputs) {
-    Stream<xpu> *s = ctx.get_stream<xpu>();
-    const SampleNegBinomialParam& param = nnvm::get<SampleNegBinomialParam>(attrs.parsed);
-    CHECK_GE(param.k, 0) << "k parameter in negative binomial distribution has to be non-negative";
-    CHECK_GE(param.p, 0) << "p parameter in negative binomial distribution has to be non-negative";
-    Tensor<xpu, 1, float> k, p;
-    GetSamplingTempData<xpu, float>(param.k, param.p, ctx, &k, &p);
-    NegativeBinomialSampler<xpu> sampler;
-    MSHADOW_REAL_TYPE_SWITCH(outputs[0].type_flag_, OType, {
-      RandGenerator<xpu, OType> *pgen = ctx.requested[0].get_parallel_random<xpu, OType>();
-      Tensor<xpu, 1, OType> out = outputs->FlatTo1D<xpu, OType>(s);
-      sampler.Sample(k, p, out, pgen, s);
-    });
+struct SampleMaster<xpu, SampleGammaLikeParam> {
+  static inline void op(const nnvm::NodeAttrs& attrs,
+                        const OpContext& ctx,
+                        const OpReqType& req,
+                        TBlob* outputs) {
+    gamma_op<xpu, SampleGammaLikeParam>(attrs, ctx, req, outputs);
   }
 };
 
 template<typename xpu>
-struct SampleMaster<xpu, GeneralizedNegativeBinomialSampler<xpu>> {
-  static void op(const nnvm::NodeAttrs& attrs,
-                 const OpContext& ctx,
-                 const OpReqType& req,
-                 TBlob* outputs) {
-    Stream<xpu> *s = ctx.get_stream<xpu>();
-    const SampleGenNegBinomialParam& param = nnvm::get<SampleGenNegBinomialParam>(attrs.parsed);
-    CHECK_GE(param.mu, 0)
-      << "mu parameter in generalized negative binomial distribution has to be non-negative";
-    CHECK_GE(param.alpha, 0)
-      << "alpha parameter in generalized negative binomial distribution has to be non-negative";
-    Tensor<xpu, 1, float> mu, alpha;
-    GetSamplingTempData<xpu, float>(param.mu, param.alpha, ctx, &mu, &alpha);
-    GeneralizedNegativeBinomialSampler<xpu> sampler;
-    MSHADOW_REAL_TYPE_SWITCH(outputs[0].type_flag_, OType, {
-      RandGenerator<xpu, OType> *pgen = ctx.requested[0].get_parallel_random<xpu, OType>();
-      Tensor<xpu, 1, OType> out = outputs->FlatTo1D<xpu, OType>(s);
-      sampler.Sample(mu, alpha, out, pgen, s);
-    });
+struct SampleMaster<xpu, SampleExponentialParam> {
+  static inline void op(const nnvm::NodeAttrs& attrs,
+                        const OpContext& ctx,
+                        const OpReqType& req,
+                        TBlob* outputs) {
+    exponential_op<xpu, SampleExponentialParam>(attrs, ctx, req, outputs);
+  }
+};
+
+template<typename xpu>
+struct SampleMaster<xpu, SampleExponentialLikeParam> {
+  static inline void op(const nnvm::NodeAttrs& attrs,
+                        const OpContext& ctx,
+                        const OpReqType& req,
+                        TBlob* outputs) {
+    exponential_op<xpu, SampleExponentialLikeParam>(attrs, ctx, req, outputs);
+  }
+};
+
+template<typename xpu>
+struct SampleMaster<xpu, SamplePoissonParam> {
+  static inline void op(const nnvm::NodeAttrs& attrs,
+                        const OpContext& ctx,
+                        const OpReqType& req,
+                        TBlob* outputs) {
+    poisson_op<xpu, SamplePoissonParam>(attrs, ctx, req, outputs);
+  }
+};
+
+template<typename xpu>
+struct SampleMaster<xpu, SamplePoissonLikeParam> {
+  static inline void op(const nnvm::NodeAttrs& attrs,
+                        const OpContext& ctx,
+                        const OpReqType& req,
+                        TBlob* outputs) {
+    poisson_op<xpu, SamplePoissonLikeParam>(attrs, ctx, req, outputs);
+  }
+};
+
+template<typename xpu>
+struct SampleMaster<xpu, SampleNegBinomialParam> {
+  static inline void op(const nnvm::NodeAttrs& attrs,
+                        const OpContext& ctx,
+                        const OpReqType& req,
+                        TBlob* outputs) {
+    neg_binomial_op<xpu, SampleNegBinomialParam>(attrs, ctx, req, outputs);
+  }
+};
+
+template<typename xpu>
+struct SampleMaster<xpu, SampleNegBinomialLikeParam> {
+  static inline void op(const nnvm::NodeAttrs& attrs,
+                        const OpContext& ctx,
+                        const OpReqType& req,
+                        TBlob* outputs) {
+    neg_binomial_op<xpu, SampleNegBinomialLikeParam>(attrs, ctx, req, outputs);
+  }
+};
+
+template<typename xpu>
+struct SampleMaster<xpu, SampleGenNegBinomialParam> {
+  static inline void op(const nnvm::NodeAttrs& attrs,
+                        const OpContext& ctx,
+                        const OpReqType& req,
+                        TBlob* outputs) {
+    gen_neg_binomial_op<xpu, SampleGenNegBinomialParam>(attrs, ctx, req, outputs);
   }
 };
 
-template<typename xpu, typename Sampler>
+template<typename xpu>
+struct SampleMaster<xpu, SampleGenNegBinomialLikeParam> {
+  static inline void op(const nnvm::NodeAttrs& attrs,
+                        const OpContext& ctx,
+                        const OpReqType& req,
+                        TBlob* outputs) {
+    gen_neg_binomial_op<xpu, SampleGenNegBinomialLikeParam>(attrs, ctx, req, outputs);
+  }
+};
+
+
+template<typename xpu, typename ParamType>
 void SampleComputeEx_(const nnvm::NodeAttrs& attrs,
                       const OpContext& ctx,
                       const std::vector<NDArray>& inputs,
                       const std::vector<OpReqType>& req,
                       const std::vector<NDArray>& outputs,
-                      SampleMaster<xpu, Sampler> sample_master) {
+                      SampleMaster<xpu, ParamType> sample_master) {
   using namespace mxnet::op;
   NDArray output = outputs[0];
   mshadow::Stream<xpu> *s = ctx.get_stream<xpu>();
@@ -438,24 +645,24 @@ void SampleComputeEx_(const nnvm::NodeAttrs& attrs,
   }
 }
 
-template<typename xpu, typename Sampler>
+template<typename xpu, typename ParamType>
 void Sample_(const nnvm::NodeAttrs& attrs,
              const OpContext& ctx,
              const std::vector<TBlob>& inputs,
              const std::vector<OpReqType>& req,
              const std::vector<TBlob>& outputs) {
   TBlob out = outputs[0];
-  SampleMaster<xpu, Sampler>::op(attrs, ctx, req[0], &out);
+  SampleMaster<xpu, ParamType>::op(attrs, ctx, req[0], &out);
 }
 
-template<typename xpu, typename Sampler>
+template<typename xpu, typename ParamType>
 void SampleEx_(const nnvm::NodeAttrs& attrs,
                const OpContext& ctx,
                const std::vector<NDArray>& inputs,
                const std::vector<OpReqType>& req,
                const std::vector<NDArray>& outputs) {
-  SampleMaster<xpu, Sampler> sample_master;
-  SampleComputeEx_<xpu, Sampler>(attrs, ctx, inputs, req, outputs, sample_master);
+  SampleMaster<xpu, ParamType> sample_master;
+  SampleComputeEx_<xpu, ParamType>(attrs, ctx, inputs, req, outputs, sample_master);
 }
 
 template<typename ParamType>
diff --git a/tests/python/unittest/test_random.py b/tests/python/unittest/test_random.py
index 4310658..6a59d86 100644
--- a/tests/python/unittest/test_random.py
+++ b/tests/python/unittest/test_random.py
@@ -46,6 +46,16 @@ def check_with_device(device, dtype):
             ]
         },
         {
+            'name': 'normal_like',
+            'symbol': mx.sym.random.normal_like,
+            'ndop': mx.nd.random.normal_like,
+            'params': { 'loc': 10.0, 'scale': 0.5 },
+            'checks': [
+                ('mean', lambda x, params: np.mean(x.astype(np.float64) - params['loc']),  tol),
+                ('std',  lambda x, params: np.std(x.astype(np.float64)) - params['scale'], tol)
+            ]
+        },
+        {
             'name': 'randn',
             'ndop': mx.nd.random.randn,
             'params': { 'loc': 10.0, 'scale': 0.5 },
@@ -66,62 +76,122 @@ def check_with_device(device, dtype):
             ]
         },
         {
-                'name': 'gamma',
-                'symbol': mx.sym.random.gamma,
-                'ndop': mx.nd.random.gamma,
-                'params': { 'alpha': 9.0, 'beta': 0.5 },
-                'inputs': [ ('alpha', [ [ 0.0, 2.5 ], [ 9.75, 11.0 ] ]) , ('beta', [ [ 1.0, 0.7 ], [ 0.5, 0.3 ] ]) ],
-                'checks': [
-                    ('mean', lambda x, params: np.mean(x.astype(np.float64)) - params['alpha'] * params['beta'], tol),
-                    ('std', lambda x, params: np.std(x.astype(np.float64)) - np.sqrt(params['alpha'] * params['beta'] ** 2), tol)
-                ]
-            },
-            {
-                'name': 'exponential',
-                'symbol': mx.sym.random.exponential,
-                'ndop': mx.nd.random.exponential,
-                'params': { 'scale': 1.0/4.0 },
-                'inputs': [ ('scale', [ [ 1.0/1.0, 1.0/8.5 ], [ 1.0/2.7 , 1.0/0.5 ] ]) ],
-                'checks': [
-                    ('mean', lambda x, params: np.mean(x.astype(np.float64)) - params['scale'], tol),
-                    ('std', lambda x, params: np.std(x.astype(np.float64)) - params['scale'], tol)
-                ]
-            },
-            {
-                'name': 'poisson',
-                'symbol': mx.sym.random.poisson,
-                'ndop': mx.nd.random.poisson,
-                'params': { 'lam': 4.0 },
-                'inputs': [ ('lam', [ [ 25.0, 8.5 ], [ 2.7 , 0.5 ] ]) ],
-                'checks': [
-                    ('mean', lambda x, params: np.mean(x.astype(np.float64)) - params['lam'], tol),
-                    ('std', lambda x, params: np.std(x.astype(np.float64)) - np.sqrt(params['lam']), tol)
-                ]
-            },
-            {
-                'name': 'neg-binomial',
-                'symbol': mx.sym.random.negative_binomial,
-                'ndop': mx.nd.random.negative_binomial,
-                'params': { 'k': 3, 'p': 0.4 },
-                'inputs': [ ('k', [ [ 3, 4 ], [ 5 , 6 ] ]) , ('p', [ [ 0.4 , 0.77 ], [ 0.5, 0.84 ] ]) ],
-                'checks': [
-                    ('mean', lambda x, params: np.mean(x.astype(np.float64)) - params['k'] * (1.0 - params['p']) /  params['p'], tol),
-                    ('std', lambda x, params: np.std(x.astype(np.float64)) - np.sqrt(params['k'] * (1.0 - params['p']))/params['p'], tol)
-                ]
-            },
-            {
-                'name': 'gen-neg-binomial',
-                'symbol': mx.sym.random.generalized_negative_binomial,
-                'ndop': mx.nd.random.generalized_negative_binomial,
-                'params': { 'mu': 2.0, 'alpha': 0.3 },
-                'inputs': [ ('mu', [ [ 2.0, 2.5 ], [ 1.3, 1.9 ] ]) , ('alpha', [ [ 1.0, 0.1 ], [ 0.2, 0.5 ] ]) ],
-                'checks': [
-                    ('mean', lambda x, params: np.mean(x.astype(np.float64)) - params['mu'], tol),
-                    ('std', lambda x, params: np.std(x.astype(np.float64)) - np.sqrt(params['mu'] + params['alpha'] * params['mu'] ** 2 ), tol)
-                ]
-            }
-
-        ]
+            'name': 'uniform_like',
+            'symbol': mx.sym.random.uniform_like,
+            'ndop': mx.nd.random.uniform_like,
+            'params': { 'low': -1.5, 'high': 3.0 },
+            'checks': [
+                ('mean', lambda x, params: np.mean(x.astype(np.float64)) - (params['low'] + params['high']) / 2.0, tol),
+                ('std', lambda x,  params: np.std(x.astype(np.float64)) - np.sqrt(1.0 / 12.0) * (params['high'] - params['low']), tol)
+            ]
+        },
+        {
+            'name': 'gamma',
+            'symbol': mx.sym.random.gamma,
+            'ndop': mx.nd.random.gamma,
+            'params': { 'alpha': 9.0, 'beta': 0.5 },
+            'inputs': [ ('alpha', [ [ 0.0, 2.5 ], [ 9.75, 11.0 ] ]) , ('beta', [ [ 1.0, 0.7 ], [ 0.5, 0.3 ] ]) ],
+            'checks': [
+                ('mean', lambda x, params: np.mean(x.astype(np.float64)) - params['alpha'] * params['beta'], tol),
+                ('std', lambda x, params: np.std(x.astype(np.float64)) - np.sqrt(params['alpha'] * params['beta'] ** 2), tol)
+            ]
+        },
+        {
+            'name': 'gamma_like',
+            'symbol': mx.sym.random.gamma_like,
+            'ndop': mx.nd.random.gamma_like,
+            'params': { 'alpha': 9.0, 'beta': 0.5 },
+            'checks': [
+                ('mean', lambda x, params: np.mean(x.astype(np.float64)) - params['alpha'] * params['beta'], tol),
+                ('std', lambda x, params: np.std(x.astype(np.float64)) - np.sqrt(params['alpha'] * params['beta'] ** 2), tol)
+            ]
+        },
+        {
+            'name': 'exponential',
+            'symbol': mx.sym.random.exponential,
+            'ndop': mx.nd.random.exponential,
+            'params': { 'scale': 1.0/4.0 },
+            'inputs': [ ('scale', [ [ 1.0/1.0, 1.0/8.5 ], [ 1.0/2.7 , 1.0/0.5 ] ]) ],
+            'checks': [
+                ('mean', lambda x, params: np.mean(x.astype(np.float64)) - params['scale'], tol),
+                ('std', lambda x, params: np.std(x.astype(np.float64)) - params['scale'], tol)
+            ]
+        },
+        {
+            'name': 'exponential_like',
+            'symbol': mx.sym.random.exponential_like,
+            'ndop': mx.nd.random.exponential_like,
+            'params': { 'lam': 4.0 },
+            'checks': [
+                ('mean', lambda x, params: np.mean(x.astype(np.float64)) - 1.0/params['lam'], tol),
+                ('std', lambda x, params: np.std(x.astype(np.float64)) - 1.0/params['lam'], tol)
+            ]
+        },
+        {
+            'name': 'poisson',
+            'symbol': mx.sym.random.poisson,
+            'ndop': mx.nd.random.poisson,
+            'params': { 'lam': 4.0 },
+            'inputs': [ ('lam', [ [ 25.0, 8.5 ], [ 2.7 , 0.5 ] ]) ],
+            'checks': [
+                ('mean', lambda x, params: np.mean(x.astype(np.float64)) - params['lam'], tol),
+                ('std', lambda x, params: np.std(x.astype(np.float64)) - np.sqrt(params['lam']), tol)
+            ]
+        },
+        {
+            'name': 'poisson_like',
+            'symbol': mx.sym.random.poisson_like,
+            'ndop': mx.nd.random.poisson_like,
+            'params': { 'lam': 4.0 },
+            'checks': [
+                ('mean', lambda x, params: np.mean(x.astype(np.float64)) - params['lam'], tol),
+                ('std', lambda x, params: np.std(x.astype(np.float64)) - np.sqrt(params['lam']), tol)
+            ]
+        },
+        {
+            'name': 'neg_binomial',
+            'symbol': mx.sym.random.negative_binomial,
+            'ndop': mx.nd.random.negative_binomial,
+            'params': { 'k': 3, 'p': 0.4 },
+            'inputs': [ ('k', [ [ 3, 4 ], [ 5 , 6 ] ]) , ('p', [ [ 0.4 , 0.77 ], [ 0.5, 0.84 ] ]) ],
+            'checks': [
+                ('mean', lambda x, params: np.mean(x.astype(np.float64)) - params['k'] * (1.0 - params['p']) /  params['p'], tol),
+                ('std', lambda x, params: np.std(x.astype(np.float64)) - np.sqrt(params['k'] * (1.0 - params['p']))/params['p'], tol)
+            ]
+        },
+        {
+            'name': 'neg_binomial_like',
+            'symbol': mx.sym.random.negative_binomial_like,
+            'ndop': mx.nd.random.negative_binomial_like,
+            'params': { 'k': 3, 'p': 0.4 },
+            'checks': [
+                ('mean', lambda x, params: np.mean(x.astype(np.float64)) - params['k'] * (1.0 - params['p']) /  params['p'], tol),
+                ('std', lambda x, params: np.std(x.astype(np.float64)) - np.sqrt(params['k'] * (1.0 - params['p']))/params['p'], tol)
+            ]
+        },
+        {
+            'name': 'gen_neg_binomial',
+            'symbol': mx.sym.random.generalized_negative_binomial,
+            'ndop': mx.nd.random.generalized_negative_binomial,
+            'params': { 'mu': 2.0, 'alpha': 0.3 },
+            'inputs': [ ('mu', [ [ 2.0, 2.5 ], [ 1.3, 1.9 ] ]) , ('alpha', [ [ 1.0, 0.1 ], [ 0.2, 0.5 ] ]) ],
+            'checks': [
+                ('mean', lambda x, params: np.mean(x.astype(np.float64)) - params['mu'], tol),
+                ('std', lambda x, params: np.std(x.astype(np.float64)) - np.sqrt(params['mu'] + params['alpha'] * params['mu'] ** 2 ), tol)
+            ]
+        },
+        {
+            'name': 'gen_neg_binomial_like',
+            'symbol': mx.sym.random.generalized_negative_binomial_like,
+            'ndop': mx.nd.random.generalized_negative_binomial_like,
+            'params': { 'mu': 2.0, 'alpha': 0.3 },
+            'checks': [
+                ('mean', lambda x, params: np.mean(x.astype(np.float64)) - params['mu'], tol),
+                ('std', lambda x, params: np.std(x.astype(np.float64)) - np.sqrt(params['mu'] + params['alpha'] * params['mu'] ** 2 ), tol)
+            ]
+        },
+
+    ]
 
     # Create enough samples such that we get a meaningful distribution.
     shape = (500, 500)
@@ -136,6 +206,10 @@ def check_with_device(device, dtype):
         if name == 'randn':
             params.pop('shape')  # randn does not accept shape param
             args = shape
+        if name.endswith('_like'):
+            params['data'] = mx.nd.ones(params.pop('shape'),
+                                        dtype=params.pop('dtype'),
+                                        ctx=params.pop('ctx'))
         mx.random.seed(128)
         ret1 = ndop(*args, **params).asnumpy()
         mx.random.seed(128)
@@ -171,6 +245,8 @@ def check_with_device(device, dtype):
         X = mx.sym.Variable("X")
         params = symbdic['params'].copy()
         params.update(shape=shape, dtype=dtype)
+        if name.endswith('_like'):
+            params['data'] = mx.sym.ones(params.pop('shape'))
         Y = symbol(**params) + X
         x = mx.nd.zeros(shape, dtype=dtype, ctx=device)
         xgrad = mx.nd.zeros(shape, dtype=dtype, ctx=device)
@@ -189,6 +265,7 @@ def check_with_device(device, dtype):
         ret1 = un1.asnumpy()
         for check_name, check_func, tol in symbdic['checks']:
             assert np.abs(check_func(ret1, params)) < tol, "symbolic test: %s check for `%s` did not pass" % (check_name, name)
+        if name.endswith('_like'): continue
 
         # check multi-distribution sampling
         symbol = symbdic['symbol']