Files
paddlepaddle--paddle/paddle/phi/kernels/funcs/math/tree2col.cc
T
2026-07-13 12:40:42 +08:00

200 lines
7.0 KiB
C++

// Copyright (c) 2024 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 "paddle/phi/kernels/funcs/math/tree2col.h"
#include <deque>
#include <stack>
namespace phi {
namespace math {
std::vector<TreeNode> Tree2ColUtil::construct_patch(
size_t root, int max_depth, const std::vector<std::vector<int>> &tr) {
std::stack<TreeNode, std::deque<TreeNode>> stack;
std::unordered_map<int, bool> visited;
std::vector<TreeNode> patch;
stack.emplace(root, 1, 1, 0);
patch.emplace_back(root, 1, 1, 0);
visited[static_cast<int>(root)] = true;
while (!stack.empty()) {
TreeNode &u = stack.top();
bool end = true;
size_t node = u.get_node(), sz = tr[node].size();
visited[static_cast<int>(node)] = true;
for (size_t i = 0; i < sz; i++) {
size_t v = tr[node][i];
if (!visited[static_cast<int>(v)] &&
static_cast<int>(u.get_depth()) + 1 < max_depth) {
visited[static_cast<int>(v)] = true;
stack.emplace(v, i, sz, u.get_depth() + 1);
patch.emplace_back(v, i + 1, sz, u.get_depth() + 1);
end = false;
}
}
if (end) {
stack.pop();
}
}
return patch;
}
void Tree2ColUtil::construct_tree(const DenseTensor &EdgeSet,
std::vector<std::vector<int>> *tr,
size_t *node_count) {
const auto &edge_set_dims = EdgeSet.dims();
PADDLE_ENFORCE_EQ(edge_set_dims[1],
2,
common::errors::InvalidArgument(
"The second dimension of the EdgeSet shall be 2, but "
"got %ld != 2. Please check the input value.",
edge_set_dims[1]));
int64_t edge_count = EdgeSet.numel();
const int *edge_data = EdgeSet.data<int>();
for (int64_t i = 0; i < edge_count; i += 2) {
int u = edge_data[i], v = edge_data[i + 1];
if (u != 0 && v != 0) (*node_count)++;
}
(*node_count)++;
tr->resize(static_cast<size_t>(*node_count + 1));
for (int64_t i = 0; i < edge_count; i += 2) {
int u = edge_data[i], v = edge_data[i + 1];
if (u != 0 && v != 0) {
tr->at(u).push_back(v);
} else {
break;
}
}
}
template <typename T>
class Tree2ColFunctor<CPUContext, T> {
public:
void operator()(const CPUContext &dev_ctx,
const DenseTensor &EdgeSet,
const DenseTensor &node_features,
DenseTensor *patch,
int max_depth) {
std::vector<std::vector<int>> tr;
const auto &feature_dims = node_features.dims();
funcs::SetConstant<CPUContext, T> constant;
int64_t feature_size = feature_dims[1];
size_t patch_elem_size = 3 * static_cast<size_t>(feature_size);
size_t node_count = 0, patch_count = 0, patch_size = 0;
Tree2ColUtil::construct_tree(EdgeSet, &tr, &node_count);
std::vector<std::vector<TreeNode>> processing_list;
for (size_t u = 1; u <= node_count; u++) {
std::vector<TreeNode> temp_patch =
Tree2ColUtil::construct_patch(u, max_depth, tr);
if (!temp_patch.empty()) {
processing_list.emplace_back(temp_patch);
}
}
patch_size = processing_list.size();
patch->Resize({static_cast<int64_t>(patch_size),
static_cast<int64_t>(patch_elem_size)});
T *patch_data = dev_ctx.template Alloc<T>(patch);
constant(dev_ctx, patch, 0);
const T *features = node_features.data<T>();
for (auto &patch_item : processing_list) {
size_t pointer_base = patch_count * patch_elem_size;
for (auto &v : patch_item) {
T eta_l = v.eta_l<T>(max_depth), eta_r = v.eta_r<T>(max_depth),
eta_t = v.eta_t<T>(max_depth);
size_t id = v.get_node() - 1;
for (int i = 0; i < feature_size; i++) {
patch_data[pointer_base + i * 3] +=
eta_l * features[id * feature_size + i];
patch_data[pointer_base + i * 3 + 1] +=
eta_r * features[id * feature_size + i];
patch_data[pointer_base + i * 3 + 2] +=
eta_t * features[id * feature_size + i];
}
}
patch_count++;
}
patch->Resize({static_cast<int64_t>(patch_count),
static_cast<int64_t>(patch_elem_size)});
}
};
template <typename T>
class Col2TreeFunctor<CPUContext, T> {
public:
void operator()(const CPUContext &dev_ctx,
const DenseTensor &EdgeSet,
const DenseTensor &out_grad,
DenseTensor *in_grad,
int max_depth) {
std::vector<std::vector<int>> tr;
const auto &output_dims = out_grad.dims();
funcs::SetConstant<CPUContext, T> constant;
int64_t output_size = output_dims[1];
size_t grad_elem_size = 3 * static_cast<size_t>(output_size);
size_t node_count = 0, grad_count = 0;
Tree2ColUtil::construct_tree(EdgeSet, &tr, &node_count);
std::vector<std::vector<TreeNode>> processing_list;
std::vector<std::vector<TreeNode>> grad_list;
grad_list.resize(node_count);
for (size_t u = 1; u <= node_count; u++) {
std::vector<TreeNode> tmp =
Tree2ColUtil::construct_patch(u, max_depth, tr);
if (!tmp.empty()) {
processing_list.push_back(tmp);
}
}
for (size_t patch_id = 0; patch_id < processing_list.size(); patch_id++) {
for (auto v : processing_list[patch_id]) {
grad_list[v.get_node() - 1].push_back(v.change_node(patch_id + 1));
}
}
in_grad->Resize({static_cast<int64_t>(node_count),
static_cast<int64_t>(grad_elem_size)});
T *grad_data = dev_ctx.template Alloc<T>(in_grad);
constant(dev_ctx, in_grad, 0);
const T *out_g = out_grad.data<T>();
for (auto &patch_item : grad_list) {
size_t pointer_base = grad_count * grad_elem_size;
for (auto &v : patch_item) {
T eta_l = v.eta_l<T>(max_depth), eta_r = v.eta_r<T>(max_depth),
eta_t = v.eta_t<T>(max_depth);
size_t id = v.get_node() - 1;
for (int i = 0; i < output_size; i++) {
grad_data[pointer_base + i * 3] +=
eta_l * out_g[id * output_size + i];
grad_data[pointer_base + i * 3 + 1] +=
eta_r * out_g[id * output_size + i];
grad_data[pointer_base + i * 3 + 2] +=
eta_t * out_g[id * output_size + i];
}
}
grad_count++;
}
}
};
template class Tree2ColFunctor<CPUContext, float>;
template class Tree2ColFunctor<CPUContext, double>;
template class Col2TreeFunctor<CPUContext, float>;
template class Col2TreeFunctor<CPUContext, double>;
} // namespace math
} // namespace phi