Files
2026-07-13 12:40:42 +08:00

333 lines
10 KiB
C++

// Copyright (c) 2022 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.
#pragma once
#include "paddle/common/macros.h"
#include "paddle/phi/kernels/funcs/eigen/common.h"
#include "paddle/phi/kernels/funcs/eigen/eigen_function.h"
namespace phi {
namespace funcs {
//////// Frobenius Norm Functor ///////
struct FrobeniusNormFunctor {
template <typename DeviceContext, typename X, typename Y, typename Dim>
void operator()(const DeviceContext& place, X* x, Y* y, const Dim& dim) {
y->device(place) = ((x->square()).sum(dim)).sqrt();
}
};
struct FrobeniusNormGradFunctor {
template <typename DeviceContext,
typename X,
typename Y,
typename DX,
typename DY,
typename Dim>
void operator()(const DeviceContext& place,
X* x,
Y* y,
DX* dx,
DY* dy,
const Dim& dim,
int size UNUSED) {
dx->device(place) = y->broadcast(dim);
dx->device(place) = *dx + dx->constant(1e-12f);
dx->device(place) = (*x / *dx) * (dy->broadcast(dim));
}
};
//////// Max Functor ///////
struct MaxFunctor {
template <typename DeviceContext, typename X, typename Y, typename Dim>
void operator()(const DeviceContext& place, X* x, Y* y, const Dim& dim) {
y->device(place) = x->template maximum<Dim, Eigen::PropagateNaN>(dim);
}
};
//////// Mean Functor ///////
struct MeanFunctor {
template <typename DeviceContext, typename X, typename Y, typename Dim>
void operator()(const DeviceContext& place, X* x, Y* y, const Dim& dim) {
y->device(place) = x->mean(dim);
}
};
//////// Prod Functor ///////
struct ProdFunctor {
template <typename DeviceContext, typename X, typename Y, typename Dim>
void operator()(const DeviceContext& place, X* x, Y* y, const Dim& dim) {
y->device(place) = x->prod(dim);
}
};
//////// Sum Functor ///////
struct SumFunctor {
template <typename DeviceContext, typename X, typename Y, typename Dim>
void operator()(const DeviceContext& place, X* x, Y* y, const Dim& dim) {
y->device(place) = x->sum(dim);
}
};
//////// Min Functor ///////
struct MinFunctor {
template <typename DeviceContext, typename X, typename Y, typename Dim>
void operator()(const DeviceContext& place, X* x, Y* y, const Dim& dim) {
y->device(place) = x->template minimum<Dim, Eigen::PropagateNaN>(dim);
}
};
//////// All Functor ///////
template <typename T>
struct AllFunctor {
template <typename DeviceContext, typename X, typename Y, typename Dim>
void operator()(const DeviceContext& place, X* x, Y* y, const Dim& dim) {
y->device(place) = x->all(dim);
}
};
template <typename T>
struct AllFunctor<std::complex<T>> {
template <typename DeviceContext, typename X, typename Y, typename Dim>
void operator()(const DeviceContext& place, X* x, Y* y, const Dim& dim) {
auto to_bool = [](const std::complex<T>& v) {
return v.real() != 0 || v.imag() != 0;
};
y->device(place) = x->unaryExpr(to_bool).all(dim);
}
};
//////// Any Functor ///////
template <typename T>
struct AnyFunctor {
template <typename DeviceContext, typename X, typename Y, typename Dim>
void operator()(const DeviceContext& place, X* x, Y* y, const Dim& dim) {
y->device(place) = x->any(dim);
}
};
template <typename T>
struct AnyFunctor<std::complex<T>> {
template <typename DeviceContext, typename X, typename Y, typename Dim>
void operator()(const DeviceContext& place, X* x, Y* y, const Dim& dim) {
auto to_bool = [](const std::complex<T>& v) {
return v.real() != 0 || v.imag() != 0;
};
y->device(place) = x->unaryExpr(to_bool).all(dim);
}
};
struct MeanGradFunctor {
template <typename DeviceContext,
typename X,
typename Y,
typename DX,
typename DY,
typename Dim>
void operator()(const DeviceContext& place,
X* x UNUSED,
Y* y UNUSED,
DX* dx,
DY* dy,
const Dim& dim,
int size) {
dx->device(place) = dy->broadcast(dim) / dx->constant(size);
}
};
struct SumGradFunctor {
template <typename DeviceContext,
typename X,
typename Y,
typename DX,
typename DY,
typename Dim>
void operator()(const DeviceContext& place,
X* x UNUSED,
Y* y UNUSED,
DX* dx,
DY* dy,
const Dim& dim,
int size UNUSED) {
dx->device(place) = dy->broadcast(dim);
}
};
struct ProdGradFunctor {
template <typename DeviceContext,
typename X,
typename Y,
typename DX,
typename DY,
typename Dim>
void operator()(const DeviceContext& place,
X* x,
Y* y,
DX* dx,
DY* dy,
const Dim& dim,
int size UNUSED) {
dx->device(place) = dy->broadcast(dim) * y->broadcast(dim) * x->inverse();
}
};
struct MaxOrMinGradFunctor {
template <typename DeviceContext,
typename X,
typename Y,
typename DX,
typename DY,
typename Dim>
void operator()(const DeviceContext& place,
X* x,
Y* y,
DX* dx,
DY* dy,
const Dim& dim,
int size UNUSED) {
auto equals = (*x) == y->broadcast(dim);
auto ones = dx->constant(1);
auto zeros = dx->constant(0);
// If there are multiple minimum or maximum elements, the subgradient of
// each is the set [0, 1], and we pass gradient to all of them here.
dx->device(place) = dy->broadcast(dim).reshape(x->dimensions()) *
equals.select(ones, zeros);
}
};
#define HANDLE_AXIS_DIM(BROADCAST_DIM, AXIS_DIM) \
if (broadcast_dim_size == BROADCAST_DIM && rank == AXIS_DIM) { \
AMaxOrAMinAxisIsListGradFunctor<DeviceContext, \
X, \
Y, \
DX, \
DY, \
Dim, \
BROADCAST_DIM, \
AXIS_DIM>( \
place, x, y, dx, dy, dim, axis_dim); \
}
template <typename DeviceContext,
typename X,
typename Y,
typename DX,
typename DY,
typename Dim,
int R,
int D>
void AMaxOrAMinAxisIsListGradFunctor(const DeviceContext& place,
X* x,
Y* y,
DX* dx,
DY* dy,
const Dim& dim,
const std::vector<int>& axis_dim) {
// R is x->dimensions().size();
// D is axis_dim->dimensions().size();
auto axis = Eigen::array<int, D>();
auto reshape_x = Eigen::array<int, R>();
auto reshape_y = Eigen::array<int, R>();
for (int i = 0; i < D; i++) axis[i] = axis_dim[i];
for (int i = 0; i < R; i++) {
reshape_x[i] = x->dimensions()[i];
reshape_y[i] = y->dimensions()[i];
}
auto equals = (*x) == y->broadcast(dim);
auto ones = dx->constant(1);
auto zeros = dx->constant(0);
auto mask = equals.select(ones, zeros);
dx->device(place) =
dy->broadcast(dim) * mask /
mask.reshape(reshape_x).sum(axis).reshape(reshape_y).broadcast(dim);
}
struct AMaxOrAMinGradFunctor {
template <typename DeviceContext,
typename X,
typename Y,
typename DX,
typename DY,
typename Dim>
void operator()(const DeviceContext& place,
X* x,
Y* y,
DX* dx,
DY* dy,
const Dim& dim,
int size) {
auto equals = (*x) == y->broadcast(dim);
auto ones = dx->constant(1);
auto zeros = dx->constant(0);
auto mask = equals.select(ones, zeros);
// If there are multiple minimum or maximum elements,
// we evenly distribute gradient between these equal values
size_t x_numel = 1;
for (size_t i = 0; i < x->dimensions().size(); i++)
x_numel *= x->dimensions()[i];
// reduce_all
if (size == static_cast<int>(x_numel)) {
auto equal_number = mask.sum()
.reshape(Eigen::array<int, 1>({{1}}))
.broadcast(Eigen::array<int, 1>({size}));
dx->device(place) =
dy->broadcast(dim).reshape(x->dimensions()) * mask / equal_number;
return;
}
// compute forward reduce axis_dim by dim (which is broadcast_dim)
std::vector<int> axis_dim;
int broadcast_dim_size = static_cast<int>(dim.size());
for (int i = 0; i < broadcast_dim_size; i++) {
if (dim[i] > 1) {
axis_dim.push_back(i);
}
}
int rank = static_cast<int>(axis_dim.size());
// axis is a int element
if (rank == 1) {
auto axis = Eigen::array<int, 1>({axis_dim[0]});
dx->device(place) =
dy->broadcast(dim) * mask /
mask.sum(axis).reshape(dy->dimensions()).broadcast(dim);
return;
}
if (rank == 0) {
dx->device(place) = dy->broadcast(dim) * mask;
return;
}
// axis is list, HANDLE_AXIS_DIM(broadcast_dim_size, rank)
HANDLE_AXIS_DIM(3, 2);
HANDLE_AXIS_DIM(4, 2);
HANDLE_AXIS_DIM(4, 3);
// comments for accelerating compiling temporarily.
// HANDLE_AXIS_DIM(5, 2);
// HANDLE_AXIS_DIM(5, 3);
// HANDLE_AXIS_DIM(5, 4);
// HANDLE_AXIS_DIM(6, 2);
// HANDLE_AXIS_DIM(6, 3);
// HANDLE_AXIS_DIM(6, 4);
// HANDLE_AXIS_DIM(6, 5);
}
};
} // namespace funcs
} // namespace phi