// Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved. // // 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 #include "paddle/common/macros.h" #include "paddle/fluid/framework/new_executor/pir_interpreter.h" #include "paddle/fluid/framework/new_executor/standalone_executor.h" #include "paddle/fluid/pir/dialect/operator/ir/api_builder.h" #include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h" #include "paddle/fluid/pir/dialect/operator/ir/op_type.h" #include "paddle/fluid/pir/dialect/operator/ir/pd_op.h" #include "paddle/fluid/pir/dialect/operator/utils/utils.h" #include "paddle/fluid/pir/transforms/pd_op_to_kernel_pass.h" #include "paddle/pir/include/core/block.h" #include "paddle/pir/include/core/builtin_attribute.h" #include "paddle/pir/include/core/builtin_op.h" #include "paddle/pir/include/core/ir_context.h" #include "paddle/pir/include/core/program.h" #include "paddle/pir/include/core/utils.h" DECLARE_FILE_SYMBOLS(kernel_dialect); PD_DECLARE_KERNEL(full, CPU, ALL_LAYOUT); PD_DECLARE_KERNEL(tanh, CPU, ALL_LAYOUT); PD_DECLARE_KERNEL(mean, CPU, ALL_LAYOUT); PD_DECLARE_KERNEL(tanh_grad, CPU, ALL_LAYOUT); PD_DECLARE_KERNEL(mean_grad, CPU, ALL_LAYOUT); PD_DECLARE_KERNEL(add_grad, CPU, ALL_LAYOUT); namespace paddle { namespace framework { pir::Operation* GetOpFromProgram(const std::string& op_name, const pir::Program& program) { for (auto& op : *(program.block())) { if (op.name() == op_name) { return &op; } } return nullptr; } TEST(VJP, TanhBackwardTest) { pir::IrContext* ctx = pir::IrContext::Instance(); ctx->GetOrRegisterDialect(); pir::Program program((ctx)); paddle::dialect::ApiBuilder::Instance().SetProgram(&program); std::shared_ptr builder = paddle::dialect::ApiBuilder::Instance().GetBuilder(); paddle::dialect::FullOp op1 = builder->Build( std::vector{1}, 1.0, phi::DataType::FLOAT32, phi::CPUPlace()); paddle::dialect::TanhOp op2 = builder->Build(op1.out()); paddle::dialect::FullOp op3 = builder->Build( std::vector{1}, 2.0, phi::DataType::FLOAT32, phi::CPUPlace()); std::vector> stop_gradients{{false}}; std::vector> inputs{{op1.out()}}; std::vector> outputs{{op2.out()}}; std::vector> out_grads{{op3.out()}}; pir::OpInfo op2_info = ctx->GetRegisteredOpInfo("pd_op.tanh"); auto tanh_vjp_interface_impl = op2_info.GetInterfaceImpl(); tanh_vjp_interface_impl->vjp_( op2.operation(), inputs, outputs, out_grads, stop_gradients); builder->Build(op2->result(0), "tanh_out"); builder->Build( GetOpFromProgram("pd_op.tanh_grad", program)->result(0), "tanh_grad_out"); auto kernel_program = pir::PdOpLowerToKernelPass(&program); auto place = phi::CPUPlace(); Scope scope; InterpreterCore test_core(place, {}, kernel_program->block(), &scope); test_core.SetSkipGcVars({"tanh_out", "tanh_grad_out"}); test_core.Run({}); auto out_tensor = test_core.local_scope() == nullptr ? scope.FindVar("tanh_out")->Get() : test_core.local_scope() ->FindVar("tanh_out") ->Get(); auto grad_out_tensor = test_core.local_scope() == nullptr ? scope.FindVar("tanh_grad_out")->Get() : test_core.local_scope() ->FindVar("tanh_grad_out") ->Get(); ASSERT_NEAR(out_tensor.data()[0], 0.76159, 1e-5); ASSERT_NEAR(grad_out_tensor.data()[0], 0.83995, 1e-5); } TEST(VJP, Tanh_BackwardTest) { pir::IrContext* ctx = pir::IrContext::Instance(); ctx->GetOrRegisterDialect(); pir::Program program((ctx)); paddle::dialect::ApiBuilder::Instance().SetProgram(&program); std::shared_ptr builder = paddle::dialect::ApiBuilder::Instance().GetBuilder(); paddle::dialect::FullOp op1 = builder->Build( std::vector{1}, 1.0, phi::DataType::FLOAT32, phi::CPUPlace()); paddle::dialect::Tanh_Op op2 = builder->Build(op1.out()); paddle::dialect::FullOp op3 = builder->Build( std::vector{1}, 2.0, phi::DataType::FLOAT32, phi::CPUPlace()); std::vector> stop_gradients{{false}}; std::vector> inputs{{op1.out()}}; std::vector> outputs{{op2.out()}}; std::vector> out_grads{{op3.out()}}; pir::OpInfo op2_info = ctx->GetRegisteredOpInfo("pd_op.tanh_"); auto tanh_vjp_interface_impl = op2_info.GetInterfaceImpl(); tanh_vjp_interface_impl->vjp_( op2.operation(), inputs, outputs, out_grads, stop_gradients); std::string tanh_out = "tanh_out"; std::string tanh_grad_out = "tanh_grad_out"; builder->Build(op2->result(0), tanh_out); builder->Build( GetOpFromProgram("pd_op.tanh_grad", program)->result(0), tanh_grad_out); auto kernel_program = pir::PdOpLowerToKernelPass(&program); auto place = phi::CPUPlace(); Scope scope; InterpreterCore test_core(place, {}, kernel_program->block(), &scope); test_core.SetSkipGcVars({tanh_out, tanh_grad_out}); test_core.Run({}); auto out_tensor = test_core.local_scope() == nullptr ? scope.FindVar(tanh_out)->Get() : test_core.local_scope()->FindVar(tanh_out)->Get(); auto grad_out_tensor = test_core.local_scope() == nullptr ? scope.FindVar(tanh_grad_out)->Get() : test_core.local_scope() ->FindVar(tanh_grad_out) ->Get(); ASSERT_NEAR(out_tensor.data()[0], 0.76159, 1e-5); ASSERT_NEAR(grad_out_tensor.data()[0], 0.83995, 1e-5); } TEST(VJP, MeanBackwardTest) { pir::IrContext* ctx = pir::IrContext::Instance(); ctx->GetOrRegisterDialect(); pir::Program program((ctx)); paddle::dialect::ApiBuilder::Instance().SetProgram(&program); std::shared_ptr builder = paddle::dialect::ApiBuilder::Instance().GetBuilder(); paddle::dialect::FullOp op1 = builder->Build( std::vector{2, 2}, 2.0, phi::DataType::FLOAT32, phi::CPUPlace()); paddle::dialect::MeanOp op2 = builder->Build(op1.out()); paddle::dialect::FullOp op3 = builder->Build( std::vector{}, 1.0, phi::DataType::FLOAT32, phi::CPUPlace()); std::vector> stop_gradients{{false}}; std::vector> inputs{{op1.out()}, {op2.operand_source(1)}}; std::vector> outputs{{op2.out()}}; std::vector> out_grads{{op3.out()}}; pir::OpInfo op2_info = ctx->GetRegisteredOpInfo("pd_op.mean"); auto mean_vjp_interface_impl = op2_info.GetInterfaceImpl(); mean_vjp_interface_impl->vjp_( op2.operation(), inputs, outputs, out_grads, stop_gradients); builder->Build(op2->result(0), "mean_out"); builder->Build( GetOpFromProgram("pd_op.mean_grad", program)->result(0), "mean_grad_out"); auto kernel_program = pir::PdOpLowerToKernelPass(&program); auto place = phi::CPUPlace(); Scope scope; ProgramDesc prog_desc; InterpreterCore test_core(place, {}, kernel_program->block(), &scope); test_core.SetSkipGcVars({"mean_out", "mean_grad_out"}); test_core.Run({}); auto out_tensor = test_core.local_scope() == nullptr ? scope.FindVar("mean_out")->Get() : test_core.local_scope() ->FindVar("mean_out") ->Get(); auto grad_out_tensor = test_core.local_scope() == nullptr ? scope.FindVar("mean_grad_out")->Get() : test_core.local_scope() ->FindVar("mean_grad_out") ->Get(); ASSERT_EQ(out_tensor.data()[0], 2.0); ASSERT_EQ(grad_out_tensor.data()[0], 0.25); ASSERT_EQ(grad_out_tensor.data()[1], 0.25); ASSERT_EQ(grad_out_tensor.data()[2], 0.25); ASSERT_EQ(grad_out_tensor.data()[3], 0.25); } TEST(VJP, ConcatBackwardTest) { pir::IrContext* ctx = pir::IrContext::Instance(); ctx->GetOrRegisterDialect(); pir::Program program((ctx)); paddle::dialect::ApiBuilder::Instance().SetProgram(&program); std::shared_ptr builder = paddle::dialect::ApiBuilder::Instance().GetBuilder(); paddle::dialect::FullOp op1 = builder->Build( std::vector{1, 2}, 2.0, phi::DataType::FLOAT32, phi::CPUPlace()); std::vector combine_input{{op1.out(), op1.out()}}; pir::CombineOp op2 = builder->Build(combine_input); paddle::dialect::ConcatOp op3 = builder->Build(op2.out(), 0); paddle::dialect::FullOp op4 = builder->Build( std::vector{2, 2}, 1.0, phi::DataType::FLOAT32, phi::CPUPlace()); std::vector> stop_gradients{{false, false}}; std::vector> inputs{{op1.out(), op1.out()}, {op3.axis()}}; std::vector> outputs{{op3.out()}}; std::vector> out_grads{{op4.out()}}; pir::OpInfo op2_info = ctx->GetRegisteredOpInfo("pd_op.concat"); auto concat_vjp_interface_impl = op2_info.GetInterfaceImpl(); concat_vjp_interface_impl->vjp_( op3.operation(), inputs, outputs, out_grads, stop_gradients); builder->Build(op3->result(0), "concat_out"); builder->Build( GetOpFromProgram("builtin.split", program)->result(0), "split_out_0"); builder->Build( GetOpFromProgram("builtin.split", program)->result(1), "split_out_1"); auto kernel_program = pir::PdOpLowerToKernelPass(&program); auto place = phi::CPUPlace(); Scope scope; ProgramDesc prog_desc; InterpreterCore test_core(place, {}, kernel_program->block(), &scope); test_core.SetSkipGcVars({"concat_out", "split_out_0", "split_out_1"}); test_core.Run({}); auto out_tensor = test_core.local_scope() == nullptr ? scope.FindVar("concat_out")->Get() : test_core.local_scope() ->FindVar("concat_out") ->Get(); auto grad_out_tensor_0 = test_core.local_scope() == nullptr ? scope.FindVar("split_out_0")->Get() : test_core.local_scope() ->FindVar("split_out_0") ->Get(); auto grad_out_tensor_1 = test_core.local_scope() == nullptr ? scope.FindVar("split_out_1")->Get() : test_core.local_scope() ->FindVar("split_out_1") ->Get(); ASSERT_EQ(out_tensor.data()[0], 2.0); ASSERT_EQ(grad_out_tensor_0.data()[0], 1.0); ASSERT_EQ(grad_out_tensor_0.data()[1], 1.0); ASSERT_EQ(grad_out_tensor_1.data()[0], 1.0); ASSERT_EQ(grad_out_tensor_1.data()[1], 1.0); } TEST(VJP, AddBackwardTest) { pir::IrContext* ctx = pir::IrContext::Instance(); ctx->GetOrRegisterDialect(); pir::Program program((ctx)); paddle::dialect::ApiBuilder::Instance().SetProgram(&program); std::shared_ptr builder = paddle::dialect::ApiBuilder::Instance().GetBuilder(); paddle::dialect::FullOp op1 = builder->Build( std::vector{1}, 2.0, phi::DataType::FLOAT32, phi::CPUPlace()); paddle::dialect::FullOp op2 = builder->Build( std::vector{1}, 2.0, phi::DataType::FLOAT32, phi::CPUPlace()); paddle::dialect::AddOp op3 = builder->Build(op1.out(), op2.out()); paddle::dialect::FullOp op4 = builder->Build( std::vector{1}, 1.0, phi::DataType::FLOAT32, phi::CPUPlace()); std::vector> stop_gradients{{false}, {false}}; std::vector> inputs{{op1.out()}, {op2.out()}}; std::vector> outputs{{op3.out()}}; std::vector> out_grads{{op4.out()}}; pir::OpInfo op3_info = ctx->GetRegisteredOpInfo("pd_op.add"); auto add_vjp_interface_impl = op3_info.GetInterfaceImpl(); add_vjp_interface_impl->vjp_( op3.operation(), inputs, outputs, out_grads, stop_gradients); builder->Build(op3->result(0), "add_out"); builder->Build( GetOpFromProgram("pd_op.add_grad", program)->result(0), "add_grad_out_0"); builder->Build( GetOpFromProgram("pd_op.add_grad", program)->result(1), "add_grad_out_1"); auto kernel_program = pir::PdOpLowerToKernelPass(&program); auto place = phi::CPUPlace(); Scope scope; ProgramDesc prog_desc; InterpreterCore test_core(place, {}, kernel_program->block(), &scope); test_core.SetSkipGcVars({"add_out", "add_grad_out_0", "add_grad_out_1"}); test_core.Run({}); auto out_tensor = test_core.local_scope() == nullptr ? scope.FindVar("add_out")->Get() : test_core.local_scope() ->FindVar("add_out") ->Get(); auto dx = test_core.local_scope() == nullptr ? scope.FindVar("add_grad_out_0")->Get() : test_core.local_scope() ->FindVar("add_grad_out_0") ->Get(); auto dy = test_core.local_scope() == nullptr ? scope.FindVar("add_grad_out_1")->Get() : test_core.local_scope() ->FindVar("add_grad_out_1") ->Get(); ASSERT_EQ(out_tensor.data()[0], 4.0); ASSERT_EQ(dx.data()[0], 1.0); ASSERT_EQ(dy.data()[0], 1.0); } TEST(VJP, Add_BackwardTest) { pir::IrContext* ctx = pir::IrContext::Instance(); ctx->GetOrRegisterDialect(); pir::Program program((ctx)); paddle::dialect::ApiBuilder::Instance().SetProgram(&program); std::shared_ptr builder = paddle::dialect::ApiBuilder::Instance().GetBuilder(); paddle::dialect::FullOp op1 = builder->Build( std::vector{1}, 2.0, phi::DataType::FLOAT32, phi::CPUPlace()); paddle::dialect::FullOp op2 = builder->Build( std::vector{1}, 2.0, phi::DataType::FLOAT32, phi::CPUPlace()); paddle::dialect::Add_Op op3 = builder->Build(op1.out(), op2.out()); paddle::dialect::FullOp op4 = builder->Build( std::vector{1}, 1.0, phi::DataType::FLOAT32, phi::CPUPlace()); std::vector> stop_gradients{{false}, {false}}; std::vector> inputs{{op1.out()}, {op2.out()}}; std::vector> outputs{{op3.out()}}; std::vector> out_grads{{op4.out()}}; pir::OpInfo op3_info = ctx->GetRegisteredOpInfo("pd_op.add_"); auto add_inplace_vjp_interface_impl = op3_info.GetInterfaceImpl(); add_inplace_vjp_interface_impl->vjp_( op3.operation(), inputs, outputs, out_grads, stop_gradients); builder->Build(op1->result(0), "full_op1_out"); builder->Build( GetOpFromProgram("pd_op.add_grad", program)->result(0), "add_grad_out_0"); builder->Build( GetOpFromProgram("pd_op.add_grad", program)->result(1), "add_grad_out_1"); auto kernel_program = pir::PdOpLowerToKernelPass(&program); auto place = phi::CPUPlace(); Scope scope; ProgramDesc prog_desc; InterpreterCore test_core(place, {}, kernel_program->block(), &scope); test_core.SetSkipGcVars({"full_op1_out", "add_grad_out_0", "add_grad_out_1"}); test_core.Run({}); auto out_tensor = test_core.local_scope() == nullptr ? scope.FindVar("full_op1_out")->Get() : test_core.local_scope() ->FindVar("full_op1_out") ->Get(); auto dx = test_core.local_scope() == nullptr ? scope.FindVar("add_grad_out_0")->Get() : test_core.local_scope() ->FindVar("add_grad_out_0") ->Get(); auto dy = test_core.local_scope() == nullptr ? scope.FindVar("add_grad_out_1")->Get() : test_core.local_scope() ->FindVar("add_grad_out_1") ->Get(); ASSERT_EQ(out_tensor.data()[0], 4.0); ASSERT_EQ(dx.data()[0], 1.0); ASSERT_EQ(dy.data()[0], 1.0); } TEST(VJP, SplitBackwardTest) { pir::IrContext* ctx = pir::IrContext::Instance(); ctx->GetOrRegisterDialect(); pir::Program program((ctx)); paddle::dialect::ApiBuilder::Instance().SetProgram(&program); std::shared_ptr builder = paddle::dialect::ApiBuilder::Instance().GetBuilder(); paddle::dialect::FullOp op1 = builder->Build( std::vector{2, 2}, 2.0, phi::DataType::FLOAT32, phi::CPUPlace()); paddle::dialect::SplitOp op2 = builder->Build( op1.out(), std::vector{1, 1}, 0); pir::SplitOp op3 = builder->Build(op2.out()); paddle::dialect::FullOp op4 = builder->Build( std::vector{1, 2}, 1.0, phi::DataType::FLOAT32, phi::CPUPlace()); std::vector> stop_gradients{{false}}; std::vector> inputs{ {op2.x()}, {op2.sections()}, {op2.axis()}}; std::vector> outputs(1); std::vector res; for (uint32_t i = 0; i < op3.outputs().size(); i++) { res.push_back(op3.outputs()[i]); } outputs[0] = res; std::vector> out_grads{{op3.result(0), op4.out()}}; pir::OpInfo op2_info = ctx->GetRegisteredOpInfo("pd_op.split"); auto concat_vjp_interface_impl = op2_info.GetInterfaceImpl(); concat_vjp_interface_impl->vjp_( op2.operation(), inputs, outputs, out_grads, stop_gradients); std::string split_out1 = "split_out1"; std::string split_out2 = "split_out2"; std::string concat_out = "concat_out"; builder->Build(op3->result(0), split_out1); builder->Build(op3->result(1), split_out2); builder->Build( GetOpFromProgram("pd_op.concat", program)->result(0), concat_out); auto kernel_program = pir::PdOpLowerToKernelPass(&program); auto place = phi::CPUPlace(); Scope scope; InterpreterCore test_core(place, {}, kernel_program->block(), &scope); test_core.SetSkipGcVars({split_out1, split_out2, concat_out}); test_core.Run({}); auto out_tensor_0 = test_core.local_scope() == nullptr ? scope.FindVar(split_out1)->Get() : test_core.local_scope() ->FindVar(split_out1) ->Get(); auto out_tensor_1 = test_core.local_scope() == nullptr ? scope.FindVar(split_out2)->Get() : test_core.local_scope() ->FindVar(split_out2) ->Get(); auto grad_out_tensor_0 = test_core.local_scope() == nullptr ? scope.FindVar(concat_out)->Get() : test_core.local_scope() ->FindVar(concat_out) ->Get(); ASSERT_EQ(out_tensor_0.data()[0], 2.0); ASSERT_EQ(out_tensor_0.data()[1], 2.0); ASSERT_EQ(out_tensor_1.data()[0], 2.0); ASSERT_EQ(out_tensor_1.data()[1], 2.0); ASSERT_EQ(grad_out_tensor_0.data()[0], 2.0); ASSERT_EQ(grad_out_tensor_0.data()[1], 2.0); ASSERT_EQ(grad_out_tensor_0.data()[2], 1.0); ASSERT_EQ(grad_out_tensor_0.data()[3], 1.0); } } // namespace framework } // namespace paddle