| @@ -209,7 +209,8 @@ def _poisson( | |||||
| def _permutation(n: int, seed: int, device: str, handle: int, dtype: str) -> Tensor: | def _permutation(n: int, seed: int, device: str, handle: int, dtype: str) -> Tensor: | ||||
| assert isinstance(n, int) and n > 0, "Permutation is not defined when n <= 0" | |||||
| assert isinstance(n, int) | |||||
| assert n >= 0, "Permutation is not defined when n < 0" | |||||
| size = (n,) | size = (n,) | ||||
| op = PermutationRNG(seed=seed, handle=handle, dtype=dtype) | op = PermutationRNG(seed=seed, handle=handle, dtype=dtype) | ||||
| _ref = Tensor([], dtype="int32", device=device) | _ref = Tensor([], dtype="int32", device=device) | ||||
| @@ -10,7 +10,7 @@ import numpy as np | |||||
| import pytest | import pytest | ||||
| import megengine.functional as F | import megengine.functional as F | ||||
| from megengine import Tensor | |||||
| from megengine import Tensor, jit, random | |||||
| from megengine.core._imperative_rt import CompNode | from megengine.core._imperative_rt import CompNode | ||||
| from megengine.core._imperative_rt.core2 import apply | from megengine.core._imperative_rt.core2 import apply | ||||
| from megengine.core._imperative_rt.ops import ( | from megengine.core._imperative_rt.ops import ( | ||||
| @@ -402,3 +402,44 @@ def test_seed(): | |||||
| seed(11) | seed(11) | ||||
| out4 = uniform(size=[10, 10]) | out4 = uniform(size=[10, 10]) | ||||
| assert not (out1.numpy() == out4.numpy()).all() | assert not (out1.numpy() == out4.numpy()).all() | ||||
| @pytest.mark.parametrize("is_symbolic", [None, False, True]) | |||||
| def test_rng_empty_tensor(is_symbolic): | |||||
| shapes = [ | |||||
| (0,), | |||||
| (0, 0, 0), | |||||
| (10, 0, 10), | |||||
| ] | |||||
| def fn(shape): | |||||
| o1 = random.uniform(0, 1, shape) | |||||
| o2 = random.normal(0, 1, shape) | |||||
| o3 = random.gamma(2, 1, shape) | |||||
| o4 = random.beta(2, 1, shape) | |||||
| o5 = random.poisson(2, shape) | |||||
| return o1, o2, o3, o4, o5 | |||||
| for shape in shapes: | |||||
| if is_symbolic is not None: | |||||
| fn_ = jit.trace(symbolic=is_symbolic)(fn) | |||||
| else: | |||||
| fn_ = fn | |||||
| for _ in range(3): | |||||
| outs = fn_(shape) | |||||
| for out in outs: | |||||
| np.testing.assert_equal(out.numpy().shape, shape) | |||||
| if is_symbolic is None: | |||||
| break | |||||
| def fn2(n): | |||||
| return random.permutation(n=n) | |||||
| if is_symbolic is not None: | |||||
| fn2 = jit.trace(symbolic=is_symbolic)(fn2) | |||||
| for _ in range(3): | |||||
| out = fn2(0) | |||||
| np.testing.assert_equal(out.numpy().shape, (0,)) | |||||
| if is_symbolic is None: | |||||
| break | |||||
| @@ -312,12 +312,7 @@ struct _InferLayout<false> | |||||
| template<typename Op> | template<typename Op> | ||||
| static TensorLayout do_infer(const LogicalTensorDesc& inp, const Op& rng){ | static TensorLayout do_infer(const LogicalTensorDesc& inp, const Op& rng){ | ||||
| size_t size = inp.layout.total_nr_elems(); | |||||
| mgb_assert( | |||||
| size > 0, | |||||
| "target size of %s expects size>0; got size=%lu actually", | |||||
| rng.dyn_typeinfo()->name, | |||||
| size); | |||||
| mgb_assert(inp.layout.ndim); | |||||
| return inp.layout; | return inp.layout; | ||||
| } | } | ||||
| }; | }; | ||||
| @@ -376,6 +371,7 @@ void exec(const OpDef& op, const SmallVector<TensorPtr>& inputs, | |||||
| auto&& rng = op.cast_final_safe<Op>(); | auto&& rng = op.cast_final_safe<Op>(); | ||||
| auto dest = outputs[0]; | auto dest = outputs[0]; | ||||
| if (dest->layout().is_empty()) return; | |||||
| auto cn = dest->comp_node(); | auto cn = dest->comp_node(); | ||||
| auto handle = rng.handle; | auto handle = rng.handle; | ||||
| if (!handle) { | if (!handle) { | ||||
| @@ -48,6 +48,9 @@ cg::OperatorNodeBase::NodeProp* RNGOpr::do_make_node_prop() const { | |||||
| auto prop = Super::do_make_node_prop(); \ | auto prop = Super::do_make_node_prop(); \ | ||||
| prop->add_flag(NodeProp::Flag::IMPURE_FUNC); \ | prop->add_flag(NodeProp::Flag::IMPURE_FUNC); \ | ||||
| prop->reset_dep_type(input(), {NodeProp::DepType::HOST_VALUE}); \ | prop->reset_dep_type(input(), {NodeProp::DepType::HOST_VALUE}); \ | ||||
| for (auto i: input()) { \ | |||||
| prop->add_dep_type_existing_var(i, NodeProp::DepType::VALUE_ALLOW_EMPTY); \ | |||||
| } \ | |||||
| return prop; \ | return prop; \ | ||||
| } \ | } \ | ||||
| RNGOpr::RNGOpr(VarNode *shape, const Param ¶m, \ | RNGOpr::RNGOpr(VarNode *shape, const Param ¶m, \ | ||||
| @@ -56,7 +59,7 @@ RNGOpr::RNGOpr(VarNode *shape, const Param ¶m, | |||||
| { \ | { \ | ||||
| DType dtype = DType::from_enum(param.dtype); \ | DType dtype = DType::from_enum(param.dtype); \ | ||||
| add_input({shape}); \ | add_input({shape}); \ | ||||
| add_output(None)->dtype(dtype); \ | |||||
| add_output(None)->dtype(dtype).add_flag(VarNode::Flag::ALLOW_EMPTY_SHAPE); \ | |||||
| cg::add_workspace_output(this); \ | cg::add_workspace_output(this); \ | ||||
| add_equivalence_component<ScalarHash<void*>>(this); \ | add_equivalence_component<ScalarHash<void*>>(this); \ | ||||
| } \ | } \ | ||||
| @@ -84,7 +87,12 @@ void RNGOpr::init_output_static_infer_desc() { | |||||
| {SourceType::DEP, {{output(0), DepType::SHAPE}}, infer_wk}); \ | {SourceType::DEP, {{output(0), DepType::SHAPE}}, infer_wk}); \ | ||||
| } \ | } \ | ||||
| void RNGOpr::scn_do_execute() { \ | void RNGOpr::scn_do_execute() { \ | ||||
| m_dnn_opr->exec(output(0)->dev_tensor().as_megdnn(), \ | |||||
| auto&& ret = output(0); \ | |||||
| if (ret->layout().is_empty()) { \ | |||||
| mgb_assert(ret->dev_tensor().empty()); \ | |||||
| return; \ | |||||
| } \ | |||||
| m_dnn_opr->exec(ret->dev_tensor().as_megdnn(), \ | |||||
| get_megdnn_workspace_from_var(output(1))); \ | get_megdnn_workspace_from_var(output(1))); \ | ||||
| } | } | ||||
| @@ -105,7 +113,7 @@ RNGOpr::RNGOpr(_INPUTS(VarNode*,), const Param ¶m, | |||||
| Super({i0->owner_graph(), config, (name), {_INPUTS(,)}}, param) \ | Super({i0->owner_graph(), config, (name), {_INPUTS(,)}}, param) \ | ||||
| { \ | { \ | ||||
| add_input({_INPUTS(,)}); \ | add_input({_INPUTS(,)}); \ | ||||
| add_output(None)->dtype(i0->dtype()); \ | |||||
| add_output(None)->dtype(i0->dtype()).add_flag(VarNode::Flag::ALLOW_EMPTY_SHAPE); \ | |||||
| cg::add_workspace_output(this); \ | cg::add_workspace_output(this); \ | ||||
| add_equivalence_component<ScalarHash<void*>>(this); \ | add_equivalence_component<ScalarHash<void*>>(this); \ | ||||
| } \ | } \ | ||||
| @@ -132,9 +140,22 @@ void RNGOpr::add_input_layout_constraint(){ | |||||
| for (auto i : input()) i->add_layout_constraint_contiguous(); \ | for (auto i : input()) i->add_layout_constraint_contiguous(); \ | ||||
| }; \ | }; \ | ||||
| void RNGOpr::scn_do_execute() { \ | void RNGOpr::scn_do_execute() { \ | ||||
| auto&& ret = output(0); \ | |||||
| if (ret->layout().is_empty()) { \ | |||||
| mgb_assert(ret->dev_tensor().empty()); \ | |||||
| return; \ | |||||
| } \ | |||||
| m_dnn_opr->exec(_FOR_EACH(_AS_MEGDNN),output(0)->dev_tensor().as_megdnn(), \ | m_dnn_opr->exec(_FOR_EACH(_AS_MEGDNN),output(0)->dev_tensor().as_megdnn(), \ | ||||
| get_megdnn_workspace_from_var(output(1))); \ | get_megdnn_workspace_from_var(output(1))); \ | ||||
| } | |||||
| } \ | |||||
| cg::OperatorNodeBase::NodeProp* RNGOpr::do_make_node_prop() const { \ | |||||
| auto prop = Super::do_make_node_prop(); \ | |||||
| prop->add_flag(NodeProp::Flag::IMPURE_FUNC); \ | |||||
| for (auto i: input()) { \ | |||||
| prop->add_dep_type_existing_var(i, NodeProp::DepType::VALUE_ALLOW_EMPTY); \ | |||||
| } \ | |||||
| return prop; \ | |||||
| } | |||||
| /* ================= 1 input ================= */ | /* ================= 1 input ================= */ | ||||
| #define _INPUTS(prefix, subfix) prefix i0 subfix | #define _INPUTS(prefix, subfix) prefix i0 subfix | ||||
| @@ -67,6 +67,7 @@ _DEFINE_RNG_OPR_WITH_SHAPE_CLASS(PermutationRNG) | |||||
| #define _DEFINE_RNG_OPR_WITH_INPUT_CLASS(RNG) \ | #define _DEFINE_RNG_OPR_WITH_INPUT_CLASS(RNG) \ | ||||
| MGB_DEFINE_OPR_CLASS(RNG, RNGOprBase<megdnn::RNG>) \ | MGB_DEFINE_OPR_CLASS(RNG, RNGOprBase<megdnn::RNG>) \ | ||||
| void add_input_layout_constraint() override; \ | void add_input_layout_constraint() override; \ | ||||
| cg::OperatorNodeBase::NodeProp* do_make_node_prop() const override; \ | |||||
| public: \ | public: \ | ||||
| RNG(_INPUTS(VarNode*), const Param ¶m, \ | RNG(_INPUTS(VarNode*), const Param ¶m, \ | ||||
| const OperatorNodeConfig &config); \ | const OperatorNodeConfig &config); \ | ||||
| @@ -247,6 +247,92 @@ TEST(TestOprRand, PermutationRNG) { | |||||
| } | } | ||||
| } | } | ||||
| TEST(TestOprRand, EmptyShape) { | |||||
| auto test_uniform = []() { | |||||
| static constexpr size_t M = 128, N = 0; | |||||
| auto graph = ComputingGraph::make(); | |||||
| SymbolVar dev_out = opr::UniformRNG::make( | |||||
| *graph, {M, N}, {CompNode::load("xpu0")}, {23, DTypeEnum::Float32}); | |||||
| HostTensorND host_out; | |||||
| auto func = graph->compile({make_callback_copy(dev_out, host_out)}); | |||||
| func->execute(); | |||||
| ASSERT_EQ(host_out.shape(), TensorShape({M, N})); | |||||
| }; | |||||
| auto test_gaussian = []() { | |||||
| size_t SIZE = 0; | |||||
| constexpr float MEAN = 1, STD = 2; | |||||
| auto graph = ComputingGraph::make(); | |||||
| auto y = opr::GaussianRNG::make( | |||||
| SymbolVar::make_scalar(int(SIZE), *graph, {CompNode::load("xpu0")}), | |||||
| {23, MEAN, STD, DTypeEnum::Float32}); | |||||
| HostTensorND host_y; | |||||
| auto func = graph->compile({make_callback_copy(y, host_y)}); | |||||
| func->execute(); | |||||
| ASSERT_EQ(TensorShape({SIZE}), host_y.shape()); | |||||
| }; | |||||
| auto test_gamma = []() { | |||||
| std::shared_ptr<HostTensorND> shape_host(new HostTensorND{ | |||||
| CompNode::load("xpux"), TensorShape{10, 0}, dtype::Float32()}); | |||||
| std::shared_ptr<HostTensorND> scale_host(new HostTensorND{ | |||||
| CompNode::load("xpux"), TensorShape{10, 0}, dtype::Float32()}); | |||||
| auto graph = ComputingGraph::make(); | |||||
| auto shape_sym = opr::Host2DeviceCopy::make(*graph, shape_host); | |||||
| auto scale_sym = opr::Host2DeviceCopy::make(*graph, scale_host); | |||||
| auto y = opr::GammaRNG::make(shape_sym, scale_sym, {10}); | |||||
| HostTensorND host_y; | |||||
| auto func = graph->compile({make_callback_copy(y, host_y)}); | |||||
| func->execute(); | |||||
| ASSERT_EQ(TensorShape({10, 0}), host_y.shape()); | |||||
| }; | |||||
| auto test_poisson = []() { | |||||
| std::shared_ptr<HostTensorND> lam_host(new HostTensorND{ | |||||
| CompNode::load("xpux"), TensorShape{10, 0}, dtype::Float32()}); | |||||
| auto graph = ComputingGraph::make(); | |||||
| auto lam_sym = opr::Host2DeviceCopy::make(*graph, lam_host); | |||||
| auto y = opr::PoissonRNG::make(lam_sym, {10}); | |||||
| HostTensorND host_y; | |||||
| auto func = graph->compile({make_callback_copy(y, host_y)}); | |||||
| func->execute(); | |||||
| ASSERT_EQ(TensorShape({10, 0}), host_y.shape()); | |||||
| }; | |||||
| auto test_beta = []() { | |||||
| std::shared_ptr<HostTensorND> alpha_host(new HostTensorND{ | |||||
| CompNode::load("xpux"), TensorShape{10, 0}, dtype::Float32()}); | |||||
| std::shared_ptr<HostTensorND> beta_host(new HostTensorND{ | |||||
| CompNode::load("xpux"), TensorShape{10, 0}, dtype::Float32()}); | |||||
| auto graph = ComputingGraph::make(); | |||||
| auto alpha_sym = opr::Host2DeviceCopy::make(*graph, alpha_host); | |||||
| auto beta_sym = opr::Host2DeviceCopy::make(*graph, beta_host); | |||||
| auto y = opr::BetaRNG::make(alpha_sym,beta_sym, {10}); | |||||
| HostTensorND host_y; | |||||
| auto func = graph->compile({make_callback_copy(y, host_y)}); | |||||
| func->execute(); | |||||
| ASSERT_EQ(TensorShape({10, 0}), host_y.shape()); | |||||
| }; | |||||
| auto test_permutation = []() { | |||||
| static constexpr size_t SIZE = 0; | |||||
| auto graph = ComputingGraph::make(); | |||||
| auto y = opr::PermutationRNG::make( | |||||
| SymbolVar::make_scalar(int(SIZE), *graph, {CompNode::load("xpu0")}), | |||||
| {23, DTypeEnum::Int32}); | |||||
| HostTensorND host_y; | |||||
| auto func = graph->compile({make_callback_copy(y, host_y)}); | |||||
| func->execute(); | |||||
| ASSERT_EQ(TensorShape({SIZE}), host_y.shape()); | |||||
| }; | |||||
| test_uniform(); | |||||
| test_gaussian(); | |||||
| test_gamma(); | |||||
| test_poisson(); | |||||
| test_beta(); | |||||
| test_permutation(); | |||||
| } | |||||
| TEST(TestOprRand, UniformReprod) { | TEST(TestOprRand, UniformReprod) { | ||||
| static constexpr size_t SIZE = 123; | static constexpr size_t SIZE = 123; | ||||
| auto graph = ComputingGraph::make(); | auto graph = ComputingGraph::make(); | ||||