From 52fa742c51f1adf583eaeb1275bbce48ec4aa96c Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Wed, 14 Feb 2018 10:19:54 -0800 Subject: [PATCH] Revert D6893040: Implementing Pow operator (this merges existing pow with a scalar and new pow with a tensor exponent). Summary: This reverts commit 30f614beea6f859fee25ce4f85573142885dde45 bypass-lint An infra SEV is better than not reverting this diff. If you copy this password, see you in SEV Review! cause_a_sev_many_files Differential Revision: D6893040 Original commit changeset: 30f614beea6f fbshipit-source-id: 5e98a24699088283f864efe31234874bdacbe3c3 --- caffe2/operators/elementwise_op.cu | 10 - caffe2/operators/math_ops.cc | 61 ++++ caffe2/operators/math_ops.cu | 3 + caffe2/operators/math_ops.h | 17 + caffe2/operators/pow_op.cc | 323 ------------------ caffe2/operators/pow_op.h | 149 -------- caffe2/python/hypothesis_test.py | 21 -- .../elementwise_op_broadcast_test.py | 52 --- .../operator_test/elementwise_ops_test.py | 25 -- 9 files changed, 81 insertions(+), 580 deletions(-) delete mode 100644 caffe2/operators/pow_op.cc delete mode 100644 caffe2/operators/pow_op.h diff --git a/caffe2/operators/elementwise_op.cu b/caffe2/operators/elementwise_op.cu index f8fb042ebad09..adbc0e44ce4d8 100644 --- a/caffe2/operators/elementwise_op.cu +++ b/caffe2/operators/elementwise_op.cu @@ -113,16 +113,6 @@ CUDA_FUNCTOR(Or, CUDA_OR, BoolTypes, FixedType); CUDA_FUNCTOR(Xor, CUDA_XOR, BoolTypes, FixedType); #undef CUDA_XOR -// pow, log and other math functions are defined in CUDA math library -// in header file math.h -#define CUDA_POW(x, y) (pow(x, y)) -CUDA_FUNCTOR( - Pow, - CUDA_POW, - TensorTypes /*NumericTypes*/, - SameTypeAsInput); -#undef CUDA_POW - __global__ void NotKernel(const int n, const bool* x, bool* y) { CUDA_1D_KERNEL_LOOP(i, n) { y[i] = !x[i]; diff --git a/caffe2/operators/math_ops.cc b/caffe2/operators/math_ops.cc index 36b2f867b0d09..f84852c46c0f7 100644 --- a/caffe2/operators/math_ops.cc +++ b/caffe2/operators/math_ops.cc @@ -82,4 +82,65 @@ OPERATOR_SCHEMA(Sign) .IdenticalTypeAndShape(); SHOULD_NOT_DO_GRADIENT(Sign); +REGISTER_CPU_OPERATOR( + Pow, + UnaryElementwiseWithArgsOp, CPUContext, PowFunctor>); + +OPERATOR_SCHEMA(Pow) + .NumInputs(1) + .NumOutputs(1) + .Arg("exponent", "The exponent of the power function.") + .AllowInplace({{0, 0}}) + .IdenticalTypeAndShape() + .SetDoc(R"DOC( +Pow takes input data (Tensor) and an argument exponent, and +produces one output data (Tensor) where the function `f(x) = x^exponent`, +is applied to the data tensor elementwise. +)DOC") + .Input(0, "X", "Input tensor of any shape") + .Output(0, "Y", "Output tensor (same size as X)"); + +class GetPowGradient : public GradientMakerBase { + using GradientMakerBase::GradientMakerBase; + vector GetGradientDefs() override { + ArgumentHelper arg_helper(def_); + float exponent = arg_helper.GetSingleArgument("exponent", 0.0); + Argument scale_arg; + scale_arg.set_name("scale"); + scale_arg.set_f(exponent); + Argument pow_arg; + pow_arg.set_name("exponent"); + if (I(0) != O(0)) { + pow_arg.set_f(exponent - 1); + } else { + LOG(WARNING) << "In-place Pow gradient, possible loss of precision"; + constexpr float kEps = 1e-12f; + CAFFE_ENFORCE(std::fabs(exponent) > kEps); + pow_arg.set_f((exponent - 1) / exponent); + } + return vector{CreateOperatorDef( + "Pow", + "", + std::vector{I(0)}, + std::vector{GI(0)}, + std::vector{pow_arg}), + CreateOperatorDef( + "Mul", + "", + std::vector{GI(0), GO(0)}, + std::vector{GI(0)}), + CreateOperatorDef( + "Scale", + "", + std::vector{GI(0)}, + std::vector{GI(0)}, + std::vector{scale_arg})}; + } + virtual bool CopyArguments() const override { + return false; + } +}; + +REGISTER_GRADIENT(Pow, GetPowGradient); + } // namespace caffe2 diff --git a/caffe2/operators/math_ops.cu b/caffe2/operators/math_ops.cu index f98a3bf27f351..377e941692339 100644 --- a/caffe2/operators/math_ops.cu +++ b/caffe2/operators/math_ops.cu @@ -52,4 +52,7 @@ REGISTER_CUDA_OPERATOR( REGISTER_CUDA_OPERATOR( Sign, UnaryElementwiseOp, CUDAContext, SignCUDAFunctor>); +REGISTER_CUDA_OPERATOR( + Pow, + UnaryElementwiseWithArgsOp, CUDAContext, PowFunctor>); } diff --git a/caffe2/operators/math_ops.h b/caffe2/operators/math_ops.h index 75a3d7be0394e..e06af243c125d 100644 --- a/caffe2/operators/math_ops.h +++ b/caffe2/operators/math_ops.h @@ -25,4 +25,21 @@ #include "caffe2/operators/elementwise_op.h" #include "caffe2/utils/math.h" +namespace caffe2 { + +struct PowFunctor { + explicit PowFunctor(OperatorBase& op) { + exponent_ = op.GetSingleArgument("exponent", 0); + } + + template + inline void + operator()(const int n, const T* x, T* y, Context* device_context) { + math::Powx(n, x, exponent_, y, device_context); + } + + float exponent_; +}; +} + #endif diff --git a/caffe2/operators/pow_op.cc b/caffe2/operators/pow_op.cc deleted file mode 100644 index e7a3dd9780dc7..0000000000000 --- a/caffe2/operators/pow_op.cc +++ /dev/null @@ -1,323 +0,0 @@ -/** - * Copyright (c) 2018-present, Facebook, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "caffe2/operators/pow_op.h" -#include "caffe2/utils/math.h" -// definition of NumericTypes and SameTypeAsInput is in below header file -//#include "caffe2/operators/elementwise_op.h" -#include - -namespace caffe2 { - -#define EIGEN_POW(x, y) (x.pow(y)) - -struct EigenPowFunctor { - template - inline void Run(size_t n, const T1* a, const T2* b, R* out, CPUContext*) { - if (b_is_scalar) { - EigenVectorArrayMap(out, n) = - EIGEN_POW((ConstEigenVectorArrayMap(a, n)), (b[0])); - } else { - EigenVectorArrayMap(out, n) = EIGEN_POW( - (ConstEigenVectorArrayMap(a, n)), - (ConstEigenVectorArrayMap(b, n))); - } - } - template - void RunWithBroadcast( - const T1* a, - const T2* b, - R* out, - size_t pre, - size_t n, - CPUContext*) { - EigenArrayMap(out, n, pre) = EIGEN_POW( - (ConstEigenArrayMap(a, n, pre)), - (ConstEigenVectorArrayMap(b, n)).rowwise().replicate(pre)); - /* - //below code only allows elementary ops, such as +, -, * and /, - //and does not allow operations, such as pow, exp and log - EIGEN_POW( - (ConstEigenArrayMap(a, n, pre).colwise()), - (ConstEigenVectorArrayMap(b, n))); - */ - } - template - void RunWithBroadcast2( - const T1* a, - const T2* b, - R* out, - size_t pre, - size_t n, - size_t post, - CPUContext*) { - for (int i = 0; i < pre; ++i) { - EigenArrayMap(out + i * n * post, post, n) = EIGEN_POW( - (ConstEigenArrayMap(a + i * n * post, post, n)), - (Eigen::Map>(b, n)) - .colwise() - .replicate(post)); - /* - //below code only allows elementary ops, such as +, -, * and /, - //and does not allow for operations, such as pow, exp and log - EIEGN_POW( - (ConstEigenArrayMap(a + i * n * post, post, n).rowwise()), - (Eigen::Map>(b, n))); - */ - } - } -}; - -REGISTER_CPU_OPERATOR( - Pow, - PowOp< - TensorTypes, /*NumericTypes,*/ - CPUContext, - EigenPowFunctor, - SameTypeAsInput>) - -OPERATOR_SCHEMA(Pow) - .NumInputs(1, 2) - .NumOutputs(1) - .Arg("exponent", "The exponent of the power function.") - .AllowInplace({{0, 0}, {1, 0}}) - .SetDoc(R"DOC( -Pow takes input data (Tensor) and an argument exponent, which can be a -scalar or another tensor. It produces one output data (Tensor), where -the function `f(x) = x^exponent` is applied to the data tensor elementwise. -)DOC") - .Input(0, "X", "Input tensor of any shape") - .Input(1, "exponent", "The exponent of the power function.") - .Output(0, "Y", "Output tensor (same size as X)"); - -class GetPowGradient : public GradientMakerBase { - using GradientMakerBase::GradientMakerBase; - vector GetGradientDefs() override { - ArgumentHelper arg_helper(def_); - if (arg_helper.HasArgument("exponent")) { // second input is a scalar - // function f(w,a) = w^a - // gradient operator with respect to first input tensor - // df/dw = a * w^(a-1) (all operations are component-wise) - float exponent = arg_helper.GetSingleArgument("exponent", 0.0); - Argument scale_arg; - scale_arg.set_name("scale"); - scale_arg.set_f(exponent); - Argument pow_arg; - pow_arg.set_name("exponent"); - if (I(0) != O(0)) { - pow_arg.set_f(exponent - 1); - } else { - LOG(WARNING) << "In-place Pow gradient, possible loss of precision"; - constexpr float kEps = 1e-12f; - CAFFE_ENFORCE(std::fabs(exponent) > kEps); - pow_arg.set_f((exponent - 1) / exponent); - } - return vector{CreateOperatorDef( - "Pow", - "", - std::vector{I(0)}, - std::vector{GI(0)}, - std::vector{pow_arg}), - CreateOperatorDef( - "Mul", - "", - std::vector{GI(0), GO(0)}, - std::vector{GI(0)}), - CreateOperatorDef( - "Scale", - "", - std::vector{GI(0)}, - std::vector{GI(0)}, - std::vector{scale_arg})}; - /* - // Alternative gradient computation - return vector{CreateOperatorDef( - "Div", - "", - std::vector{O(0), I(0)}, - std::vector{GI(0)}), - CreateOperatorDef( - "Mul", - "", - std::vector{GI(0), GO(0)}, - std::vector{GI(0)}), - CreateOperatorDef( - "Scale", - "", - std::vector{GI(0)}, - std::vector{GI(0)}, - std::vector{scale_arg})}; - */ - } else { // second input is a tensor - CAFFE_ENFORCE( - Def().input(0) != Def().output(0) && - Def().input(1) != Def().output(0), - "Gradient computation cannot be carried out if Pow uses in-place " - "computation: ", - ProtoDebugString(Def())); - vector grad_ops; - Argument one_arg; - one_arg.set_name("value"); - one_arg.set_f(1); - Argument broadcast, axis, axis_str, order; - bool bflag = ArgumentHelper::HasArgument(Def(), "broadcast"); - - if (bflag) { - if (ArgumentHelper::HasArgument(Def(), "broadcast")) { - broadcast = GetArgument(Def(), "broadcast"); - } else { - broadcast = MakeArgument("broadcast", 0); - } - if (ArgumentHelper::HasArgument(Def(), "axis")) { - axis = GetArgument(Def(), "axis"); - } else { - axis = MakeArgument("axis", -1); - } - if (ArgumentHelper::HasArgument(Def(), "axis_str")) { - axis_str = GetArgument(Def(), "axis_str"); - } else { - axis_str = MakeArgument("axis_str", ""); - } - if (ArgumentHelper::HasArgument(Def(), "order")) { - order = GetArgument(Def(), "order"); - } else { - order = MakeArgument("order", "NCHW"); - } - } - - // function f(w,a) = w^a - // gradient operator with respect to first input tensor - // df/dw = a * w^(a-1) (all operations are component-wise) - grad_ops.push_back(CreateOperatorDef( - "ConstantFill", - "", - std::vector{I(1)}, - std::vector{GI(1)}, - std::vector{one_arg})); - grad_ops.push_back(CreateOperatorDef( - "Sub", - "", - std::vector{I(1), GI(1)}, - std::vector{GI(1)})); - if (bflag) { - grad_ops.push_back(CreateOperatorDef( - "Pow", - "", - std::vector{I(0), GI(1)}, - std::vector{GI(0)}, - vector{broadcast, axis, axis_str, order})); - } else { - grad_ops.push_back(CreateOperatorDef( - "Pow", - "", - std::vector{I(0), GI(1)}, - std::vector{GI(0)})); - } - - grad_ops.push_back(CreateOperatorDef( - "Mul", - "", - std::vector{GI(0), GO(0)}, - std::vector{GI(0)})); - if (bflag) { - grad_ops.push_back(CreateOperatorDef( - "Mul", - "", - std::vector{GI(0), I(1)}, - std::vector{GI(0)}, - vector{broadcast, axis, axis_str, order})); - } else { - grad_ops.push_back(CreateOperatorDef( - "Mul", - "", - std::vector{GI(0), I(1)}, - std::vector{GI(0)})); - } - /* - // Alternative gradient computation (no broadcast support) - grad_ops.push_back(CreateOperatorDef( - "Div", - "", - std::vector{O(0), I(0)}, - std::vector{GI(0)})); - grad_ops.push_back(CreateOperatorDef( - "Mul", - "", - std::vector{GI(0), GO(0)}, - std::vector{GI(0)})); - grad_ops.push_back(CreateOperatorDef( - "Mul", - "", - std::vector{GI(0), I(1)}, - std::vector{GI(0)})); - */ - // gradient operator for with respect to second input tensor - // df/da = w^a * ln w (all operations are component-wise) - /* - // reset GI(1) to zero - Argument zero_arg; - zero_arg.set_name("value"); - zero_arg.set_f(0); - grad_ops.push_back(CreateOperatorDef( - "ConstantFill", - "", - std::vector{I(1)}, - std::vector{GI(1)}, - std::vector{zero_arg})); - */ - grad_ops.push_back(CreateOperatorDef( - "Log", - "", - std::vector{I(0)}, - std::vector{GI(1) + "_autogen_pre_red"})); - grad_ops.push_back(CreateOperatorDef( - "Mul", - "", - std::vector{GI(1) + "_autogen_pre_red", O(0)}, - std::vector{GI(1) + "_autogen_pre_red"})); - if (bflag) { - grad_ops.push_back(CreateOperatorDef( - "Mul", - "", - std::vector{GI(1) + "_autogen_pre_red", GO(0)}, - std::vector{GI(1) + "_autogen_pre_red"})); - grad_ops.push_back(CreateOperatorDef( - "SumReduceLike", - "", - vector{GI(1) + "_autogen_pre_red", I(1)}, - vector{GI(1)}, - vector{axis, axis_str, order})); - } else { - grad_ops.push_back(CreateOperatorDef( - "Mul", - "", - std::vector{GI(1) + "_autogen_pre_red", GO(0)}, - std::vector{GI(1)})); - } - - return grad_ops; - } - } - - // Argument `shape` is no longer needed in backprop. - bool CopyArguments() const override { - return false; - } -}; - -REGISTER_GRADIENT(Pow, GetPowGradient); - -} // namespace caffe2 diff --git a/caffe2/operators/pow_op.h b/caffe2/operators/pow_op.h deleted file mode 100644 index 579210ad66bfb..0000000000000 --- a/caffe2/operators/pow_op.h +++ /dev/null @@ -1,149 +0,0 @@ -/** - * Copyright (c) 2018-present, Facebook, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef CAFFE2_OPERATORS_POW_OP_H_ -#define CAFFE2_OPERATORS_POW_OP_H_ - -#include "caffe2/core/common_omp.h" -#include "caffe2/core/context.h" -#include "caffe2/core/logging.h" -#include "caffe2/core/operator.h" -#include "caffe2/utils/math.h" -// definition of NumericTypes and SameTypeAsInput is in below header file -#include "caffe2/operators/elementwise_op.h" - -namespace caffe2 { - -template < - typename InputTypes, - class Context, - class Functor, - class TypeMap = SameTypeAsInput> -class PowOp : public Operator { - public: - USE_OPERATOR_CONTEXT_FUNCTIONS; - - PowOp(const OperatorDef& operator_def, Workspace* ws) - : Operator(operator_def, ws), - OP_SINGLE_ARG(bool, "broadcast", enable_broadcast_, 0), - OP_SINGLE_ARG(int, "axis", axis_, -1), - OP_SINGLE_ARG(string, "axis_str", axis_str_, ""), - OP_SINGLE_ARG(string, "order", order_, "NCHW"), - functor_() { - if ((InputSize() == 1) && HasArgument("exponent")) { // UnaryElementwiseOp - exponent_ = this->template GetSingleArgument( - "exponent", 0); // based on pow_ops.h - } else if (InputSize() == 2) { // BinaryElementwiseOp - // Figure out the correct axis to use. - if (enable_broadcast_) { - if (axis_ != -1) { - // Get axis from an explicit axis argument. - CAFFE_ENFORCE_EQ( - axis_str_.size(), - 0, - "Args axis and axis_str cannot be used simultaneously."); - } else if (axis_str_.size()) { - // Get the axis index semantically. - CAFFE_ENFORCE_EQ( - axis_str_.size(), 1, "Unsupported axis string", axis_str_); - size_t semantic_axis_ = order_.find(axis_str_); - CAFFE_ENFORCE_NE( - semantic_axis_, - string::npos, - "Unrecognizable axis string ", - axis_str_, - " from order string ", - order_); - axis_ = semantic_axis_; - } - } else { - CAFFE_ENFORCE( - axis_ == -1 && axis_str_.size() == 0, - "Do not specify axis or axis_str if broadcast is not enabled."); - } - } else { - CAFFE_THROW( - "Only a tensor with an argument or two input tensors are supported as input to pow operator."); - } - } - - bool RunOnDevice() override { - return DispatchHelper::call(this, Input(0)); - } - - template - bool DoRunWithType() { - if ((InputSize() == 1) && HasArgument("exponent")) { // UnaryElementwiseOp - const auto& A = Input(0); - auto* C = Output(0); - C->ResizeLike(A); - const T* Adata = A.template data(); - auto* Cdata = - C->template mutable_data>(); - functor_.template Run( - A.size(), Adata, &exponent_, Cdata, &context_); - } else if (InputSize() == 2) { // BinaryElementwiseOp - const auto& A = Input(0); - const auto& B = Input(1); - auto* C = Output(0); - CAFFE_ENFORCE( - &B != C || !enable_broadcast_, - "In-place is allowed only with the first tensor when broadcasting"); - C->ResizeLike(A); - const T* Adata = A.template data(); - const T* Bdata = B.template data(); - auto* Cdata = - C->template mutable_data>(); - if (!enable_broadcast_) { - CAFFE_ENFORCE_EQ( - A.dims(), - B.dims(), - "Dimension mismatch - did you forget to set broadcast=1?"); - functor_.template Run( - A.size(), Adata, Bdata, Cdata, &context_); - } else if (B.size() == 1) { - functor_.template Run( - A.size(), Adata, Bdata, Cdata, &context_); - } else { - size_t pre, n, post; - std::tie(pre, n, post) = calculate_broadcast_sizes(A, B, axis_); - if (post == 1) { - functor_.template RunWithBroadcast( - Adata, Bdata, Cdata, pre, n, &context_); - } else { - functor_.template RunWithBroadcast2( - Adata, Bdata, Cdata, pre, n, post, &context_); - } - } - } else { - CAFFE_THROW( - "Only a tensor with an argument or two input tensors are supported as input to pow operator."); - } - return true; - } - - private: - bool enable_broadcast_; - int axis_; - string axis_str_; - string order_; - float exponent_; - Functor functor_; -}; - -} // namespace caffe2 - -#endif // CAFFE2_OPERATORS_POW_OP_H_ diff --git a/caffe2/python/hypothesis_test.py b/caffe2/python/hypothesis_test.py index 7492455e177ca..d1cfa806e3b1c 100644 --- a/caffe2/python/hypothesis_test.py +++ b/caffe2/python/hypothesis_test.py @@ -1107,27 +1107,6 @@ def log_ref(input_tensor): reference=log_ref) self.assertGradientChecks(gc, op, [input_tensor], 0, [0]) - @given(input_tensors=hu.tensors(n=2, elements=st.floats(min_value=2.0, max_value=3.0, allow_nan=False, allow_infinity=False)), - **hu.gcs_cpu_only) - def test_powt(self, input_tensors, gc, dc): - X1, X2 = input_tensors - - op = core.CreateOperator( - "Pow", - ["X1", "X2"], - ["output"] - ) - - def powt_ref(X1, X2): - return (np.power(X1,X2),) - - self.assertReferenceChecks( - device_option=gc, - op=op, - inputs=[X1, X2], - reference=powt_ref) - self.assertGradientChecks(gc, op, [X1, X2], 0, [0]) - def test_blobs_dequeue_timeout(self): op = core.CreateOperator( "CreateBlobsQueue", diff --git a/caffe2/python/operator_test/elementwise_op_broadcast_test.py b/caffe2/python/operator_test/elementwise_op_broadcast_test.py index 926545392785a..e4e44117c4c7d 100644 --- a/caffe2/python/operator_test/elementwise_op_broadcast_test.py +++ b/caffe2/python/operator_test/elementwise_op_broadcast_test.py @@ -183,58 +183,6 @@ def test_broadcast_Sub(self, gc, dc): self.assertDeviceChecks(dc, op, [X, Y], [0]) self.assertGradientChecks(gc, op, [X, Y], 1, [0]) - @given(**hu.gcs) - def test_broadcast_powt(self, gc, dc): - # Set broadcast and no axis, i.e. broadcasting last dimensions. - X = np.random.rand(2, 3, 4, 5).astype(np.float32) - Y = np.random.rand(4, 5).astype(np.float32) + 2.0 - - op = core.CreateOperator("Pow", ["X", "Y"], "out", broadcast=1) - workspace.FeedBlob("X", X) - workspace.FeedBlob("Y", Y) - workspace.RunOperatorOnce(op) - out = workspace.FetchBlob("out") - np.testing.assert_array_almost_equal(out, np.power(X, Y)) - self.assertDeviceChecks(dc, op, [X, Y], [0]) - self.assertGradientChecks(gc, op, [X, Y], 1, [0]) - - # broadcasting intermediate dimensions - X = np.random.rand(2, 3, 4, 5).astype(np.float32) - Y = np.random.rand(3, 4).astype(np.float32) + 2.0 - op = core.CreateOperator("Pow", ["X", "Y"], "out", broadcast=1, axis=1) - workspace.FeedBlob("X", X) - workspace.FeedBlob("Y", Y) - workspace.RunOperatorOnce(op) - out = workspace.FetchBlob("out") - np.testing.assert_array_almost_equal(out, np.power(X, Y[:, :, np.newaxis])) - self.assertDeviceChecks(dc, op, [X, Y], [0]) - self.assertGradientChecks(gc, op, [X, Y], 1, [0]) - - # broadcasting the first dimension - X = np.random.rand(2, 3, 4, 5).astype(np.float32) - Y = np.random.rand(2).astype(np.float32) + 2.0 - op = core.CreateOperator("Pow", ["X", "Y"], "out", broadcast=1, axis=0) - workspace.FeedBlob("X", X) - workspace.FeedBlob("Y", Y) - workspace.RunOperatorOnce(op) - out = workspace.FetchBlob("out") - np.testing.assert_array_almost_equal( - out, np.power(X, Y[:, np.newaxis, np.newaxis, np.newaxis])) - self.assertDeviceChecks(dc, op, [X, Y], [0]) - self.assertGradientChecks(gc, op, [X, Y], 1, [0]) - - # broadcasting with single elem dimensions at both ends - X = np.random.rand(2, 3, 4, 5).astype(np.float32) - Y = np.random.rand(1, 4, 1).astype(np.float32) + 2.0 - op = core.CreateOperator("Pow", ["X", "Y"], "out", broadcast=1, axis=1) - workspace.FeedBlob("X", X) - workspace.FeedBlob("Y", Y) - workspace.RunOperatorOnce(op) - out = workspace.FetchBlob("out") - np.testing.assert_array_almost_equal(out, np.power(X, Y)) - self.assertDeviceChecks(dc, op, [X, Y], [0]) - self.assertGradientChecks(gc, op, [X, Y], 1, [0]) - @given(**hu.gcs) def test_broadcast_scalar(self, gc, dc): # broadcasting constant diff --git a/caffe2/python/operator_test/elementwise_ops_test.py b/caffe2/python/operator_test/elementwise_ops_test.py index 10fb7cc523701..3e0259408304e 100644 --- a/caffe2/python/operator_test/elementwise_ops_test.py +++ b/caffe2/python/operator_test/elementwise_ops_test.py @@ -75,31 +75,6 @@ def log_op(X): self.assertGradientChecks( gc, op, [X], 0, [0], stepsize=1e-4, threshold=1e-2) - @given(n=st.integers(2, 10), m=st.integers(4, 6), - d=st.integers(2, 3), **hu.gcs) - def test_powt(self, n, m, d, gc, dc): - X = np.random.rand(n, m, d).astype(np.float32) - Y = np.random.rand(n, m, d).astype(np.float32) + 2.0 - - def powt_op(X, Y): - return [np.power(X, Y)] - - op = core.CreateOperator( - "Pow", - ["X", "Y"], - ["Z"] - ) - - self.assertReferenceChecks( - device_option=gc, - op=op, - inputs=[X, Y], - reference=powt_op, - ) - - self.assertGradientChecks( - gc, op, [X, Y], 0, [0], stepsize=1e-4, threshold=1e-2) - @given(n=st.integers(5, 6), m=st.integers(4, 6), **hu.gcs) def test_sqr(self, n, m, gc, dc): X = np.random.rand(n, m).astype(np.float32)