chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
add_subdirectory(profiler)
|
||||
add_subdirectory(lib)
|
||||
add_subdirectory(include/compat)
|
||||
@@ -0,0 +1,40 @@
|
||||
/* Copyright (c) 2021 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
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX // msvc max/min macro conflict with std::min/max
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// new phi apis
|
||||
#include "paddle/phi/api/include/api.h"
|
||||
#include "paddle/phi/api/include/context_pool.h"
|
||||
#include "paddle/phi/api/include/fused_api.h"
|
||||
#include "paddle/phi/api/include/sparse_api.h"
|
||||
#include "paddle/phi/api/include/tensor.h"
|
||||
#include "paddle/phi/api/include/tensor_utils.h"
|
||||
|
||||
// phi common headers
|
||||
#include "paddle/phi/common/backend.h"
|
||||
#include "paddle/phi/common/data_type.h"
|
||||
#include "paddle/phi/common/int_array.h"
|
||||
#include "paddle/phi/common/scalar.h"
|
||||
|
||||
// original custom op headers
|
||||
#include "paddle/phi/api/ext/dispatch.h"
|
||||
#include "paddle/phi/api/ext/op_meta_info.h"
|
||||
#include "paddle/phi/api/ext/tensor_compat.h"
|
||||
@@ -0,0 +1 @@
|
||||
The code files in this directory(paddle/phi/api/backward) are auto-generated when building PaddlePaddle.
|
||||
@@ -0,0 +1,70 @@
|
||||
/* Copyright (c) 2021 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/phi/core/visit_type.h"
|
||||
|
||||
namespace paddle {
|
||||
|
||||
// Note: Keep this file only for compatibility with custom operators
|
||||
|
||||
///////// Floating Dispatch Marco ///////////
|
||||
|
||||
#define PD_DISPATCH_FLOATING_TYPES(TYPE, NAME, ...) \
|
||||
PD_VISIT_FLOATING_TYPES(TYPE, NAME, __VA_ARGS__)
|
||||
|
||||
#define PD_DISPATCH_FLOATING_AND_HALF_TYPES(TYPE, NAME, ...) \
|
||||
PD_VISIT_FLOATING_AND_HALF_TYPES(TYPE, NAME, __VA_ARGS__)
|
||||
|
||||
///////// Integral Dispatch Marco ///////////
|
||||
|
||||
#define PD_DISPATCH_INTEGRAL_TYPES(TYPE, NAME, ...) \
|
||||
PD_VISIT_INTEGRAL_TYPES(TYPE, NAME, __VA_ARGS__)
|
||||
|
||||
///////// Complex Dispatch Marco ///////////
|
||||
|
||||
#define PD_DISPATCH_COMPLEX_TYPES(TYPE, NAME, ...) \
|
||||
PD_VISIT_COMPLEX_TYPES(TYPE, NAME, __VA_ARGS__)
|
||||
|
||||
///////// Floating and Integral Dispatch Marco ///////////
|
||||
|
||||
#define PD_DISPATCH_FLOATING_AND_INTEGRAL_TYPES(TYPE, NAME, ...) \
|
||||
PD_VISIT_FLOATING_AND_INTEGRAL_TYPES(TYPE, NAME, __VA_ARGS__)
|
||||
|
||||
///////// Floating and Complex Dispatch Marco ///////////
|
||||
|
||||
#define PD_DISPATCH_FLOATING_AND_COMPLEX_TYPES(TYPE, NAME, ...) \
|
||||
PD_VISIT_FLOATING_AND_COMPLEX_TYPES(TYPE, NAME, __VA_ARGS__)
|
||||
|
||||
///////// Floating and Complex and other type Dispatch Marco ///////////
|
||||
|
||||
#define PD_DISPATCH_FLOATING_AND_COMPLEX_AND_1_TYPE( \
|
||||
SPECIFIED_TYPE, TYPE, NAME, ...) \
|
||||
PD_VISIT_FLOATING_AND_COMPLEX_AND_1_TYPE( \
|
||||
SPECIFIED_TYPE, TYPE, NAME, __VA_ARGS__)
|
||||
|
||||
///////// Floating and Complex and 2 other type Dispatch Marco ///////////
|
||||
|
||||
#define PD_DISPATCH_FLOATING_AND_COMPLEX_AND_2_TYPES( \
|
||||
SPECIFIED_TYPE1, SPECIFIED_TYPE2, TYPE, NAME, ...) \
|
||||
PD_VISIT_FLOATING_AND_COMPLEX_AND_2_TYPES( \
|
||||
SPECIFIED_TYPE1, SPECIFIED_TYPE2, TYPE, NAME, __VA_ARGS__)
|
||||
|
||||
///////// Floating, Integral and Complex Dispatch Marco ///////////
|
||||
|
||||
#define PD_DISPATCH_FLOATING_AND_INTEGRAL_AND_COMPLEX_TYPES(TYPE, NAME, ...) \
|
||||
PD_VISIT_FLOATING_AND_INTEGRAL_AND_COMPLEX_TYPES(TYPE, NAME, __VA_ARGS__)
|
||||
|
||||
} // namespace paddle
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) 2025 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/common/data_type.h"
|
||||
#include "paddle/phi/core/ddim.h"
|
||||
|
||||
namespace phi {
|
||||
class PADDLE_API NativeMetaTensor {
|
||||
public:
|
||||
NativeMetaTensor() = default;
|
||||
NativeMetaTensor(phi::DataType dtype, phi::DDim dims)
|
||||
: dims_(dims), dtype_(dtype) {}
|
||||
DDim dims() const;
|
||||
DataType dtype() const;
|
||||
void set_dims(const DDim& dims);
|
||||
void set_dtype(DataType dtype);
|
||||
|
||||
private:
|
||||
phi::DDim dims_;
|
||||
phi::DataType dtype_{phi::DataType::FLOAT32};
|
||||
};
|
||||
} // namespace phi
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,165 @@
|
||||
/* 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. */
|
||||
|
||||
#pragma once
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_meta_tensor.h"
|
||||
#include "paddle/phi/core/distributed/type_defs.h"
|
||||
|
||||
namespace paddle {
|
||||
|
||||
using CustomSpmdInferTensorArg =
|
||||
paddle::variant<phi::distributed::DistMetaTensor,
|
||||
std::vector<phi::distributed::DistMetaTensor>>;
|
||||
|
||||
using CustomSpmdInferAttrArg = paddle::any;
|
||||
template <typename T>
|
||||
struct SpmdInferHelperTypeEnd {};
|
||||
|
||||
#define PD_INFER_SPMD_CHECK_INPUTS_SIZE_GT(inputs, size_bound) \
|
||||
PADDLE_ENFORCE_GT(inputs.size(), \
|
||||
size_bound, \
|
||||
common::errors::PreconditionNotMet( \
|
||||
"The size of %s must be greater than %d, but got %d.", \
|
||||
#inputs, \
|
||||
size_bound, \
|
||||
inputs.size()))
|
||||
|
||||
#define PD_INFER_SPMD_CHECK_INPUTS_SIZE_EQ(inputs, size_bound) \
|
||||
PADDLE_ENFORCE_EQ(inputs.size(), \
|
||||
size_bound, \
|
||||
common::errors::PreconditionNotMet( \
|
||||
"The size of %s must be equal to %d, but got %d.", \
|
||||
#inputs, \
|
||||
size_bound, \
|
||||
inputs.size()))
|
||||
|
||||
#define PD_SPECIALIZE_SpmdInferHelper_FOR_AttrType(attr_type) \
|
||||
template <typename... Tail> \
|
||||
struct SpmdInferHelper<attr_type, Tail...> { \
|
||||
template <int in_idx, int attr_idx, typename... PreviousArgs> \
|
||||
static phi::distributed::SpmdInfo InferSpmd( \
|
||||
const std::vector<CustomSpmdInferTensorArg>& inputs, \
|
||||
const std::vector<CustomSpmdInferAttrArg>& attrs, \
|
||||
const PreviousArgs&... pargs) { \
|
||||
try { \
|
||||
PD_INFER_SPMD_CHECK_INPUTS_SIZE_GT(attrs, attr_idx); \
|
||||
attr_type arg = paddle::any_cast<attr_type>(attrs[attr_idx]); \
|
||||
return SpmdInferHelper<Tail...>::template InferSpmd<in_idx, \
|
||||
attr_idx + 1>( \
|
||||
inputs, attrs, pargs..., arg); \
|
||||
} catch (paddle::bad_any_cast&) { \
|
||||
PD_THROW( \
|
||||
"Attribute cast error in custom operator SpmdInferFunc " \
|
||||
"function. " \
|
||||
"Expected " #attr_type \
|
||||
" value. SpmdInferFunc's attribute list must be exactly " \
|
||||
"same " \
|
||||
"as " \
|
||||
"Forward " \
|
||||
"KernelFn's attribute list except std::vector<int64_t> " \
|
||||
"attribute."); \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
template <typename F, F f>
|
||||
struct SpmdInferImpl;
|
||||
|
||||
template <typename... Args, phi::distributed::SpmdInfo (*impl_fn)(Args...)>
|
||||
struct SpmdInferImpl<phi::distributed::SpmdInfo (*)(Args...), impl_fn> {
|
||||
static phi::distributed::SpmdInfo InferSpmd(
|
||||
const std::vector<CustomSpmdInferTensorArg>& inputs,
|
||||
const std::vector<CustomSpmdInferAttrArg>& attrs) {
|
||||
return SpmdInferHelper<Args..., SpmdInferHelperTypeEnd<int>>::
|
||||
template InferSpmd<0, 0>(inputs, attrs);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename... RemainingArgs>
|
||||
struct SpmdInferHelper;
|
||||
|
||||
// Handle args for general tensor input case
|
||||
template <typename... Tail>
|
||||
struct SpmdInferHelper<const phi::distributed::DistMetaTensor&, Tail...> {
|
||||
template <int in_idx, int attr_idx, typename... PreviousArgs>
|
||||
static phi::distributed::SpmdInfo InferSpmd(
|
||||
const std::vector<CustomSpmdInferTensorArg>& inputs,
|
||||
const std::vector<CustomSpmdInferAttrArg>& attrs,
|
||||
PreviousArgs&... pargs) {
|
||||
static_assert(attr_idx == 0, "attributes must come after tensor inputs");
|
||||
PD_INFER_SPMD_CHECK_INPUTS_SIZE_GT(inputs, in_idx);
|
||||
auto& arg =
|
||||
PADDLE_GET_CONST(phi::distributed::DistMetaTensor, inputs[in_idx]);
|
||||
return SpmdInferHelper<Tail...>::template InferSpmd<in_idx + 1, attr_idx>(
|
||||
inputs, attrs, pargs..., arg);
|
||||
}
|
||||
};
|
||||
|
||||
// Handle args for vector of Tensor input case
|
||||
template <typename... Tail>
|
||||
struct SpmdInferHelper<const std::vector<phi::distributed::DistMetaTensor>&,
|
||||
Tail...> {
|
||||
template <int in_idx, int attr_idx, typename... PreviousArgs>
|
||||
static phi::distributed::SpmdInfo InferSpmd(
|
||||
const std::vector<CustomSpmdInferTensorArg>& inputs,
|
||||
const std::vector<CustomSpmdInferAttrArg>& attrs,
|
||||
PreviousArgs&... pargs) {
|
||||
static_assert(attr_idx == 0, "attributes must come after tensor inputs");
|
||||
PD_INFER_SPMD_CHECK_INPUTS_SIZE_GT(inputs, in_idx);
|
||||
auto& arg = PADDLE_GET_CONST(
|
||||
std::vector<phi::distributed::DistMetaTensor>, inputs[in_idx]);
|
||||
return SpmdInferHelper<Tail...>::template InferSpmd<in_idx + 1, attr_idx>(
|
||||
inputs, attrs, pargs..., arg);
|
||||
}
|
||||
};
|
||||
|
||||
PD_SPECIALIZE_SpmdInferHelper_FOR_AttrType(bool);
|
||||
PD_SPECIALIZE_SpmdInferHelper_FOR_AttrType(int);
|
||||
PD_SPECIALIZE_SpmdInferHelper_FOR_AttrType(float);
|
||||
PD_SPECIALIZE_SpmdInferHelper_FOR_AttrType(int64_t);
|
||||
PD_SPECIALIZE_SpmdInferHelper_FOR_AttrType(const std::string&);
|
||||
PD_SPECIALIZE_SpmdInferHelper_FOR_AttrType(const std::vector<int>&);
|
||||
PD_SPECIALIZE_SpmdInferHelper_FOR_AttrType(const std::vector<float>&);
|
||||
PD_SPECIALIZE_SpmdInferHelper_FOR_AttrType(const std::vector<std::string>&);
|
||||
PD_SPECIALIZE_SpmdInferHelper_FOR_AttrType(const std::vector<int64_t>&);
|
||||
PD_SPECIALIZE_SpmdInferHelper_FOR_AttrType(const bool&);
|
||||
PD_SPECIALIZE_SpmdInferHelper_FOR_AttrType(const int&);
|
||||
PD_SPECIALIZE_SpmdInferHelper_FOR_AttrType(const float&);
|
||||
PD_SPECIALIZE_SpmdInferHelper_FOR_AttrType(const int64_t&);
|
||||
|
||||
PD_SPECIALIZE_SpmdInferHelper_FOR_AttrType(std::string);
|
||||
PD_SPECIALIZE_SpmdInferHelper_FOR_AttrType(std::vector<int>);
|
||||
PD_SPECIALIZE_SpmdInferHelper_FOR_AttrType(std::vector<float>);
|
||||
PD_SPECIALIZE_SpmdInferHelper_FOR_AttrType(std::vector<std::string>);
|
||||
PD_SPECIALIZE_SpmdInferHelper_FOR_AttrType(const std::vector<int64_t>);
|
||||
|
||||
// end: base template
|
||||
template <typename T>
|
||||
struct SpmdInferHelper<SpmdInferHelperTypeEnd<T>> {
|
||||
template <int in_idx, int attr_idx, typename... PreviousArgs>
|
||||
static phi::distributed::SpmdInfo InferSpmd(
|
||||
const std::vector<CustomSpmdInferTensorArg>& inputs,
|
||||
const std::vector<CustomSpmdInferAttrArg>& attrs,
|
||||
PreviousArgs&... pargs) {
|
||||
PD_INFER_SPMD_CHECK_INPUTS_SIZE_EQ(inputs, in_idx);
|
||||
PD_INFER_SPMD_CHECK_INPUTS_SIZE_EQ(attrs, attr_idx);
|
||||
return impl_fn(pargs...);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
#define PD_INFER_SPMD_RULE(...) \
|
||||
::paddle::SpmdInferImpl<decltype(&__VA_ARGS__), &__VA_ARGS__>::InferSpmd
|
||||
|
||||
} // namespace paddle
|
||||
@@ -0,0 +1,163 @@
|
||||
/* Copyright (c) 2021 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/phi/api/include/api.h"
|
||||
#include "paddle/phi/api/include/tensor.h"
|
||||
|
||||
// Note(chenweihang): In order to be compatible with the original custom
|
||||
// operator Tensor interface, only available to external users, the file
|
||||
// cannot be included in paddle
|
||||
|
||||
namespace paddle {
|
||||
// using several Tensor initialize functions in paddle namespace
|
||||
using experimental::abs;
|
||||
using experimental::acos;
|
||||
using experimental::acosh;
|
||||
using experimental::add;
|
||||
using experimental::addmm;
|
||||
using experimental::allclose;
|
||||
using experimental::argsort;
|
||||
using experimental::asin;
|
||||
using experimental::asinh;
|
||||
using experimental::atan;
|
||||
using experimental::atan2;
|
||||
using experimental::atanh;
|
||||
using experimental::bernoulli;
|
||||
using experimental::ceil;
|
||||
using experimental::cholesky;
|
||||
using experimental::cholesky_solve;
|
||||
using experimental::clip;
|
||||
using experimental::concat;
|
||||
using experimental::conj;
|
||||
using experimental::cos;
|
||||
using experimental::cosh;
|
||||
using experimental::cross;
|
||||
using experimental::det;
|
||||
using experimental::diag;
|
||||
using experimental::diagonal;
|
||||
using experimental::digamma;
|
||||
using experimental::dist;
|
||||
using experimental::divide;
|
||||
using experimental::dot;
|
||||
using experimental::elu;
|
||||
using experimental::empty;
|
||||
using experimental::empty_like;
|
||||
using experimental::equal;
|
||||
using experimental::equal_all;
|
||||
using experimental::erf;
|
||||
using experimental::erfinv;
|
||||
using experimental::exp;
|
||||
using experimental::expand;
|
||||
using experimental::expm1;
|
||||
using experimental::flatten;
|
||||
using experimental::flip;
|
||||
using experimental::floor;
|
||||
using experimental::floor_divide;
|
||||
using experimental::fmax;
|
||||
using experimental::fmin;
|
||||
using experimental::frame;
|
||||
using experimental::full;
|
||||
using experimental::gather;
|
||||
using experimental::gather_nd;
|
||||
using experimental::gelu;
|
||||
using experimental::greater_equal;
|
||||
using experimental::greater_than;
|
||||
using experimental::gumbel_softmax;
|
||||
using experimental::hardswish;
|
||||
using experimental::hardtanh;
|
||||
using experimental::imag;
|
||||
using experimental::increment;
|
||||
using experimental::index_sample;
|
||||
using experimental::is_empty;
|
||||
using experimental::isclose;
|
||||
using experimental::isfinite;
|
||||
using experimental::isinf;
|
||||
using experimental::isnan;
|
||||
using experimental::kron;
|
||||
using experimental::kthvalue;
|
||||
using experimental::label_smooth;
|
||||
using experimental::lerp;
|
||||
using experimental::less_equal;
|
||||
using experimental::less_than;
|
||||
using experimental::lgamma;
|
||||
using experimental::log;
|
||||
using experimental::log10;
|
||||
using experimental::log1p;
|
||||
using experimental::log2;
|
||||
using experimental::logit;
|
||||
using experimental::masked_select;
|
||||
using experimental::matmul;
|
||||
using experimental::matrix_power;
|
||||
using experimental::maximum;
|
||||
using experimental::maxout;
|
||||
using experimental::meshgrid;
|
||||
using experimental::minimum;
|
||||
using experimental::mode;
|
||||
using experimental::multi_dot;
|
||||
using experimental::multinomial;
|
||||
using experimental::multiply;
|
||||
using experimental::mv;
|
||||
using experimental::nll_loss;
|
||||
using experimental::not_equal;
|
||||
using experimental::npu_identity;
|
||||
using experimental::one_hot;
|
||||
using experimental::ones;
|
||||
using experimental::overlap_add;
|
||||
using experimental::pixel_shuffle;
|
||||
using experimental::poisson;
|
||||
using experimental::put_along_axis;
|
||||
using experimental::qr;
|
||||
using experimental::real;
|
||||
using experimental::reciprocal;
|
||||
using experimental::relu;
|
||||
using experimental::relu6;
|
||||
using experimental::remainder;
|
||||
using experimental::reshape;
|
||||
using experimental::roll;
|
||||
using experimental::round;
|
||||
using experimental::rsqrt;
|
||||
using experimental::scatter;
|
||||
using experimental::scatter_nd_add;
|
||||
using experimental::selu;
|
||||
using experimental::send_u_recv;
|
||||
using experimental::send_ue_recv;
|
||||
using experimental::send_uv;
|
||||
using experimental::sigmoid;
|
||||
using experimental::sign;
|
||||
using experimental::silu;
|
||||
using experimental::sin;
|
||||
using experimental::sinh;
|
||||
using experimental::split;
|
||||
using experimental::sqrt;
|
||||
using experimental::square;
|
||||
using experimental::stack;
|
||||
using experimental::standard_gamma;
|
||||
using experimental::strided_slice;
|
||||
using experimental::subtract;
|
||||
using experimental::swish;
|
||||
using experimental::tanh;
|
||||
using experimental::thresholded_relu;
|
||||
using experimental::tile;
|
||||
using experimental::trace;
|
||||
using experimental::triangular_solve;
|
||||
using experimental::tril;
|
||||
using experimental::unbind;
|
||||
using experimental::unique;
|
||||
using experimental::unsqueeze;
|
||||
using experimental::where;
|
||||
using experimental::zeros;
|
||||
|
||||
} // namespace paddle
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,781 @@
|
||||
# Copyright (c) 2021 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.
|
||||
import argparse
|
||||
import re
|
||||
|
||||
import yaml
|
||||
from api_base import PREFIX_TENSOR_NAME, BaseAPI, IsUsePredefinedOut
|
||||
|
||||
backward_api_black_list = [
|
||||
"scale_grad", # tensor = scale is not implemented in api_custom_impl.cc
|
||||
]
|
||||
|
||||
inplace_out_type_map = {
|
||||
"Tensor": "Tensor&",
|
||||
"std::vector<Tensor>": "std::vector<Tensor>&",
|
||||
}
|
||||
|
||||
inplace_optional_out_type_map = {
|
||||
"Tensor": "paddle::optional<Tensor>&",
|
||||
"std::vector<Tensor>": "paddle::optional<std::vector<Tensor>>&",
|
||||
}
|
||||
|
||||
optional_out_type_map = {
|
||||
"Tensor": "paddle::optional<Tensor>",
|
||||
"std::vector<Tensor>": "paddle::optional<std::vector<Tensor>>",
|
||||
}
|
||||
|
||||
|
||||
class ForwardAPI(BaseAPI):
|
||||
def __init__(self, api_item_yaml):
|
||||
super().__init__(api_item_yaml)
|
||||
self.is_dygraph_api, self.intermediate_outs = self.parse_intermediate(
|
||||
api_item_yaml
|
||||
)
|
||||
self.inplace_map, self.view_map = self.parse_inplace_and_view(
|
||||
api_item_yaml
|
||||
)
|
||||
|
||||
def get_api_func_name(self):
|
||||
if self.is_dygraph_api:
|
||||
return self.api + '_intermediate'
|
||||
else:
|
||||
return self.api
|
||||
|
||||
def gene_input(self, kernel_tensor_type=None, code_indent=''):
|
||||
kernel_param = self.kernel['param']
|
||||
input_name_tensor_map, input_tensor_code = super().gene_input(
|
||||
kernel_tensor_type, code_indent
|
||||
)
|
||||
|
||||
# generate the input that is in view list
|
||||
for i, input_name in enumerate(self.inputs['names']):
|
||||
if (
|
||||
input_name in self.view_map.values()
|
||||
and input_name not in input_name_tensor_map.keys()
|
||||
):
|
||||
if (
|
||||
kernel_tensor_type is None
|
||||
or kernel_tensor_type[0][kernel_param.index(input_name)]
|
||||
== 'dense'
|
||||
):
|
||||
trans_flag = self.gene_trans_flag(input_name)
|
||||
input_tensor_code = (
|
||||
input_tensor_code
|
||||
+ f"""
|
||||
{code_indent} auto {PREFIX_TENSOR_NAME}{input_name} = PrepareData({input_name}, kernel.InputAt(0), {trans_flag}, kernel_result.is_stride_kernel);"""
|
||||
)
|
||||
else:
|
||||
# do nothing
|
||||
pass
|
||||
|
||||
return input_name_tensor_map, input_tensor_code
|
||||
|
||||
def parse_intermediate(self, api_item_yaml):
|
||||
if 'intermediate' in api_item_yaml:
|
||||
intermediate_outs = [
|
||||
item.strip()
|
||||
for item in api_item_yaml['intermediate'].split(',')
|
||||
]
|
||||
return True, intermediate_outs
|
||||
else:
|
||||
return False, []
|
||||
|
||||
def parse_inplace_and_view(self, api_item_yaml):
|
||||
inplace_map, view_map = {}, {}
|
||||
for mode in ['inplace', 'view']:
|
||||
if mode in api_item_yaml:
|
||||
if mode == 'inplace':
|
||||
inplace_map = {}
|
||||
else:
|
||||
view_map = {}
|
||||
in_out_mapping_list = api_item_yaml[mode].split(',')
|
||||
for item in in_out_mapping_list:
|
||||
result = re.search(r"(?P<in>\w+)\s*->\s*(?P<out>\w+)", item)
|
||||
in_val = result.group('in')
|
||||
out_val = result.group('out')
|
||||
assert in_val in self.inputs['names'], (
|
||||
f"{self.api} : {mode} input error: the input var name('{in_val}') is not found in the input args of {self.api}."
|
||||
)
|
||||
assert out_val in self.outputs['names'], (
|
||||
f"{self.api} : {mode} output error: the output var name('{out_val}') is not found in the output args of {self.api}."
|
||||
)
|
||||
|
||||
if mode == 'inplace':
|
||||
inplace_map[out_val] = in_val
|
||||
else:
|
||||
view_map[out_val] = in_val
|
||||
|
||||
return inplace_map, view_map
|
||||
|
||||
def get_return_type_with_intermediate(self, inplace_flag=False):
|
||||
out_type_list = []
|
||||
for i, out_type in enumerate(self.outputs['types']):
|
||||
out_name = self.outputs['names'][i].split('@')[0]
|
||||
if inplace_flag and out_name in self.inplace_map:
|
||||
if self.inplace_map[out_name] in self.optional_vars:
|
||||
out_type_list.append(
|
||||
inplace_optional_out_type_map[out_type]
|
||||
)
|
||||
else:
|
||||
out_type_list.append(inplace_out_type_map[out_type])
|
||||
else:
|
||||
out_type_list.append(out_type)
|
||||
|
||||
if len(out_type_list) == 1:
|
||||
return out_type_list[0]
|
||||
else:
|
||||
return "std::tuple<" + ", ".join(out_type_list) + ">"
|
||||
|
||||
def get_return_type(self, inplace_flag=False):
|
||||
out_type_list = []
|
||||
for i, out_type in enumerate(self.outputs['types']):
|
||||
out_name = self.outputs['names'][i].split('@')[0]
|
||||
if inplace_flag and out_name in self.inplace_map:
|
||||
if self.inplace_map[out_name] in self.optional_vars:
|
||||
out_type_list.append(
|
||||
inplace_optional_out_type_map[out_type]
|
||||
)
|
||||
else:
|
||||
out_type_list.append(inplace_out_type_map[out_type])
|
||||
elif self.is_dygraph_api or out_name not in self.intermediate_outs:
|
||||
out_type_list.append(out_type)
|
||||
|
||||
if len(out_type_list) == 1:
|
||||
return out_type_list[0]
|
||||
else:
|
||||
return "std::tuple<" + ", ".join(out_type_list) + ">"
|
||||
|
||||
def gene_return_code(self):
|
||||
if self.is_dygraph_api or len(self.intermediate_outs) == 0:
|
||||
return "return api_output;"
|
||||
else:
|
||||
return_out_list = []
|
||||
for i, name in enumerate(self.outputs['names']):
|
||||
if name.split('@')[0] not in self.intermediate_outs:
|
||||
return_out_list.append(i)
|
||||
if len(return_out_list) == 1:
|
||||
return f"return std::get<{return_out_list[0]}>(api_output);"
|
||||
else:
|
||||
selected_code = [
|
||||
f"std::get<{i}>(api_output)" for i in return_out_list
|
||||
]
|
||||
return 'return std::make_tuple(' + ", ".join(selected_code) + ');'
|
||||
|
||||
def gene_fallback_code_after_gene_output_of_vector(
|
||||
self, code_indent, output_idx, is_inplace, is_optional
|
||||
):
|
||||
fallback_code = ""
|
||||
if is_inplace and is_optional:
|
||||
fallback_code = f"""
|
||||
{code_indent} if (kernel_result.has_fallback_cpu) {{
|
||||
{code_indent} for (size_t i = 0; i < kernel_out_{output_idx}.size(); ++i) {{
|
||||
{code_indent} kernel_out_{output_idx}[i] = const_cast<phi::DenseTensor*>({PREFIX_TENSOR_NAME}{self.inplace_map[self.outputs['names'][output_idx]]}->at(i));
|
||||
{code_indent} }}
|
||||
{code_indent} }}"""
|
||||
elif is_inplace:
|
||||
fallback_code = f"""
|
||||
{code_indent} if (kernel_result.has_fallback_cpu) {{
|
||||
{code_indent} for (size_t i = 0; i < kernel_out_{output_idx}.size(); ++i) {{
|
||||
{code_indent} kernel_out_{output_idx}[i] = const_cast<phi::DenseTensor*>({PREFIX_TENSOR_NAME}{self.inplace_map[self.outputs['names'][output_idx]]}[i]);
|
||||
{code_indent} }}
|
||||
{code_indent} }}"""
|
||||
else:
|
||||
fallback_code = ""
|
||||
|
||||
return fallback_code
|
||||
|
||||
def gene_output(
|
||||
self,
|
||||
out_dtype_list,
|
||||
out_tensor_type_list=None,
|
||||
code_indent='',
|
||||
inplace_flag=False,
|
||||
):
|
||||
kernel_output = []
|
||||
output_names = []
|
||||
output_create = ""
|
||||
return_type = self.get_return_type_with_intermediate(inplace_flag)
|
||||
|
||||
if len(out_dtype_list) == 1:
|
||||
kernel_output.append('kernel_out')
|
||||
output_names.append('kernel_out')
|
||||
inplace_assign = (
|
||||
" = " + self.inplace_map[self.outputs['names'][0]]
|
||||
if inplace_flag and self.outputs['names'][0] in self.inplace_map
|
||||
else ""
|
||||
)
|
||||
|
||||
if (
|
||||
len(self.outputs['names']) == 1
|
||||
and self.outputs['types'][0] == "Tensor"
|
||||
and not (
|
||||
inplace_flag
|
||||
and self.outputs['names'][0].split('@')[0]
|
||||
in self.inplace_map
|
||||
)
|
||||
and self.api != "empty_like"
|
||||
):
|
||||
output_create = f"""
|
||||
{code_indent} Tensor out_tmp; Tensor& api_output = predefined_out ? **predefined_out : out_tmp;"""
|
||||
else:
|
||||
output_create = f"""
|
||||
{code_indent} {return_type} api_output{inplace_assign};"""
|
||||
|
||||
set_out_func = (
|
||||
'SetKernelOutput'
|
||||
if out_tensor_type_list is None
|
||||
or out_tensor_type_list[0] == 'dense'
|
||||
else 'SetSelectedRowsKernelOutput'
|
||||
)
|
||||
|
||||
if (
|
||||
return_type == 'std::vector<Tensor>'
|
||||
or return_type == 'std::vector<Tensor>&'
|
||||
):
|
||||
assert self.outputs['out_size_expr'][0] is not None, (
|
||||
f"{self.api}: The out size expr : '{{expr}}' should be set when output has Tensor[]. You can refer 'split' api."
|
||||
)
|
||||
output_create = (
|
||||
output_create
|
||||
+ f"""
|
||||
{code_indent} auto kernel_out = {set_out_func}({self.outputs['out_size_expr'][0]}, &api_output);"""
|
||||
)
|
||||
elif (
|
||||
return_type == 'paddle::optional<std::vector<Tensor>>'
|
||||
or return_type == 'paddle::optional<std::vector<Tensor>>&'
|
||||
):
|
||||
assert self.outputs['out_size_expr'][0] is not None, (
|
||||
f"{self.api}: The out size expr : '{{expr}}' should be set when output has Tensor[]. You can refer 'split' api."
|
||||
)
|
||||
output_create = (
|
||||
output_create
|
||||
+ f"""
|
||||
{code_indent} auto kernel_out = {set_out_func}({self.outputs['out_size_expr'][0]}, api_output.get_ptr());"""
|
||||
)
|
||||
elif (
|
||||
return_type == 'paddle::optional<Tensor>'
|
||||
or return_type == 'paddle::optional<Tensor>&'
|
||||
):
|
||||
output_create = (
|
||||
output_create
|
||||
+ f"""
|
||||
{code_indent} auto kernel_out = {set_out_func}(api_output.get_ptr());"""
|
||||
)
|
||||
elif return_type == 'Tensor' or return_type == 'Tensor&':
|
||||
output_create = (
|
||||
output_create
|
||||
+ f"""
|
||||
{code_indent} auto kernel_out = {set_out_func}(&api_output);"""
|
||||
)
|
||||
|
||||
if (
|
||||
not inplace_flag
|
||||
and self.view_map is not None
|
||||
and self.outputs['names'][0] in self.view_map
|
||||
):
|
||||
output_create = (
|
||||
output_create
|
||||
+ f"""
|
||||
{code_indent} kernel_out->ShareBufferWith(*{PREFIX_TENSOR_NAME}{self.view_map[self.outputs['names'][0]]});
|
||||
{code_indent} kernel_out->ShareInplaceVersionCounterWith(*{PREFIX_TENSOR_NAME}{self.view_map[self.outputs['names'][0]]});
|
||||
{code_indent} VLOG(5) << "Perform View between Output and Input Tensor, share allocation and inplace version.";"""
|
||||
)
|
||||
|
||||
elif len(out_dtype_list) > 1:
|
||||
if not (
|
||||
inplace_flag
|
||||
and any(
|
||||
name.split('@')[0] in self.inplace_map
|
||||
for name in self.outputs['names']
|
||||
)
|
||||
):
|
||||
if IsUsePredefinedOut(self.outputs['types']):
|
||||
length = len(self.outputs['names'])
|
||||
if length == 1:
|
||||
output_create = f"""
|
||||
{code_indent} Tensor out_tmp; Tensor& api_output = predefined_out ? **predefined_out : out_tmp;"""
|
||||
else:
|
||||
tuple_types = ", ".join(["Tensor"] * length)
|
||||
get_indices = ", ".join(
|
||||
f"*std::get<{i}>(*predefined_out)"
|
||||
for i in range(length)
|
||||
)
|
||||
output_create = f"""
|
||||
{code_indent} std::tuple<{tuple_types}> out_tmp;
|
||||
{code_indent} paddle::optional<std::tuple<{tuple_types}>> predefined_out_value;
|
||||
{code_indent} if(predefined_out) {{ predefined_out_value = std::make_tuple({get_indices}); }}
|
||||
{code_indent} std::tuple<{tuple_types}>& api_output = predefined_out_value ? *predefined_out_value : out_tmp;"""
|
||||
else:
|
||||
output_create = f"""
|
||||
{code_indent} {return_type} api_output;"""
|
||||
else:
|
||||
output_create = f"""
|
||||
{code_indent} {return_type} api_output;"""
|
||||
|
||||
if inplace_flag:
|
||||
output_create = f"""
|
||||
{code_indent} {return_type} api_output{{"""
|
||||
|
||||
for out_name in self.outputs['names']:
|
||||
if out_name in self.inplace_map:
|
||||
output_create += self.inplace_map[out_name] + ', '
|
||||
else:
|
||||
output_create += 'Tensor(), '
|
||||
output_create = output_create[:-2] + '};'
|
||||
|
||||
for i in range(len(out_dtype_list)):
|
||||
kernel_output.append(f'kernel_out_{i}')
|
||||
output_names.append(f'kernel_out_{i}')
|
||||
set_out_func = (
|
||||
'SetKernelOutput'
|
||||
if out_tensor_type_list is None
|
||||
or out_tensor_type_list[i] == 'dense'
|
||||
else 'SetSelectedRowsKernelOutput'
|
||||
)
|
||||
|
||||
get_out_code = f"&std::get<{i}>(api_output)"
|
||||
if (
|
||||
inplace_flag
|
||||
and self.outputs['names'][i] in self.inplace_map
|
||||
and self.inplace_map[self.outputs['names'][i]]
|
||||
in self.optional_vars
|
||||
):
|
||||
get_out_code = f"std::get<{i}>(api_output).get_ptr()"
|
||||
|
||||
if out_dtype_list[i] == 'std::vector<Tensor>':
|
||||
assert self.outputs['out_size_expr'][i] is not None, (
|
||||
f"{self.api}: The out size expr : '{{expr}}' should be set when output has Tensor[]. You can refer 'split' api."
|
||||
)
|
||||
# Special case for inplace vector and inplace optional<vector>
|
||||
if self.outputs['names'][i] in self.inplace_map:
|
||||
set_out_func = "SetInplaceVectorKernelOutput"
|
||||
if (
|
||||
self.inplace_map[self.outputs['names'][i]]
|
||||
in self.optional_vars
|
||||
):
|
||||
set_out_func = (
|
||||
"SetInplaceOptionalVectorKernelOutput"
|
||||
)
|
||||
get_out_code = f"std::get<{i}>(api_output)"
|
||||
output_create = (
|
||||
output_create
|
||||
+ f"""
|
||||
{code_indent} auto kernel_out_{i} = {set_out_func}({self.outputs['out_size_expr'][i]}, {get_out_code});"""
|
||||
+ self.gene_fallback_code_after_gene_output_of_vector(
|
||||
code_indent, i, True, True
|
||||
)
|
||||
)
|
||||
else:
|
||||
output_create = (
|
||||
output_create
|
||||
+ f"""
|
||||
{code_indent} auto kernel_out_{i} = {set_out_func}({self.outputs['out_size_expr'][i]}, {get_out_code});"""
|
||||
+ self.gene_fallback_code_after_gene_output_of_vector(
|
||||
code_indent, i, True, False
|
||||
)
|
||||
)
|
||||
else:
|
||||
output_create = (
|
||||
output_create
|
||||
+ f"""
|
||||
{code_indent} auto kernel_out_{i} = {set_out_func}({self.outputs['out_size_expr'][i]}, {get_out_code});"""
|
||||
)
|
||||
|
||||
else:
|
||||
output_create = (
|
||||
output_create
|
||||
+ f"""
|
||||
{code_indent} auto kernel_out_{i} = {set_out_func}({get_out_code});"""
|
||||
)
|
||||
|
||||
if (
|
||||
not inplace_flag
|
||||
and self.view_map is not None
|
||||
and self.outputs['names'][i] in self.view_map
|
||||
):
|
||||
if out_dtype_list[i] == 'Tensor':
|
||||
output_create = (
|
||||
output_create
|
||||
+ f"""
|
||||
{code_indent} kernel_out_{i}->ShareBufferWith(*{PREFIX_TENSOR_NAME}{self.view_map[self.outputs['names'][i]]});
|
||||
{code_indent} kernel_out_{i}->ShareInplaceVersionCounterWith(*{PREFIX_TENSOR_NAME}{self.view_map[self.outputs['names'][i]]});
|
||||
{code_indent} VLOG(5) << "Perform View between Output and Input Tensor, share allocation and inplace version.";"""
|
||||
)
|
||||
else:
|
||||
raise ValueError(
|
||||
f"{self.api} : Output error: only support Tensor type when use view in yaml. But get {out_dtype_list[i]}"
|
||||
)
|
||||
else:
|
||||
raise ValueError(
|
||||
f"{self.api} : Output error: the output should not be empty."
|
||||
)
|
||||
|
||||
return kernel_output, output_names, output_create
|
||||
|
||||
def reset_view_after_fallback(
|
||||
self, out_dtype_list, code_indent='', inplace_flag=False
|
||||
):
|
||||
remap_code = ''
|
||||
|
||||
if len(out_dtype_list) == 1:
|
||||
if (
|
||||
not inplace_flag
|
||||
and self.view_map is not None
|
||||
and self.outputs['names'][0] in self.view_map
|
||||
):
|
||||
remap_code += f"""
|
||||
{code_indent} phi::DenseTensor * {self.view_map[self.outputs['names'][0]]}_remap = static_cast<phi::DenseTensor*>({self.view_map[self.outputs['names'][0]]}.impl().get());
|
||||
{code_indent} {self.view_map[self.outputs['names'][0]]}_remap->ShareBufferWith(*kernel_out);
|
||||
{code_indent} kernel_out->ShareInplaceVersionCounterWith(*{self.view_map[self.outputs['names'][0]]}_remap);
|
||||
"""
|
||||
elif len(out_dtype_list) > 1:
|
||||
for i in range(len(out_dtype_list)):
|
||||
if (
|
||||
not inplace_flag
|
||||
and self.view_map is not None
|
||||
and self.outputs['names'][i] in self.view_map
|
||||
):
|
||||
remap_code += f"""
|
||||
{code_indent} phi::DenseTensor * {self.view_map[self.outputs['names'][i]]}_remap = static_cast<phi::DenseTensor*>({self.view_map[self.outputs['names'][i]]}.impl().get());
|
||||
{code_indent} {self.view_map[self.outputs['names'][i]]}_remap->ShareBufferWith(*kernel_out_{i});
|
||||
{code_indent} kernel_out_{i}->ShareInplaceVersionCounterWith(*{self.view_map[self.outputs['names'][i]]}_remap);
|
||||
"""
|
||||
return remap_code
|
||||
|
||||
|
||||
class BackwardAPI(ForwardAPI):
|
||||
def gene_base_api_code(
|
||||
self, inplace_flag=False, grad_flag=False, append_predefined_out=True
|
||||
):
|
||||
api_func_name = self.get_api_func_name()
|
||||
if inplace_flag and api_func_name[-1] != '_':
|
||||
inplace_name = api_func_name + '_'
|
||||
else:
|
||||
inplace_name = api_func_name
|
||||
api_code = f"""
|
||||
PADDLE_API {self.get_return_type(inplace_flag)} {inplace_name}({self.get_define_args(inplace_flag, grad_flag=grad_flag, append_predefined_out=append_predefined_out)}) {{
|
||||
{self.get_grad_outputs_define(inplace_flag)}
|
||||
{self.get_optional_inputs_change(inplace_flag)}
|
||||
{api_func_name}({self.get_grad_api_call_args(inplace_flag)});
|
||||
return {self.get_grad_output(inplace_flag)};
|
||||
}}
|
||||
"""
|
||||
return api_code
|
||||
|
||||
def gene_api_code(self, grad_flag=False, append_predefined_out=False):
|
||||
if not self.is_base_api and not self.is_only_composite_api:
|
||||
invoke_func_name = self.invoke.split('(')[0]
|
||||
if (not invoke_func_name.endswith("_grad")) and (
|
||||
not invoke_func_name.endswith('_impl')
|
||||
):
|
||||
return ""
|
||||
|
||||
if self.is_only_composite_api:
|
||||
return ""
|
||||
|
||||
api_code = self.gene_base_api_code(
|
||||
grad_flag=grad_flag, append_predefined_out=append_predefined_out
|
||||
)
|
||||
if self.is_base_api and len(self.inplace_map) > 0:
|
||||
if self.api[-1] == '_':
|
||||
api_code = ""
|
||||
api_code = api_code + self.gene_base_api_code_for_inplace()
|
||||
|
||||
return api_code
|
||||
|
||||
def gene_api_declaration(self, grad_flag=False, append_predefined_out=True):
|
||||
if not self.is_base_api and not self.is_only_composite_api:
|
||||
invoke_func_name = self.invoke.split('(')[0]
|
||||
if (not invoke_func_name.endswith("_grad")) and (
|
||||
not invoke_func_name.endswith('_impl')
|
||||
):
|
||||
return ""
|
||||
|
||||
if self.is_only_composite_api:
|
||||
return ""
|
||||
|
||||
api_declaration = ""
|
||||
api_func_name = self.get_api_func_name()
|
||||
if api_func_name[-1] != '_':
|
||||
api_declaration = f"""
|
||||
PADDLE_API {self.get_return_type()} {api_func_name}({self.get_declare_args(append_predefined_out=append_predefined_out)});
|
||||
"""
|
||||
|
||||
if self.is_base_api and len(self.inplace_map) > 0:
|
||||
if api_func_name[-1] != '_':
|
||||
api_func_name += '_'
|
||||
api_declaration = (
|
||||
api_declaration
|
||||
+ f"""
|
||||
PADDLE_API {self.get_return_type(inplace_flag=True)} {api_func_name}({self.get_declare_args(inplace_flag=True, append_predefined_out=append_predefined_out)});
|
||||
"""
|
||||
)
|
||||
|
||||
return api_declaration
|
||||
|
||||
|
||||
def header_include():
|
||||
return """
|
||||
#include <tuple>
|
||||
|
||||
#include "paddle/phi/api/include/tensor.h"
|
||||
#include "paddle/phi/common/scalar.h"
|
||||
#include "paddle/phi/common/int_array.h"
|
||||
#include "paddle/utils/optional.h"
|
||||
"""
|
||||
|
||||
|
||||
def source_include(header_file_path):
|
||||
return f"""
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "paddle/common/flags.h"
|
||||
|
||||
{header_file_path}
|
||||
#include "paddle/phi/api/lib/api_custom_impl.h"
|
||||
#include "paddle/phi/api/lib/api_gen_utils.h"
|
||||
#include "paddle/phi/api/lib/api_registry.h"
|
||||
#include "paddle/phi/api/lib/data_transform.h"
|
||||
#include "paddle/phi/api/include/tensor_utils.h"
|
||||
#include "paddle/phi/api/lib/kernel_dispatch.h"
|
||||
#include "paddle/phi/common/type_traits.h"
|
||||
#include "paddle/phi/core/kernel_registry.h"
|
||||
#include "paddle/phi/infermeta/binary.h"
|
||||
#include "paddle/phi/infermeta/multiary.h"
|
||||
#include "paddle/phi/infermeta/nullary.h"
|
||||
#include "paddle/phi/infermeta/unary.h"
|
||||
#include "paddle/phi/infermeta/ternary.h"
|
||||
#include "paddle/phi/infermeta/fusion.h"
|
||||
#include "paddle/phi/infermeta/backward.h"
|
||||
|
||||
#include "paddle/phi/api/profiler/event_tracing.h"
|
||||
#include "paddle/phi/api/profiler/supplement_tracing.h"
|
||||
|
||||
#if defined(PADDLE_WITH_NCCL) || defined(PADDLE_WITH_RCCL)
|
||||
#include "paddle/phi/core/distributed/comm_context_manager.h"
|
||||
#include "paddle/phi/core/distributed/nccl_comm_context.h"
|
||||
#elif (defined(PADDLE_WITH_XPU) && defined(PADDLE_WITH_XPU_BKCL))
|
||||
#include "paddle/phi/core/distributed/comm_context_manager.h"
|
||||
#include "paddle/phi/core/distributed/bkcl_comm_context.h"
|
||||
#elif PADDLE_WITH_CUSTOM_DEVICE
|
||||
#include "paddle/phi/core/distributed/comm_context_manager.h"
|
||||
#include "paddle/phi/core/distributed/xccl_comm_context.h"
|
||||
#endif
|
||||
|
||||
#ifdef PADDLE_WITH_DISTRIBUTE
|
||||
#include "paddle/phi/core/distributed/store/store_utils.h"
|
||||
#include "paddle/phi/infermeta/spmd_rules/rules.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_utils.h"
|
||||
#endif
|
||||
|
||||
PD_DECLARE_bool(conv2d_disable_cudnn);
|
||||
COMMON_DECLARE_int32(low_precision_op_list);
|
||||
COMMON_DECLARE_bool(benchmark);
|
||||
"""
|
||||
|
||||
|
||||
def api_namespace():
|
||||
return (
|
||||
"""
|
||||
namespace paddle {
|
||||
namespace experimental {
|
||||
|
||||
""",
|
||||
"""
|
||||
|
||||
} // namespace experimental
|
||||
} // namespace paddle
|
||||
""",
|
||||
)
|
||||
|
||||
|
||||
def declare_extension_api():
|
||||
return """
|
||||
namespace paddle {
|
||||
PD_DECLARE_API(from_blob);
|
||||
#ifdef PADDLE_WITH_DISTRIBUTE
|
||||
PD_DECLARE_API(reshard);
|
||||
#endif
|
||||
} // namespace paddle
|
||||
"""
|
||||
|
||||
|
||||
def generate_api(
|
||||
api_yaml_path,
|
||||
is_fused_ops_yaml,
|
||||
header_file_path,
|
||||
source_file_path,
|
||||
grad_flag,
|
||||
):
|
||||
apis = []
|
||||
|
||||
for each_api_yaml in api_yaml_path:
|
||||
with open(each_api_yaml, 'r') as f:
|
||||
api_list = yaml.load(f, Loader=yaml.FullLoader)
|
||||
if api_list:
|
||||
apis.extend(api_list)
|
||||
|
||||
header_file = open(header_file_path, 'w')
|
||||
source_file = open(source_file_path, 'w')
|
||||
|
||||
namespace = api_namespace()
|
||||
|
||||
header_file.write("#pragma once\n")
|
||||
header_file.write(header_include())
|
||||
header_file.write(namespace[0])
|
||||
|
||||
if not grad_flag:
|
||||
include_header_file = (
|
||||
'#include "paddle/phi/api/include/fused_api.h"'
|
||||
if is_fused_ops_yaml is True
|
||||
else '#include "paddle/phi/api/include/api.h"'
|
||||
)
|
||||
else:
|
||||
include_header_file = (
|
||||
'#include "paddle/phi/api/backward/fused_backward_api.h" \n'
|
||||
'#include "paddle/phi/api/backward/fused_backward_api_base.h" '
|
||||
if is_fused_ops_yaml is True
|
||||
else '#include "paddle/phi/api/backward/backward_api.h" \n'
|
||||
'#include "paddle/phi/api/backward/backward_api_base.h" '
|
||||
)
|
||||
# not all fused ops support dygraph
|
||||
if is_fused_ops_yaml is True:
|
||||
new_apis = [
|
||||
api
|
||||
for api in apis
|
||||
if "support_dygraph_mode" in api
|
||||
and api["support_dygraph_mode"] is True
|
||||
]
|
||||
apis = new_apis
|
||||
|
||||
source_file.write(source_include(include_header_file))
|
||||
source_file.write(namespace[0])
|
||||
|
||||
for api in apis:
|
||||
if not grad_flag:
|
||||
forward_api = ForwardAPI(api)
|
||||
else:
|
||||
forward_api = BackwardAPI(api)
|
||||
|
||||
if forward_api.api in backward_api_black_list:
|
||||
continue
|
||||
if forward_api.is_dygraph_api and not is_fused_ops_yaml:
|
||||
forward_api.is_dygraph_api = False
|
||||
|
||||
if forward_api.is_dygraph_api and is_fused_ops_yaml:
|
||||
forward_api.is_dygraph_api = False
|
||||
header_file.write(
|
||||
forward_api.gene_api_declaration(
|
||||
grad_flag=grad_flag, append_predefined_out=not grad_flag
|
||||
)
|
||||
)
|
||||
source_file.write(forward_api.gene_api_code(grad_flag=grad_flag))
|
||||
forward_api.is_dygraph_api = True
|
||||
|
||||
header_file.write(
|
||||
forward_api.gene_api_declaration(
|
||||
grad_flag=grad_flag, append_predefined_out=not grad_flag
|
||||
)
|
||||
)
|
||||
source_file.write(forward_api.gene_api_code(grad_flag=grad_flag))
|
||||
|
||||
header_file.write(namespace[1])
|
||||
source_file.write(namespace[1])
|
||||
|
||||
source_file.write(declare_extension_api())
|
||||
|
||||
header_file.close()
|
||||
source_file.close()
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Generate PaddlePaddle C++ API files'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--api_yaml_path',
|
||||
help='path to api yaml file',
|
||||
nargs='+',
|
||||
default=['paddle/phi/ops/yaml/ops.yaml'],
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--backward_api_yaml_path',
|
||||
help='path to api yaml file',
|
||||
nargs='+',
|
||||
default=['paddle/phi/ops/yaml/backward.yaml'],
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--is_fused_ops_yaml',
|
||||
help='flag of fused ops yaml',
|
||||
action='store_true',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--api_header_path',
|
||||
help='output of generated api header code file',
|
||||
default='paddle/phi/api/include/api.h',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--api_source_path',
|
||||
help='output of generated api source code file',
|
||||
default='paddle/phi/api/lib/api.cc',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--backward_api_header_path',
|
||||
help='output of generated api header code file',
|
||||
default='paddle/phi/api/backward/backward_api.h',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--backward_api_source_path',
|
||||
help='output of generated api source code file',
|
||||
default='paddle/phi/api/lib/backward_api.cc',
|
||||
)
|
||||
|
||||
options = parser.parse_args()
|
||||
|
||||
api_yaml_path = options.api_yaml_path
|
||||
backward_api_yaml_path = options.backward_api_yaml_path
|
||||
is_fused_ops_yaml = options.is_fused_ops_yaml
|
||||
header_file_path = options.api_header_path
|
||||
source_file_path = options.api_source_path
|
||||
backward_header_file_path = options.backward_api_header_path
|
||||
backward_source_file_path = options.backward_api_source_path
|
||||
|
||||
generate_api(
|
||||
api_yaml_path,
|
||||
is_fused_ops_yaml,
|
||||
header_file_path,
|
||||
source_file_path,
|
||||
False,
|
||||
)
|
||||
|
||||
generate_api(
|
||||
backward_api_yaml_path,
|
||||
is_fused_ops_yaml,
|
||||
backward_header_file_path,
|
||||
backward_source_file_path,
|
||||
True,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,430 @@
|
||||
# Copyright (c) 2021 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.
|
||||
|
||||
import argparse
|
||||
import re
|
||||
|
||||
import yaml
|
||||
from api_base import BaseAPI
|
||||
|
||||
|
||||
class BackwardAPI(BaseAPI):
|
||||
def __init__(self, backward_item_yaml):
|
||||
super().__init__(backward_item_yaml)
|
||||
self.check_args(backward_item_yaml['forward'])
|
||||
self.no_need_buffer = self.parse_no_need_buffer(backward_item_yaml)
|
||||
|
||||
def get_api_name(self, api_item_yaml):
|
||||
return api_item_yaml['backward_op']
|
||||
|
||||
def parse_forward_config(self, forward_config):
|
||||
# api_name (const Tensor& input, ... , int attr, ...) -> Tensor(out)
|
||||
result = re.search(
|
||||
r"(?P<op>[a-z][a-z0-9_]+)\s*(?P<args>\([^\)]+\))\s*->\s*(?P<outputs>.+)",
|
||||
forward_config,
|
||||
)
|
||||
api = result.group('op')
|
||||
(
|
||||
_,
|
||||
outputs,
|
||||
_,
|
||||
) = self.parse_output(self.api, result.group('outputs'))
|
||||
outputs = [item.split('@')[0] for item in outputs]
|
||||
fw_inputs, fw_attrs = self.parse_input_and_attr(
|
||||
api, result.group('args')
|
||||
)
|
||||
|
||||
return api, fw_inputs, fw_attrs, outputs
|
||||
|
||||
def parse_no_need_buffer(self, api_item_yaml):
|
||||
no_need_buffer = []
|
||||
if 'no_need_buffer' in api_item_yaml:
|
||||
no_need_buffer = [
|
||||
item.strip()
|
||||
for item in api_item_yaml['no_need_buffer'].split(',')
|
||||
]
|
||||
return no_need_buffer
|
||||
|
||||
def check_args(self, forward_config):
|
||||
# parse the forward and backward config
|
||||
_, fw_inputs, fw_attrs, fw_outputs = self.parse_forward_config(
|
||||
forward_config
|
||||
)
|
||||
|
||||
# check the inputs of backward
|
||||
for input in self.inputs['names']:
|
||||
if input not in fw_inputs['names'] and input not in fw_outputs:
|
||||
if input.endswith('_grad'):
|
||||
original_name = input[:-5]
|
||||
assert original_name in fw_outputs, (
|
||||
f"{self.api} : Input Tensor error: the input tensor({input}) of backward should be an input or output or grad of output in forward api. \
|
||||
Please check the forward of {self.api} in yaml."
|
||||
)
|
||||
|
||||
# check the attributes of backward
|
||||
for attr in self.attrs['names']:
|
||||
assert (
|
||||
attr in fw_attrs['names']
|
||||
and self.attrs['attr_info'][attr][0]
|
||||
== fw_attrs['attr_info'][attr][0]
|
||||
) or self.attrs['attr_info'][attr][1] is not None, (
|
||||
f"{self.api} : Attribute error: The attribute({attr}) of backward isn't consistent with forward api or doesn't have default value. \
|
||||
Please check the args of {self.api} in yaml."
|
||||
)
|
||||
|
||||
# check the output of backward
|
||||
assert len(self.outputs['types']) <= len(fw_inputs['names']), (
|
||||
f"{self.api} : Output error: The number of outputs should be less then the number of inputs of forward api. \
|
||||
Please check the output of {self.api} in yaml."
|
||||
)
|
||||
|
||||
def get_declare_args(
|
||||
self, inplace_flag=False, grad_flag=False, append_predefined_out=False
|
||||
):
|
||||
return self.get_define_args(
|
||||
grad_flag=grad_flag, append_predefined_out=append_predefined_out
|
||||
)
|
||||
|
||||
def get_define_args(
|
||||
self, inplace_flag=False, grad_flag=False, append_predefined_out=False
|
||||
):
|
||||
out_type_map = {
|
||||
'Tensor': 'Tensor*',
|
||||
'std::vector<Tensor>': 'std::vector<Tensor*>',
|
||||
}
|
||||
inputs_and_attrs = super().get_define_args(
|
||||
grad_flag=grad_flag, append_predefined_out=False
|
||||
)
|
||||
outs = []
|
||||
for i, name in enumerate(self.outputs['names']):
|
||||
outs.append(
|
||||
out_type_map[self.outputs['types'][i]]
|
||||
+ ' '
|
||||
+ name.split('@')[0]
|
||||
)
|
||||
result = inputs_and_attrs + ', ' + ", ".join(outs)
|
||||
return result
|
||||
|
||||
def gene_return_code(self):
|
||||
return ""
|
||||
|
||||
def gene_api_declaration(
|
||||
self, grad_flag=False, append_predefined_out=False
|
||||
):
|
||||
if not self.is_base_api and not self.is_only_composite_api:
|
||||
invoke_func_name = self.invoke.split('(')[0]
|
||||
if (not invoke_func_name.endswith("_grad")) and (
|
||||
not invoke_func_name.endswith('_impl')
|
||||
):
|
||||
return ""
|
||||
|
||||
if self.is_only_composite_api:
|
||||
return ""
|
||||
|
||||
api_func_name = self.get_api_func_name()
|
||||
api_declaration = f"""
|
||||
PADDLE_API void {api_func_name}({self.get_declare_args()});
|
||||
"""
|
||||
return api_declaration
|
||||
|
||||
def gene_kernel_backend_select(self):
|
||||
all_no_need_buffer = True
|
||||
for in_name in self.inputs['names']:
|
||||
if in_name not in self.no_need_buffer:
|
||||
all_no_need_buffer = False
|
||||
|
||||
if all_no_need_buffer:
|
||||
return """
|
||||
kernel_backend = ParseBackend(egr::Controller::Instance().GetExpectedPlace());
|
||||
"""
|
||||
else:
|
||||
return super().gene_kernel_backend_select()
|
||||
|
||||
def get_return_type(self, inplace_flag=False):
|
||||
return 'void'
|
||||
|
||||
def gene_output(
|
||||
self,
|
||||
out_dtype_list,
|
||||
out_tensor_type_list=None,
|
||||
code_indent='',
|
||||
inplace_flag=False,
|
||||
):
|
||||
kernel_output = []
|
||||
output_names = []
|
||||
output_create = ""
|
||||
|
||||
if len(out_dtype_list) == 1:
|
||||
kernel_output.append('kernel_out')
|
||||
output_names.append('kernel_out')
|
||||
inplace_assign = (
|
||||
" = " + self.inplace_map[self.outputs['names'][0]]
|
||||
if inplace_flag
|
||||
and self.inplace_map is not None
|
||||
and self.outputs['names'][0] in self.inplace_map
|
||||
else ""
|
||||
)
|
||||
output_create = ""
|
||||
set_out_func = (
|
||||
'SetKernelOutput'
|
||||
if out_tensor_type_list is None
|
||||
or out_tensor_type_list[0] == 'dense'
|
||||
else 'SetSelectedRowsKernelOutput'
|
||||
)
|
||||
if out_dtype_list[0] == 'std::vector<Tensor>':
|
||||
assert self.outputs['out_size_expr'] is not None, (
|
||||
f"{self.api}: The out size expr : '{{expr}}' should be set when output has Tensor[]. You can refer 'split' api."
|
||||
)
|
||||
output_create = (
|
||||
output_create
|
||||
+ f"""
|
||||
{code_indent} auto kernel_out = {set_out_func}(&{self.outputs['names'][0]});"""
|
||||
)
|
||||
|
||||
else:
|
||||
output_create = (
|
||||
output_create
|
||||
+ f"""
|
||||
{code_indent} auto kernel_out = {set_out_func}({self.outputs['names'][0]});"""
|
||||
)
|
||||
|
||||
elif len(out_dtype_list) > 1:
|
||||
output_create = ""
|
||||
for i, out_type_item in enumerate(out_dtype_list):
|
||||
kernel_output.append(f'kernel_out_{i}')
|
||||
output_names.append(f'kernel_out_{i}')
|
||||
set_out_func = (
|
||||
'SetKernelOutput'
|
||||
if out_tensor_type_list is None
|
||||
or out_tensor_type_list[i] == 'dense'
|
||||
else 'SetSelectedRowsKernelOutput'
|
||||
)
|
||||
if out_type_item == 'Tensor':
|
||||
if (
|
||||
inplace_flag
|
||||
and self.inplace_map is not None
|
||||
and self.outputs['names'][i] in self.inplace_map
|
||||
):
|
||||
output_create = (
|
||||
output_create
|
||||
+ f"""
|
||||
{code_indent} *{self.outputs['names'][i]} = {self.inplace_map[self.outputs['names'][i]]};"""
|
||||
)
|
||||
|
||||
output_create = (
|
||||
output_create
|
||||
+ f"""
|
||||
{code_indent} auto kernel_out_{i} = {set_out_func}({self.outputs['names'][i]});"""
|
||||
)
|
||||
|
||||
else:
|
||||
if (
|
||||
inplace_flag
|
||||
and self.inplace_map is not None
|
||||
and self.outputs['names'][i] in self.inplace_map
|
||||
):
|
||||
output_create = (
|
||||
output_create
|
||||
+ f"""
|
||||
{code_indent} *{self.outputs['names'][i]} = {self.inplace_map[self.outputs['names'][i]]};"""
|
||||
)
|
||||
|
||||
assert self.outputs['out_size_expr'][i] is not None, (
|
||||
f"{self.api}: The out size expr : '{{expr}}' should be set when output has Tensor[]. You can refer 'split' api."
|
||||
)
|
||||
output_create = (
|
||||
output_create
|
||||
+ f"""
|
||||
{code_indent} auto kernel_out_{i} = {set_out_func}(&{self.outputs['names'][i]});"""
|
||||
)
|
||||
|
||||
else:
|
||||
raise ValueError(
|
||||
f"{self.api} : Output error: the output should not be empty."
|
||||
)
|
||||
|
||||
return kernel_output, output_names, output_create
|
||||
|
||||
def gene_invoke_code(self, invoke_code, params_code):
|
||||
invoke_func_name = invoke_code.split('(')[0].strip()
|
||||
if invoke_func_name.endswith('_grad') or invoke_func_name.endswith(
|
||||
'_impl'
|
||||
):
|
||||
return f"""
|
||||
PADDLE_API {self.get_return_type()} {self.api}({params_code}) {{
|
||||
{invoke_code};
|
||||
}}"""
|
||||
|
||||
else:
|
||||
return ""
|
||||
|
||||
|
||||
def header_include():
|
||||
return """
|
||||
#include <tuple>
|
||||
|
||||
#include "paddle/phi/api/include/tensor.h"
|
||||
#include "paddle/phi/common/scalar.h"
|
||||
#include "paddle/phi/common/int_array.h"
|
||||
#include "paddle/utils/optional.h"
|
||||
"""
|
||||
|
||||
|
||||
def source_include(header_file_path, fw_header_file_path):
|
||||
return f"""
|
||||
#include "{header_file_path}"
|
||||
#include <memory>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "paddle/common/flags.h"
|
||||
|
||||
#include "paddle/phi/api/lib/api_custom_impl.h"
|
||||
#include "paddle/phi/api/lib/api_gen_utils.h"
|
||||
#include "paddle/phi/api/lib/data_transform.h"
|
||||
#include "paddle/phi/api/lib/kernel_dispatch.h"
|
||||
#include "paddle/phi/common/type_traits.h"
|
||||
#include "paddle/phi/core/kernel_registry.h"
|
||||
#include "{fw_header_file_path}"
|
||||
#include "paddle/phi/infermeta/backward.h"
|
||||
#include "paddle/phi/infermeta/unary.h"
|
||||
#include "paddle/phi/infermeta/fusion.h"
|
||||
|
||||
#include "paddle/phi/api/profiler/event_tracing.h"
|
||||
#include "paddle/phi/api/profiler/supplement_tracing.h"
|
||||
|
||||
PD_DECLARE_bool(conv2d_disable_cudnn);
|
||||
COMMON_DECLARE_int32(low_precision_op_list);
|
||||
COMMON_DECLARE_bool(benchmark);
|
||||
"""
|
||||
|
||||
|
||||
def backward_api_namespace():
|
||||
return (
|
||||
"""
|
||||
namespace paddle {
|
||||
namespace experimental {
|
||||
|
||||
""",
|
||||
"""
|
||||
|
||||
} // namespace experimental
|
||||
} // namespace paddle
|
||||
""",
|
||||
)
|
||||
|
||||
|
||||
def generate_backward_api(
|
||||
backward_yaml_path,
|
||||
is_fused_backward_yaml,
|
||||
header_file_path,
|
||||
source_file_path,
|
||||
):
|
||||
bw_apis = []
|
||||
for each_api_yaml in backward_yaml_path:
|
||||
with open(each_api_yaml, 'r') as f:
|
||||
api_list = yaml.load(f, Loader=yaml.FullLoader)
|
||||
if api_list:
|
||||
bw_apis.extend(api_list)
|
||||
|
||||
header_file = open(header_file_path, 'w')
|
||||
source_file = open(source_file_path, 'w')
|
||||
|
||||
namespace = backward_api_namespace()
|
||||
|
||||
header_file.write("#pragma once\n")
|
||||
header_file.write(header_include())
|
||||
header_file.write(namespace[0])
|
||||
|
||||
include_header_file = (
|
||||
"paddle/phi/api/backward/fused_backward_api_base.h"
|
||||
if is_fused_backward_yaml
|
||||
else "paddle/phi/api/backward/backward_api_base.h"
|
||||
)
|
||||
include_fw_header_file = (
|
||||
"paddle/phi/api/include/fused_api.h"
|
||||
if is_fused_backward_yaml
|
||||
else "paddle/phi/api/include/api.h"
|
||||
)
|
||||
source_file.write(
|
||||
source_include(include_header_file, include_fw_header_file)
|
||||
)
|
||||
source_file.write(namespace[0])
|
||||
# not all fused ops support dygraph
|
||||
if is_fused_backward_yaml is True:
|
||||
new_bw_apis = [
|
||||
bw_api
|
||||
for bw_api in bw_apis
|
||||
if "support_dygraph_mode" in bw_api
|
||||
and bw_api["support_dygraph_mode"] is True
|
||||
]
|
||||
bw_apis = new_bw_apis
|
||||
|
||||
for bw_api in bw_apis:
|
||||
bw_api = BackwardAPI(bw_api)
|
||||
header_file.write(bw_api.gene_api_declaration())
|
||||
source_file.write(bw_api.gene_api_code())
|
||||
|
||||
header_file.write(namespace[1])
|
||||
source_file.write(namespace[1])
|
||||
|
||||
header_file.close()
|
||||
source_file.close()
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Generate PaddlePaddle C++ backward API files'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--backward_yaml_path',
|
||||
help='path to backward yaml file',
|
||||
nargs='+',
|
||||
default=['paddle/phi/ops/yaml/backward.yaml'],
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--is_fused_backward_yaml',
|
||||
help='flag of fused backward yaml',
|
||||
action='store_true',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--backward_header_path',
|
||||
help='output of generated backward header code file',
|
||||
default='paddle/phi/api/backward/backward_api_base.h',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--backward_source_path',
|
||||
help='output of generated backward source code file',
|
||||
default='paddle/phi/api/lib/backward_api_base.cc',
|
||||
)
|
||||
|
||||
options = parser.parse_args()
|
||||
|
||||
backward_yaml_path = options.backward_yaml_path
|
||||
is_fused_backward_yaml = options.is_fused_backward_yaml
|
||||
header_file_path = options.backward_header_path
|
||||
source_file_path = options.backward_source_path
|
||||
|
||||
generate_backward_api(
|
||||
backward_yaml_path,
|
||||
is_fused_backward_yaml,
|
||||
header_file_path,
|
||||
source_file_path,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,690 @@
|
||||
# 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.
|
||||
|
||||
import argparse
|
||||
|
||||
import dist_api_gen
|
||||
import yaml
|
||||
from backward_api_gen import BackwardAPI
|
||||
from dist_api_gen import DistForwardAPI
|
||||
|
||||
######################
|
||||
# Code Gen Templates #
|
||||
######################
|
||||
|
||||
MAIN_DIST_BRANCH_TEMPLATE = """
|
||||
// Auto Parallel condition
|
||||
if (run_auto_parallel) {{
|
||||
// 1. InferSpmd (Infer DistAttr of Inputs&Outputs){}
|
||||
// 2. Create Temporary Output & Prepare Dist and Dense Output{}
|
||||
// 3. Infer DistTensor's Global Shape{}\n
|
||||
// 4. Set Output Dist Attr For Default Impl{}\n
|
||||
if (rank_is_in_current_mesh) {{
|
||||
// 5. Select Kernel{}
|
||||
// 6. Reshard Input{}\n
|
||||
// 7. PrepareData (DataTransform & Prepare Dense Input){}
|
||||
// 8. RecordOpInfoSupplement{}
|
||||
// 9. Infer Local DenseTensor Meta{}
|
||||
// 10. DenseTensor Kernel Call{}
|
||||
// 11. Fallback{}
|
||||
}}
|
||||
// 12. Reshard Kernel Output to API output{}\n
|
||||
// 13. Return
|
||||
{}
|
||||
}}
|
||||
"""
|
||||
|
||||
# 1. Create API Outputs
|
||||
SINGLE_OUT_CREATION_TEMPLATE_NO_SPMD = """
|
||||
auto dist_out = SetKernelDistOutput({});
|
||||
auto dense_out = dist_out->unsafe_mutable_value();
|
||||
"""
|
||||
SINGLE_OUT_CREATION_TEMPLATE_WITH_SPMD = """
|
||||
std::shared_ptr<phi::distributed::DistTensor> shared_dist_out =
|
||||
CreateKernelDistOutput({}, !rank_is_in_current_mesh, spmd_info.second[0]);
|
||||
phi::distributed::DistTensor* dist_out = shared_dist_out.get();
|
||||
phi::DenseTensor* dense_out = nullptr;
|
||||
if (dist_out) {{
|
||||
dense_out = dist_out->unsafe_mutable_value();
|
||||
if (dense_out && !rank_is_in_current_mesh && !dist_out->defined()) {{
|
||||
*dense_out = phi::DenseTensor(
|
||||
std::make_shared<phi::Allocation>(nullptr, 0, phi::distributed::GetDefaultPlace()),
|
||||
phi::DenseTensorMeta());
|
||||
}}
|
||||
}}
|
||||
"""
|
||||
SINGLE_OUT_CREATION_TEMPLATE = """
|
||||
std::shared_ptr<phi::distributed::DistTensor> shared_dist_out =
|
||||
CreateKernelDistOutput({}, !rank_is_in_current_mesh);
|
||||
phi::distributed::DistTensor* dist_out = shared_dist_out.get();
|
||||
phi::DenseTensor* dense_out = nullptr;
|
||||
if (dist_out) {{
|
||||
dense_out = dist_out->unsafe_mutable_value();
|
||||
if (dense_out && !rank_is_in_current_mesh && !dist_out->defined()) {{
|
||||
*dense_out = phi::DenseTensor(
|
||||
std::make_shared<phi::Allocation>(nullptr, 0, phi::distributed::GetDefaultPlace()),
|
||||
phi::DenseTensorMeta());
|
||||
}}
|
||||
}}
|
||||
"""
|
||||
VECTOR_OUT_CREATION_TEMPLATE_WITH_NO_SPMD = """
|
||||
auto dist_out = SetKernelDistOutput({name});
|
||||
std::vector<phi::DenseTensor*> dense_out(dist_out.size(), nullptr);
|
||||
for (size_t i=0; i<dist_out.size(); i++) {{
|
||||
if (dist_out[i]) {{
|
||||
dense_out[i] = dist_out[i]->unsafe_mutable_value();
|
||||
if (dense_out[i] && !rank_is_in_current_mesh && !dist_out[i]->defined()) {{
|
||||
*dense_out[i] = phi::DenseTensor(
|
||||
std::make_shared<phi::Allocation>(nullptr, 0, phi::distributed::GetDefaultPlace()),
|
||||
phi::DenseTensorMeta());
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
"""
|
||||
|
||||
VECTOR_OUT_CREATION_TEMPLATE_WITH_SPMD = """
|
||||
auto shared_dist_out = CreateKernelDistOutput({name}, !rank_is_in_current_mesh, spmd_info.second[0]);
|
||||
std::vector<phi::distributed::DistTensor*> dist_out;
|
||||
for(auto& e: shared_dist_out){{
|
||||
dist_out.push_back(e.get());
|
||||
}}
|
||||
std::vector<phi::DenseTensor*> dense_out(dist_out.size(), nullptr);
|
||||
for (size_t i=0; i<dist_out.size(); i++) {{
|
||||
if (dist_out[i]) {{
|
||||
dense_out[i] = dist_out[i]->unsafe_mutable_value();
|
||||
if (dense_out[i] && !rank_is_in_current_mesh && !dist_out[i]->defined()) {{
|
||||
*dense_out[i] = phi::DenseTensor(
|
||||
std::make_shared<phi::Allocation>(nullptr, 0, phi::distributed::GetDefaultPlace()),
|
||||
phi::DenseTensorMeta());
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
"""
|
||||
|
||||
|
||||
VECTOR_OUT_CREATION_TEMPLATE = """
|
||||
auto shared_dist_out = CreateKernelDistOutput({name}, !rank_is_in_current_mesh);
|
||||
std::vector<phi::distributed::DistTensor*> dist_out;
|
||||
for(auto& e: shared_dist_out){{
|
||||
dist_out.push_back(e.get());
|
||||
}}
|
||||
std::vector<phi::DenseTensor*> dense_out(dist_out.size(), nullptr);
|
||||
for (size_t i=0; i<dist_out.size(); i++) {{
|
||||
if (dist_out[i]) {{
|
||||
dense_out[i] = dist_out[i]->unsafe_mutable_value();
|
||||
if (dense_out[i] && !rank_is_in_current_mesh && !dist_out[i]->defined()) {{
|
||||
*dense_out[i] = phi::DenseTensor(
|
||||
std::make_shared<phi::Allocation>(nullptr, 0, phi::distributed::GetDefaultPlace()),
|
||||
phi::DenseTensorMeta());
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
"""
|
||||
|
||||
|
||||
INPLACE_OUT_CREATION_TEMPLATE = """
|
||||
*{} = {};
|
||||
"""
|
||||
MULTI_SINGLE_OUT_CREATION_TEMPLATE_NO_SPMD = """
|
||||
auto dist_out_{idx} = SetKernelDistOutput({name});
|
||||
auto dense_out_{idx} = dist_out_{idx} ? dist_out_{idx}->unsafe_mutable_value() : nullptr;
|
||||
if (dense_out_{idx} && !rank_is_in_current_mesh && !dist_out_{idx}->defined()) {{
|
||||
*dense_out_{idx} = phi::DenseTensor(
|
||||
std::make_shared<phi::Allocation>(nullptr, 0, phi::distributed::GetDefaultPlace()),
|
||||
phi::DenseTensorMeta());
|
||||
}}
|
||||
"""
|
||||
MULTI_SINGLE_OUT_CREATION_TEMPLATE_WITH_SPMD = """
|
||||
std::shared_ptr<phi::distributed::DistTensor> shared_dist_out_{idx} =
|
||||
CreateKernelDistOutput({name}, !rank_is_in_current_mesh, spmd_info.second[{idx}]);
|
||||
phi::distributed::DistTensor* dist_out_{idx} = shared_dist_out_{idx}.get();
|
||||
phi::DenseTensor* dense_out_{idx} = dist_out_{idx} ? dist_out_{idx}->unsafe_mutable_value() : nullptr;
|
||||
if (dense_out_{idx} && !rank_is_in_current_mesh && !dist_out_{idx}->defined()) {{
|
||||
*dense_out_{idx} = phi::DenseTensor(
|
||||
std::make_shared<phi::Allocation>(nullptr, 0, phi::distributed::GetDefaultPlace()),
|
||||
phi::DenseTensorMeta());
|
||||
}}
|
||||
"""
|
||||
MULTI_SINGLE_OUT_CREATION_TEMPLATE = """
|
||||
std::shared_ptr<phi::distributed::DistTensor> shared_dist_out_{idx} =
|
||||
CreateKernelDistOutput({name}, !rank_is_in_current_mesh);
|
||||
phi::distributed::DistTensor* dist_out_{idx} = shared_dist_out_{idx}.get();
|
||||
phi::DenseTensor* dense_out_{idx} = dist_out_{idx} ? dist_out_{idx}->unsafe_mutable_value() : nullptr;
|
||||
if (dense_out_{idx} && !rank_is_in_current_mesh && !dist_out_{idx}->defined()) {{
|
||||
*dense_out_{idx} = phi::DenseTensor(
|
||||
std::make_shared<phi::Allocation>(nullptr, 0, phi::distributed::GetDefaultPlace()),
|
||||
phi::DenseTensorMeta());
|
||||
}}
|
||||
"""
|
||||
MULTI_VECTOR_OUT_CREATION_TEMPLATE = """
|
||||
auto dist_out_{i} = SetKernelDistOutput({name});
|
||||
std::vector<phi::DenseTensor*> dense_out_{i}(dist_out_{i}.size(), nullptr);
|
||||
for (size_t i = 0; i < dist_out_{i}.size(); i++) {{
|
||||
if (dist_out_{i}[i]) {{
|
||||
dense_out_{i}[i] = const_cast<phi::DenseTensor*>(&dist_out_{i}[i]->value());
|
||||
if (dense_out_{i}[i] && !rank_is_in_current_mesh && !dist_out_{i}[i]->defined()) {{
|
||||
*dense_out_{i}[i]= phi::DenseTensor(
|
||||
std::make_shared<phi::Allocation>(nullptr, 0, phi::distributed::GetDefaultPlace()),
|
||||
phi::DenseTensorMeta());
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
"""
|
||||
|
||||
# 9. Reshard Output
|
||||
RESHARD_SINGLE_OUTPUT_TEMPLATE = """
|
||||
ReshardKernelOutputToApiOutput(dev_ctx, shared_dist_out, {}, "{}");"""
|
||||
|
||||
RESHARD_MULTI_SINGLE_OUTPUT_TEMPLATE = """
|
||||
ReshardKernelOutputToApiOutput(dev_ctx, shared_dist_out_{}, {}, "{}");"""
|
||||
|
||||
RESHARD_VECTOR_OUTPUT_TEMPLATE = """
|
||||
ReshardKernelOutputToApiOutput(dev_ctx, shared_dist_out, {}, "{}");"""
|
||||
|
||||
NONEED_TO_RESHARD_OUTPUT_TEMPLATE = """
|
||||
// API `{}` does not need to reshard output."""
|
||||
|
||||
SET_LOCAL_SHAPE_TEMPLATE = """
|
||||
{meta_tensor}.set_dims(phi::make_ddim(local_shape));"""
|
||||
|
||||
|
||||
class DistBackwardAPI(DistForwardAPI, BackwardAPI):
|
||||
def __init__(self, backward_item_yaml):
|
||||
BackwardAPI.__init__(self, backward_item_yaml)
|
||||
self.forward_config = backward_item_yaml['forward']
|
||||
self.init_dist_api_members()
|
||||
|
||||
# override DistForwardAPI's method
|
||||
def generate_output_creation_code(self) -> str:
|
||||
# backward api only need to generate kernel outputs
|
||||
output_num = len(self.outputs['types'])
|
||||
output_creation_code = ""
|
||||
output_creation_code += "\n phi::DeviceContext* dev_ctx = nullptr;"
|
||||
if output_num == 1:
|
||||
self.dist_output_args.append('dist_out')
|
||||
self.dense_output_args.append('dense_out')
|
||||
if self.outputs['types'][0] == 'Tensor':
|
||||
if self.infer_meta['spmd_rule'] is not None:
|
||||
output_creation_code += (
|
||||
SINGLE_OUT_CREATION_TEMPLATE_WITH_SPMD.format(
|
||||
self.outputs['names'][0]
|
||||
)
|
||||
)
|
||||
elif self.generate_general_infer_spmd is True:
|
||||
output_creation_code += SINGLE_OUT_CREATION_TEMPLATE.format(
|
||||
self.outputs['names'][0]
|
||||
)
|
||||
else:
|
||||
output_creation_code += (
|
||||
SINGLE_OUT_CREATION_TEMPLATE_NO_SPMD.format(
|
||||
self.outputs['names'][0]
|
||||
)
|
||||
)
|
||||
elif self.outputs['types'][0] == 'std::vector<Tensor>':
|
||||
if self.infer_meta['spmd_rule'] is not None:
|
||||
output_creation_code += (
|
||||
VECTOR_OUT_CREATION_TEMPLATE_WITH_SPMD.format(
|
||||
name=self.outputs['names'][0]
|
||||
)
|
||||
)
|
||||
elif self.generate_general_infer_spmd is True:
|
||||
output_creation_code += VECTOR_OUT_CREATION_TEMPLATE.format(
|
||||
name=self.outputs['names'][0]
|
||||
)
|
||||
else:
|
||||
output_creation_code += (
|
||||
VECTOR_OUT_CREATION_TEMPLATE_WITH_NO_SPMD.format(
|
||||
name=self.outputs['names'][0]
|
||||
)
|
||||
)
|
||||
else:
|
||||
self.vector_output_size_assertion_check()
|
||||
elif output_num > 1:
|
||||
for i, out_type in enumerate(self.outputs['types']):
|
||||
self.dist_output_args.append(f'dist_out_{i}')
|
||||
self.dense_output_args.append(f'dense_out_{i}')
|
||||
if out_type == 'Tensor':
|
||||
if self.infer_meta['spmd_rule'] is not None:
|
||||
output_creation_code += (
|
||||
MULTI_SINGLE_OUT_CREATION_TEMPLATE_WITH_SPMD.format(
|
||||
name=self.outputs['names'][i], idx=i
|
||||
)
|
||||
)
|
||||
elif self.generate_general_infer_spmd is True:
|
||||
output_creation_code += (
|
||||
MULTI_SINGLE_OUT_CREATION_TEMPLATE.format(
|
||||
name=self.outputs['names'][i], idx=i
|
||||
)
|
||||
)
|
||||
else:
|
||||
output_creation_code += (
|
||||
MULTI_SINGLE_OUT_CREATION_TEMPLATE_NO_SPMD.format(
|
||||
name=self.outputs['names'][i], idx=i
|
||||
)
|
||||
)
|
||||
elif out_type == 'std::vector<Tensor>':
|
||||
output_creation_code += (
|
||||
MULTI_VECTOR_OUT_CREATION_TEMPLATE.format(
|
||||
i=i, name=self.outputs['names'][i]
|
||||
)
|
||||
)
|
||||
else:
|
||||
self.vector_output_size_assertion_check()
|
||||
else:
|
||||
raise ValueError(
|
||||
f"{self.api} : Output error: the output should not be empty."
|
||||
)
|
||||
|
||||
return output_creation_code
|
||||
|
||||
def generate_bw_infer_local_shape_code(self, need_kernel=False):
|
||||
arg_name = self.infer_meta['local_shape']
|
||||
assert arg_name in self.outputs['names'], (
|
||||
f"Auto Parallel will calculate local_shape for {arg_name} "
|
||||
f"in {self.api}, but {arg_name} is not found in its outputs."
|
||||
)
|
||||
|
||||
_, fw_inputs, fw_attrs, fw_outputs = self.parse_forward_config(
|
||||
self.forward_config
|
||||
)
|
||||
# shape_type = self.attrs['attr_info'][shape_name][0]
|
||||
# out_name = self.dist_output_args[0]
|
||||
dist_out_name = self.dist_output_args[
|
||||
self.outputs['names'].index(arg_name)
|
||||
]
|
||||
shape_type = self.get_shape_type(fw_attrs['attr_info'])
|
||||
return_code = dist_api_gen.CALCULATE_LOCAL_SHAPE_TEMPLATE.format(
|
||||
out_name=dist_out_name,
|
||||
out_dist_attr=(
|
||||
"PADDLE_GET_CONST(phi::distributed::TensorDistAttr, spmd_info.second[0]);"
|
||||
if self.infer_meta['spmd_rule']
|
||||
else f"phi::distributed::TensorDistAttr(common::vectorize({dist_out_name}->dims()))"
|
||||
),
|
||||
dtype=shape_type,
|
||||
op_name=self.kernel['func'][0],
|
||||
)
|
||||
if need_kernel:
|
||||
return (
|
||||
dist_api_gen.CALCULATE_LOCAL_SHAPE_KERNEL_TEMPLATE.format(
|
||||
out_grad_dist_attr=(
|
||||
"PADDLE_GET_CONST(phi::distributed::TensorDistAttr, spmd_info.first[1]);"
|
||||
if self.infer_meta['spmd_rule']
|
||||
else "phi::distributed::TensorDistAttr(common::vectorize(out_grad.dims()))"
|
||||
),
|
||||
dtype=shape_type,
|
||||
op_name=self.kernel['func'][0],
|
||||
)
|
||||
+ return_code
|
||||
)
|
||||
return return_code
|
||||
|
||||
def generate_infer_meta_code(self) -> str:
|
||||
(
|
||||
infer_meta_func_code,
|
||||
input_args_code,
|
||||
output_decl_code,
|
||||
output_args_code,
|
||||
) = self.generate_infer_meta_func_and_args_code()
|
||||
|
||||
infer_meta_code = ""
|
||||
|
||||
if self.infer_meta['global_shape'] is not None:
|
||||
for i, out_name in enumerate(self.outputs['names']):
|
||||
if out_name == self.infer_meta[
|
||||
'global_shape'
|
||||
] and self.need_to_generate_code_for_inplace_impl(i):
|
||||
infer_meta_code += dist_api_gen.SET_DIMS_TEMPLATE.format(
|
||||
dst=self.dist_output_args[i],
|
||||
src=(
|
||||
self.dist_output_args[i] + '_tmp'
|
||||
if i > 0
|
||||
else self.dist_output_args[i]
|
||||
),
|
||||
)
|
||||
|
||||
infer_meta_code = (
|
||||
infer_meta_code
|
||||
+ dist_api_gen.INFER_META_TEMPLATE.format(
|
||||
infer_meta_func_code, input_args_code, output_args_code
|
||||
)
|
||||
)
|
||||
# TODO(GhostScreaming): kernel like reshape need calculate local_shape
|
||||
if self.infer_meta['local_shape'] is not None:
|
||||
if (
|
||||
self.kernel['param'] is not None
|
||||
and self.infer_meta['local_shape'] not in self.kernel['param']
|
||||
):
|
||||
infer_meta_code += self.generate_bw_infer_local_shape_code()
|
||||
else:
|
||||
infer_meta_code += self.generate_bw_infer_local_shape_code(
|
||||
need_kernel=True
|
||||
)
|
||||
infer_meta_code += SET_LOCAL_SHAPE_TEMPLATE.format(
|
||||
meta_tensor="meta_" + self.dense_output_args[0]
|
||||
)
|
||||
|
||||
return output_decl_code + infer_meta_code
|
||||
|
||||
# override DistForwardAPI's method
|
||||
def generate_return_code(self) -> str:
|
||||
return "return;"
|
||||
|
||||
# override BaseAPI's method
|
||||
def get_api_func_name(self):
|
||||
return self.api
|
||||
|
||||
# override BaseAPI's method
|
||||
# The method lookup order are: (DistBackwardAPI.__mro__)
|
||||
# <class '__main__.DistBackwardAPI'>,
|
||||
# <class 'dist_api_gen.DistForwardAPI'>,
|
||||
# <class 'api_gen.ForwardAPI'>,
|
||||
# <class 'backward_api_gen.BackwardAPI'>,
|
||||
# <class 'api_base.BaseAPI'>,
|
||||
# <class 'object'>
|
||||
# if don't override it, the ForwardAPI's gene_output will be called
|
||||
def gene_output(
|
||||
self,
|
||||
out_dtype_list,
|
||||
out_tensor_type_list=None,
|
||||
code_indent='',
|
||||
inplace_flag=False,
|
||||
):
|
||||
return BackwardAPI.gene_output(
|
||||
self,
|
||||
out_dtype_list,
|
||||
out_tensor_type_list,
|
||||
code_indent,
|
||||
inplace_flag,
|
||||
)
|
||||
|
||||
# override BaseAPI's method
|
||||
def get_return_type(self, inplace_flag=False):
|
||||
return BackwardAPI.get_return_type(self)
|
||||
|
||||
# override BaseAPI's method
|
||||
def gene_return_code(self):
|
||||
return ""
|
||||
|
||||
# override BaseAPI's method
|
||||
def gene_api_declaration(
|
||||
self, grad_flag=False, append_predefined_out=False
|
||||
) -> str:
|
||||
return BackwardAPI.gene_api_declaration(
|
||||
self, grad_flag=grad_flag, append_predefined_out=not grad_flag
|
||||
)
|
||||
|
||||
def generate_reshard_output_code(self):
|
||||
reshard_output_code = ""
|
||||
if self.generate_infer_spmd is True:
|
||||
output_num = len(self.outputs['types'])
|
||||
if output_num == 1:
|
||||
if self.outputs['types'][0] == 'Tensor':
|
||||
reshard_output_code += (
|
||||
RESHARD_SINGLE_OUTPUT_TEMPLATE.format(
|
||||
self.outputs['names'][0], self.outputs['names'][0]
|
||||
)
|
||||
)
|
||||
elif self.outputs['types'][0] == 'std::vector<Tensor>':
|
||||
reshard_output_code += (
|
||||
RESHARD_VECTOR_OUTPUT_TEMPLATE.format(
|
||||
self.outputs['names'][0], self.outputs['names'][0]
|
||||
)
|
||||
)
|
||||
else:
|
||||
self.vector_output_size_assertion_check()
|
||||
elif output_num > 1:
|
||||
for i, out_type in enumerate(self.outputs['types']):
|
||||
if out_type == 'Tensor':
|
||||
reshard_output_code += (
|
||||
RESHARD_MULTI_SINGLE_OUTPUT_TEMPLATE.format(
|
||||
i,
|
||||
self.outputs['names'][i],
|
||||
self.outputs['names'][i],
|
||||
)
|
||||
)
|
||||
else:
|
||||
self.vector_output_size_assertion_check()
|
||||
else:
|
||||
raise ValueError(
|
||||
f"{self.api} : Output error: the output should not be empty."
|
||||
)
|
||||
else:
|
||||
reshard_output_code += NONEED_TO_RESHARD_OUTPUT_TEMPLATE.format(
|
||||
self.kernel['func'][0]
|
||||
)
|
||||
# do nothing
|
||||
pass
|
||||
|
||||
return reshard_output_code
|
||||
|
||||
def generate_auto_parallel_branch(self) -> str:
|
||||
# if no tensor input, do not generate auto parallel branch
|
||||
if len(self.inputs['names']) == 0:
|
||||
return ""
|
||||
infer_spmd_code = self.generate_infer_spmd_code()
|
||||
output_creation_code = self.generate_output_creation_code()
|
||||
infer_global_shape_code = self.generate_infer_global_shape_code()
|
||||
output_dist_attr_setting = self.generate_output_dist_attr_setting()
|
||||
kernel_selection_code = self.generate_kernel_selection_code()
|
||||
reshard_input_code = self.generate_reshard_input_code()
|
||||
(
|
||||
prepare_data_code,
|
||||
input_name_tensor_map,
|
||||
) = self.generate_prepare_data_code()
|
||||
record_op_info_supplement_code = (
|
||||
self.generate_record_op_info_supplement(
|
||||
input_name_tensor_map, ' ', True
|
||||
)
|
||||
)
|
||||
infer_meta_code = self.generate_infer_meta_code()
|
||||
kernel_call_code = self.generate_kernel_call_code(is_forward=False)
|
||||
fallback_code = self.generate_fallback_code()
|
||||
reshard_output_code = self.generate_reshard_output_code()
|
||||
return_code = self.generate_return_code()
|
||||
|
||||
return MAIN_DIST_BRANCH_TEMPLATE.format(
|
||||
infer_spmd_code,
|
||||
output_creation_code,
|
||||
infer_global_shape_code,
|
||||
output_dist_attr_setting,
|
||||
kernel_selection_code,
|
||||
reshard_input_code,
|
||||
prepare_data_code,
|
||||
record_op_info_supplement_code,
|
||||
infer_meta_code,
|
||||
kernel_call_code,
|
||||
fallback_code,
|
||||
reshard_output_code,
|
||||
return_code,
|
||||
)
|
||||
|
||||
|
||||
def header_include():
|
||||
return """
|
||||
#include <tuple>
|
||||
|
||||
#include "paddle/phi/api/include/tensor.h"
|
||||
#include "paddle/phi/common/scalar.h"
|
||||
#include "paddle/phi/common/int_array.h"
|
||||
#include "paddle/utils/optional.h"
|
||||
"""
|
||||
|
||||
|
||||
def source_include(header_file_path, fw_header_file_path):
|
||||
return f"""
|
||||
#include "{header_file_path}"
|
||||
#include <memory>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "paddle/common/flags.h"
|
||||
|
||||
#include "paddle/phi/api/lib/api_custom_impl.h"
|
||||
#include "paddle/phi/api/lib/api_gen_utils.h"
|
||||
#include "paddle/phi/api/lib/data_transform.h"
|
||||
#include "paddle/phi/api/lib/kernel_dispatch.h"
|
||||
#include "paddle/phi/common/type_traits.h"
|
||||
#include "paddle/phi/core/kernel_registry.h"
|
||||
#include "{fw_header_file_path}"
|
||||
#include "paddle/phi/infermeta/backward.h"
|
||||
#include "paddle/phi/infermeta/unary.h"
|
||||
#include "paddle/phi/infermeta/fusion.h"
|
||||
|
||||
#include "paddle/phi/api/profiler/event_tracing.h"
|
||||
#include "paddle/phi/api/profiler/supplement_tracing.h"
|
||||
|
||||
#if defined(PADDLE_WITH_NCCL) || defined(PADDLE_WITH_RCCL)
|
||||
#include "paddle/phi/core/distributed/comm_context_manager.h"
|
||||
#include "paddle/phi/core/distributed/nccl_comm_context.h"
|
||||
#elif defined(PADDLE_WITH_XPU_BKCL)
|
||||
#include "paddle/phi/core/distributed/comm_context_manager.h"
|
||||
#include "paddle/phi/core/distributed/bkcl_comm_context.h"
|
||||
#elif defined(PADDLE_WITH_CUSTOM_DEVICE)
|
||||
#include "paddle/phi/core/distributed/comm_context_manager.h"
|
||||
#include "paddle/phi/core/distributed/xccl_comm_context.h"
|
||||
#endif
|
||||
|
||||
#ifdef PADDLE_WITH_DISTRIBUTE
|
||||
#include "paddle/phi/core/distributed/store/store_utils.h"
|
||||
#include "paddle/phi/infermeta/spmd_rules/rules.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_utils.h"
|
||||
#endif
|
||||
|
||||
PD_DECLARE_bool(conv2d_disable_cudnn);
|
||||
COMMON_DECLARE_int32(low_precision_op_list);
|
||||
COMMON_DECLARE_bool(benchmark);
|
||||
"""
|
||||
|
||||
|
||||
def backward_api_namespace():
|
||||
return (
|
||||
"""
|
||||
namespace paddle {
|
||||
namespace experimental {
|
||||
|
||||
""",
|
||||
"""
|
||||
|
||||
} // namespace experimental
|
||||
} // namespace paddle
|
||||
""",
|
||||
)
|
||||
|
||||
|
||||
def generate_backward_api(
|
||||
backward_yaml_path,
|
||||
is_fused_backward_yaml,
|
||||
header_file_path,
|
||||
source_file_path,
|
||||
):
|
||||
bw_apis = []
|
||||
for each_api_yaml in backward_yaml_path:
|
||||
with open(each_api_yaml, 'r') as f:
|
||||
api_list = yaml.load(f, Loader=yaml.FullLoader)
|
||||
if api_list:
|
||||
bw_apis.extend(api_list)
|
||||
|
||||
header_file = open(header_file_path, 'w')
|
||||
source_file = open(source_file_path, 'w')
|
||||
|
||||
namespace = backward_api_namespace()
|
||||
|
||||
header_file.write("#pragma once\n")
|
||||
header_file.write(header_include())
|
||||
header_file.write(namespace[0])
|
||||
|
||||
include_header_file = (
|
||||
"paddle/phi/api/backward/fused_backward_api_base.h"
|
||||
if is_fused_backward_yaml
|
||||
else "paddle/phi/api/backward/backward_api_base.h"
|
||||
)
|
||||
include_fw_header_file = (
|
||||
"paddle/phi/api/include/fused_api.h"
|
||||
if is_fused_backward_yaml
|
||||
else "paddle/phi/api/include/api.h"
|
||||
)
|
||||
source_file.write(
|
||||
source_include(include_header_file, include_fw_header_file)
|
||||
)
|
||||
source_file.write(namespace[0])
|
||||
# not all fused ops support dygraph
|
||||
if is_fused_backward_yaml is True:
|
||||
new_bw_apis = [
|
||||
bw_api
|
||||
for bw_api in bw_apis
|
||||
if "support_dygraph_mode" in bw_api
|
||||
and bw_api["support_dygraph_mode"] is True
|
||||
]
|
||||
bw_apis = new_bw_apis
|
||||
|
||||
for bw_api in bw_apis:
|
||||
dist_bw_api = DistBackwardAPI(bw_api)
|
||||
header_file.write(dist_bw_api.gene_api_declaration())
|
||||
if is_fused_backward_yaml is True:
|
||||
source_file.write(dist_bw_api.gene_api_code())
|
||||
else:
|
||||
source_file.write(dist_bw_api.gene_api_code())
|
||||
|
||||
header_file.write(namespace[1])
|
||||
source_file.write(namespace[1])
|
||||
|
||||
header_file.close()
|
||||
source_file.close()
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Generate PaddlePaddle C++ backward API files'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--backward_yaml_path',
|
||||
help='path to backward yaml file',
|
||||
nargs='+',
|
||||
default=['paddle/phi/ops/yaml/backward.yaml'],
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--is_fused_backward_yaml',
|
||||
help='flag of fused backward yaml',
|
||||
action='store_true',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--backward_header_path',
|
||||
help='output of generated backward header code file',
|
||||
default='paddle/phi/api/backward/backward_api_base.h',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--backward_source_path',
|
||||
help='output of generated backward source code file',
|
||||
default='paddle/phi/api/lib/backward_api_base.cc',
|
||||
)
|
||||
|
||||
options = parser.parse_args()
|
||||
|
||||
backward_yaml_path = options.backward_yaml_path
|
||||
is_fused_backward_yaml = options.is_fused_backward_yaml
|
||||
header_file_path = options.backward_header_path
|
||||
source_file_path = options.backward_source_path
|
||||
|
||||
generate_backward_api(
|
||||
backward_yaml_path,
|
||||
is_fused_backward_yaml,
|
||||
header_file_path,
|
||||
source_file_path,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,214 @@
|
||||
# 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.
|
||||
|
||||
import argparse
|
||||
|
||||
import yaml
|
||||
from api_gen import ForwardAPI, backward_api_black_list
|
||||
from dist_api_gen import DistForwardAPI
|
||||
from sparse_api_gen import SparseAPI
|
||||
|
||||
|
||||
def header_include():
|
||||
return """
|
||||
#include <tuple>
|
||||
|
||||
#include "paddle/phi/api/include/tensor.h"
|
||||
#include "paddle/phi/common/scalar.h"
|
||||
#include "paddle/phi/common/int_array.h"
|
||||
#include "paddle/utils/optional.h"
|
||||
"""
|
||||
|
||||
|
||||
def source_include(header_file_path):
|
||||
return f"""#include "{header_file_path}"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "paddle/common/flags.h"
|
||||
|
||||
#include "paddle/phi/api/lib/api_custom_impl.h"
|
||||
#include "paddle/phi/api/lib/api_gen_utils.h"
|
||||
#include "paddle/phi/api/lib/data_transform.h"
|
||||
#include "paddle/phi/api/lib/kernel_dispatch.h"
|
||||
#include "paddle/phi/core/kernel_registry.h"
|
||||
#include "paddle/phi/infermeta/binary.h"
|
||||
#include "paddle/phi/infermeta/multiary.h"
|
||||
#include "paddle/phi/infermeta/nullary.h"
|
||||
#include "paddle/phi/infermeta/unary.h"
|
||||
#include "paddle/phi/infermeta/ternary.h"
|
||||
|
||||
#include "paddle/phi/infermeta/sparse/unary.h"
|
||||
#include "paddle/phi/infermeta/sparse/binary.h"
|
||||
#include "paddle/phi/infermeta/sparse/multiary.h"
|
||||
|
||||
#include "paddle/phi/api/profiler/event_tracing.h"
|
||||
#include "paddle/phi/api/profiler/supplement_tracing.h"
|
||||
|
||||
#ifdef PADDLE_WITH_DISTRIBUTE
|
||||
#include "paddle/phi/infermeta/spmd_rules/rules.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_utils.h"
|
||||
#endif
|
||||
|
||||
COMMON_DECLARE_int32(low_precision_op_list);
|
||||
COMMON_DECLARE_bool(benchmark);
|
||||
"""
|
||||
|
||||
|
||||
def api_namespace():
|
||||
return (
|
||||
"""
|
||||
namespace paddle {
|
||||
namespace experimental {
|
||||
|
||||
""",
|
||||
"""
|
||||
|
||||
} // namespace experimental
|
||||
} // namespace paddle
|
||||
""",
|
||||
)
|
||||
|
||||
|
||||
def sparse_namespace():
|
||||
return (
|
||||
"""
|
||||
namespace sparse {
|
||||
""",
|
||||
"""
|
||||
} // namespace sparse
|
||||
""",
|
||||
)
|
||||
|
||||
|
||||
def generate_intermediate_api(
|
||||
api_yaml_path,
|
||||
sparse_api_yaml_path,
|
||||
dygraph_header_file_path,
|
||||
dygraph_source_file_path,
|
||||
gen_dist_branch,
|
||||
):
|
||||
dygraph_header_file = open(dygraph_header_file_path, 'w')
|
||||
dygraph_source_file = open(dygraph_source_file_path, 'w')
|
||||
|
||||
namespace = api_namespace()
|
||||
sparse_namespace_pair = sparse_namespace()
|
||||
|
||||
dygraph_header_file.write("#pragma once\n")
|
||||
dygraph_header_file.write(header_include())
|
||||
dygraph_header_file.write(namespace[0])
|
||||
|
||||
dygraph_include_header_file = "paddle/phi/api/lib/dygraph_api.h"
|
||||
dygraph_source_file.write(source_include(dygraph_include_header_file))
|
||||
dygraph_source_file.write(namespace[0])
|
||||
|
||||
apis = []
|
||||
for each_api_yaml in api_yaml_path:
|
||||
with open(each_api_yaml, 'r') as f:
|
||||
api_list = yaml.load(f, Loader=yaml.FullLoader)
|
||||
if api_list:
|
||||
apis.extend(api_list)
|
||||
|
||||
for api in apis:
|
||||
forward_api = (
|
||||
DistForwardAPI(api) if gen_dist_branch else ForwardAPI(api)
|
||||
)
|
||||
if forward_api.is_dygraph_api:
|
||||
dygraph_header_file.write(forward_api.gene_api_declaration())
|
||||
dygraph_source_file.write(forward_api.gene_api_code())
|
||||
|
||||
dygraph_header_file.write(sparse_namespace_pair[0])
|
||||
dygraph_source_file.write(sparse_namespace_pair[0])
|
||||
sparse_apis = []
|
||||
for each_sparse_api_yaml in sparse_api_yaml_path:
|
||||
with open(each_sparse_api_yaml, 'r') as f:
|
||||
sparse_api_list = yaml.load(f, Loader=yaml.FullLoader)
|
||||
if sparse_api_list:
|
||||
sparse_apis.extend(sparse_api_list)
|
||||
|
||||
for api in sparse_apis:
|
||||
sparse_api = SparseAPI(api)
|
||||
if sparse_api.api in backward_api_black_list:
|
||||
continue
|
||||
if sparse_api.is_dygraph_api:
|
||||
dygraph_header_file.write(sparse_api.gene_api_declaration())
|
||||
dygraph_source_file.write(sparse_api.gene_api_code())
|
||||
|
||||
dygraph_header_file.write(sparse_namespace_pair[1])
|
||||
dygraph_header_file.write(namespace[1])
|
||||
|
||||
dygraph_source_file.write(sparse_namespace_pair[1])
|
||||
dygraph_source_file.write(namespace[1])
|
||||
|
||||
dygraph_header_file.close()
|
||||
dygraph_source_file.close()
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Generate PaddlePaddle C++ Sparse API files'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--api_yaml_path',
|
||||
nargs='+',
|
||||
help='path to api yaml file',
|
||||
default=['paddle/phi/ops/yaml/ops.yaml'],
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--sparse_api_yaml_path',
|
||||
nargs='+',
|
||||
help='path to sparse api yaml file',
|
||||
default='paddle/phi/ops/yaml/sparse_ops.yaml',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--dygraph_api_header_path',
|
||||
help='output of generated dygraph api header code file',
|
||||
default='paddle/phi/api/lib/dygraph_api.h',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--dygraph_api_source_path',
|
||||
help='output of generated dygraph api source code file',
|
||||
default='paddle/phi/api/lib/dygraph_api.cc',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--gen_dist_branch',
|
||||
help='whether generate distributed branch code',
|
||||
dest='gen_dist_branch',
|
||||
action='store_true',
|
||||
)
|
||||
|
||||
options = parser.parse_args()
|
||||
|
||||
api_yaml_path = options.api_yaml_path
|
||||
sparse_api_yaml_path = options.sparse_api_yaml_path
|
||||
dygraph_header_file_path = options.dygraph_api_header_path
|
||||
dygraph_source_file_path = options.dygraph_api_source_path
|
||||
gen_dist_branch = options.gen_dist_branch
|
||||
|
||||
generate_intermediate_api(
|
||||
api_yaml_path,
|
||||
sparse_api_yaml_path,
|
||||
dygraph_header_file_path,
|
||||
dygraph_source_file_path,
|
||||
gen_dist_branch,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,584 @@
|
||||
# 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.
|
||||
|
||||
import argparse
|
||||
|
||||
import yaml
|
||||
from api_base import PREFIX_TENSOR_NAME
|
||||
from api_gen import ForwardAPI, backward_api_black_list
|
||||
|
||||
|
||||
class SparseAPI(ForwardAPI):
|
||||
def __init__(self, api_item_yaml):
|
||||
super().__init__(api_item_yaml)
|
||||
|
||||
def gene_api_declaration(
|
||||
self, grad_flag=False, append_predefined_out=False
|
||||
):
|
||||
return f"""
|
||||
// {", ".join(self.outputs['names'])}
|
||||
{super().gene_api_declaration(append_predefined_out=False)}
|
||||
"""
|
||||
|
||||
def gene_output(
|
||||
self,
|
||||
out_dtype_list,
|
||||
out_tensor_type_list=None,
|
||||
code_indent='',
|
||||
inplace_flag=False,
|
||||
):
|
||||
kernel_output = []
|
||||
output_names = []
|
||||
output_create = ""
|
||||
return_type = self.get_return_type_with_intermediate(inplace_flag)
|
||||
output_type_map = {
|
||||
'dense': 'TensorType::DENSE_TENSOR',
|
||||
'sparse_coo': 'TensorType::SPARSE_COO',
|
||||
'sparse_csr': 'TensorType::SPARSE_CSR',
|
||||
}
|
||||
|
||||
if len(out_dtype_list) == 1:
|
||||
kernel_output.append('kernel_out')
|
||||
output_names.append('kernel_out')
|
||||
inplace_assign = (
|
||||
" = " + self.inplace_map[self.outputs['names'][0]]
|
||||
if inplace_flag
|
||||
and self.inplace_map is not None
|
||||
and self.outputs['names'][0] in self.inplace_map
|
||||
else ""
|
||||
)
|
||||
output_create = f"""
|
||||
{return_type} api_output{inplace_assign};
|
||||
auto* kernel_out = SetSparseKernelOutput(&api_output, {output_type_map[out_dtype_list[0]]});"""
|
||||
|
||||
elif len(out_dtype_list) > 1:
|
||||
output_create = f"""
|
||||
{return_type} api_output;"""
|
||||
|
||||
if inplace_flag:
|
||||
output_create = f"""
|
||||
{return_type} api_output{{"""
|
||||
|
||||
for out_name in self.outputs['names']:
|
||||
if out_name in self.inplace_map:
|
||||
output_create = (
|
||||
output_create + self.inplace_map[out_name] + ', '
|
||||
)
|
||||
else:
|
||||
output_create += 'Tensor(), '
|
||||
output_create = output_create[:-2] + '};'
|
||||
|
||||
for i in range(len(out_dtype_list)):
|
||||
kernel_output.append(f'kernel_out_{i}')
|
||||
output_names.append(f'kernel_out_{i}')
|
||||
output_create = (
|
||||
output_create
|
||||
+ f"""
|
||||
auto* kernel_out_{i} = SetSparseKernelOutput(&std::get<{i}>(api_output), {output_type_map[out_dtype_list[i]]});"""
|
||||
)
|
||||
|
||||
else:
|
||||
raise ValueError(
|
||||
f"{self.api} : Output error: the output should not be empty."
|
||||
)
|
||||
|
||||
return kernel_output, output_names, output_create
|
||||
|
||||
def gen_sparse_kernel_context(self, kernel_output_names):
|
||||
input_trans_map = {
|
||||
'const Tensor&': 'const phi::TenseBase&',
|
||||
'const std::vector<Tensor>&': 'const std::vector<phi::TenseBase>&',
|
||||
'const paddle::optional<Tensor>&': 'paddle::optional<const phi::TenseBase&>',
|
||||
}
|
||||
out_trans_map = {
|
||||
'Tensor': 'phi::TenseBase*',
|
||||
'std::vector<Tensor>': 'std::vector<phi::TenseBase*>',
|
||||
}
|
||||
input_names = self.inputs['names']
|
||||
input_infos = self.inputs['input_info']
|
||||
input_types = self.inputs['tensor_type']
|
||||
|
||||
tensor_type_map = {
|
||||
'dense': 'phi::DenseTensor',
|
||||
'sparse_coo': 'phi::SparseCooTensor',
|
||||
'sparse_csr': 'phi::SparseCsrTensor',
|
||||
}
|
||||
inputsname2tensortype = {}
|
||||
for i in range(len(input_names)):
|
||||
inputsname2tensortype[input_names[i]] = input_types[i]
|
||||
|
||||
attr_names = self.attrs['names']
|
||||
kernel_param = self.kernel['param']
|
||||
if kernel_param is None:
|
||||
kernel_param = input_names + attr_names
|
||||
|
||||
infer_meta = self.infer_meta
|
||||
|
||||
infer_meta_params = (
|
||||
infer_meta['param']
|
||||
if infer_meta['param'] is not None
|
||||
else input_names + attr_names
|
||||
)
|
||||
|
||||
kernel_context_code = ""
|
||||
for param in kernel_param:
|
||||
if param in input_names and param not in infer_meta_params:
|
||||
var_name = " auto " + PREFIX_TENSOR_NAME + param + " = "
|
||||
if self.inputs['input_info'][param] == "const Tensor&":
|
||||
if inputsname2tensortype[param] == "sparse_coo":
|
||||
kernel_context_code = (
|
||||
kernel_context_code
|
||||
+ var_name
|
||||
+ "PrepareDataForSparseCooTensor("
|
||||
+ param
|
||||
+ ");\n"
|
||||
)
|
||||
elif inputsname2tensortype[param] == "sparse_csr":
|
||||
kernel_context_code = (
|
||||
kernel_context_code
|
||||
+ var_name
|
||||
+ "PrepareDataForSparseCsrTensor("
|
||||
+ param
|
||||
+ ");\n"
|
||||
)
|
||||
else:
|
||||
kernel_context_code = (
|
||||
kernel_context_code
|
||||
+ var_name
|
||||
+ "PrepareDataForDenseTensorInSparse("
|
||||
+ param
|
||||
+ ");\n"
|
||||
)
|
||||
elif param in self.optional_vars:
|
||||
tensor_type = 'phi::DenseTensor'
|
||||
for name, input_type in zip(input_names, input_types):
|
||||
if param == name:
|
||||
tensor_type = tensor_type_map[input_type]
|
||||
break
|
||||
optional_var = "paddle::optional<" + tensor_type + ">("
|
||||
if inputsname2tensortype[param] == "sparse_coo":
|
||||
kernel_context_code = (
|
||||
kernel_context_code
|
||||
+ var_name
|
||||
+ "PrepareDataForSparseCooTensor("
|
||||
+ param
|
||||
+ ");\n"
|
||||
)
|
||||
elif inputsname2tensortype[param] == "sparse_csr":
|
||||
kernel_context_code = (
|
||||
kernel_context_code
|
||||
+ var_name
|
||||
+ "PrepareDataForSparseCsrTensor("
|
||||
+ param
|
||||
+ ");\n"
|
||||
)
|
||||
else:
|
||||
kernel_context_code = (
|
||||
kernel_context_code
|
||||
+ var_name
|
||||
+ "PrepareDataForDenseTensorInSparse("
|
||||
+ param
|
||||
+ ");\n"
|
||||
)
|
||||
|
||||
for param in kernel_param:
|
||||
if param in input_names:
|
||||
if param in self.optional_vars:
|
||||
kernel_context_code = (
|
||||
kernel_context_code
|
||||
+ f"""
|
||||
kernel_context.EmplaceBackInput({param} ? &(*{PREFIX_TENSOR_NAME}{param}) : nullptr);"""
|
||||
)
|
||||
else:
|
||||
kernel_context_code = (
|
||||
kernel_context_code
|
||||
+ f"""
|
||||
kernel_context.EmplaceBackInput({PREFIX_TENSOR_NAME}{param}.get());"""
|
||||
)
|
||||
|
||||
continue
|
||||
if param in attr_names:
|
||||
# set attr for kernel_context
|
||||
if 'IntArray' in self.attrs['attr_info'][param][0]:
|
||||
param = 'phi::IntArray(' + param + ')'
|
||||
elif 'Scalar' in self.attrs['attr_info'][param][0]:
|
||||
param = 'phi::Scalar(' + param + ')'
|
||||
elif isinstance(param, bool):
|
||||
param = str(param).lower()
|
||||
else:
|
||||
param + str(param) + ", "
|
||||
kernel_context_code = (
|
||||
kernel_context_code
|
||||
+ f"""
|
||||
kernel_context.EmplaceBackAttr({param});"""
|
||||
)
|
||||
|
||||
for out_name in kernel_output_names:
|
||||
kernel_context_code = (
|
||||
kernel_context_code
|
||||
+ f"""
|
||||
kernel_context.EmplaceBackOutput({out_name});"""
|
||||
)
|
||||
|
||||
return kernel_context_code
|
||||
|
||||
def prepare_input(self):
|
||||
input_names = self.inputs['names']
|
||||
input_types = self.inputs['tensor_type']
|
||||
attr_names = self.attrs['names']
|
||||
infer_meta = self.infer_meta
|
||||
|
||||
infer_meta_params = (
|
||||
infer_meta['param']
|
||||
if infer_meta['param'] is not None
|
||||
else input_names + attr_names
|
||||
)
|
||||
|
||||
inputsname2tensortype = {}
|
||||
for i in range(len(input_names)):
|
||||
inputsname2tensortype[input_names[i]] = input_types[i]
|
||||
|
||||
create_input_var_code = ""
|
||||
tensor_type_map = {
|
||||
'dense': 'phi::DenseTensor',
|
||||
'sparse_coo': 'phi::SparseCooTensor',
|
||||
'sparse_csr': 'phi::SparseCsrTensor',
|
||||
}
|
||||
for param in infer_meta_params:
|
||||
if param in input_names:
|
||||
var_name = " auto " + PREFIX_TENSOR_NAME + param + " = "
|
||||
if self.inputs['input_info'][param] == "const Tensor&":
|
||||
if inputsname2tensortype[param] == "sparse_coo":
|
||||
create_input_var_code = (
|
||||
create_input_var_code
|
||||
+ var_name
|
||||
+ "PrepareDataForSparseCooTensor("
|
||||
+ param
|
||||
+ ");\n"
|
||||
)
|
||||
elif inputsname2tensortype[param] == "sparse_csr":
|
||||
create_input_var_code = (
|
||||
create_input_var_code
|
||||
+ var_name
|
||||
+ "PrepareDataForSparseCsrTensor("
|
||||
+ param
|
||||
+ ");\n"
|
||||
)
|
||||
else:
|
||||
create_input_var_code = (
|
||||
create_input_var_code
|
||||
+ var_name
|
||||
+ "PrepareDataForDenseTensorInSparse("
|
||||
+ param
|
||||
+ ");\n"
|
||||
)
|
||||
elif param in self.optional_vars:
|
||||
tensor_type = 'phi::DenseTensor'
|
||||
for name, input_type in zip(input_names, input_types):
|
||||
if param == name:
|
||||
tensor_type = tensor_type_map[input_type]
|
||||
break
|
||||
optional_var = "paddle::optional<" + tensor_type + ">("
|
||||
if inputsname2tensortype[param] == "sparse_coo":
|
||||
create_input_var_code = (
|
||||
create_input_var_code
|
||||
+ var_name
|
||||
+ "PrepareDataForSparseCooTensor("
|
||||
+ param
|
||||
+ ");\n"
|
||||
)
|
||||
elif inputsname2tensortype[param] == "sparse_csr":
|
||||
create_input_var_code = (
|
||||
create_input_var_code
|
||||
+ var_name
|
||||
+ "PrepareDataForSparseCsrTensor("
|
||||
+ param
|
||||
+ ");\n"
|
||||
)
|
||||
else:
|
||||
create_input_var_code = (
|
||||
create_input_var_code
|
||||
+ var_name
|
||||
+ "PrepareDataForDenseTensorInSparse("
|
||||
+ param
|
||||
+ ");\n"
|
||||
)
|
||||
return f"""{create_input_var_code}"""
|
||||
|
||||
def gen_sparse_kernel_code(self, kernel_name, inplace_flag=False):
|
||||
_, kernel_output_names, output_create = self.gene_output(
|
||||
self.kernel['dispatch'][kernel_name][1], None, '', inplace_flag
|
||||
)
|
||||
|
||||
kernel_context_code = self.gen_sparse_kernel_context(
|
||||
kernel_output_names
|
||||
)
|
||||
return_code = (
|
||||
""
|
||||
if len(self.gene_return_code()) == 0
|
||||
else " " + self.gene_return_code()
|
||||
)
|
||||
return f"""
|
||||
VLOG(6) << "{self.api} api sparse kernel key: [" << kernel_backend << ", " << kernel_layout << ", "<< kernel_data_type << "]";
|
||||
auto kernel_result = phi::KernelFactory::Instance().SelectKernelOrThrowError(
|
||||
"{kernel_name}", {{kernel_backend, kernel_layout, kernel_data_type}});
|
||||
const auto& phi_kernel = kernel_result.kernel;
|
||||
if (FLAGS_low_precision_op_list) {{
|
||||
phi::KernelFactory::Instance().AddToLowPrecisionKernelList("{self.api}", kernel_data_type);
|
||||
}}
|
||||
VLOG(6) << "{self.api} api sparse kernel: " << phi_kernel;
|
||||
|
||||
auto* dev_ctx = GetDeviceContextByBackend(kernel_result.has_fallback_cpu ? Backend::CPU : kernel_backend);
|
||||
auto kernel_context = phi::KernelContext(dev_ctx);
|
||||
{output_create}
|
||||
{self.prepare_input()}
|
||||
{self.gene_infer_meta(kernel_output_names, '')}
|
||||
{kernel_context_code}
|
||||
phi_kernel(&kernel_context);
|
||||
if (FLAGS_benchmark) {{
|
||||
dev_ctx->Wait();
|
||||
std::cout << \"{self.api} kernel run finish.\" << std::endl;
|
||||
}}
|
||||
{return_code}"""
|
||||
|
||||
def get_condition_code(self, kernel_name):
|
||||
assert self.kernel['dispatch'][kernel_name], (
|
||||
f"{self.api} api: the tensor type of inputs and outputs for kernel isn't set, see also 'kernel:func' of 'conv3d' in sparse_ops.yaml."
|
||||
)
|
||||
input_types = self.kernel['dispatch'][kernel_name][0]
|
||||
sparse_type_map = {
|
||||
'sparse_coo': 'DataLayout::SPARSE_COO',
|
||||
'sparse_csr': 'DataLayout::SPARSE_CSR',
|
||||
}
|
||||
condition_list = []
|
||||
tensor_type_list = []
|
||||
for i, in_type in enumerate(input_types):
|
||||
if in_type == "dense":
|
||||
if self.inputs['names'][i] in self.optional_vars:
|
||||
condition_list.append(
|
||||
f"(!{self.inputs['names'][i]} || phi::DenseTensor::classof({self.inputs['names'][i]}->impl().get()))"
|
||||
)
|
||||
else:
|
||||
condition_list.append(
|
||||
f"phi::DenseTensor::classof({self.inputs['names'][i]}.impl().get())"
|
||||
)
|
||||
else:
|
||||
if in_type == 'sparse_coo':
|
||||
condition_list.append(
|
||||
f"{self.inputs['names'][i]}.is_sparse_coo_tensor()"
|
||||
)
|
||||
else:
|
||||
condition_list.append(
|
||||
f"{self.inputs['names'][i]}.is_sparse_csr_tensor()"
|
||||
)
|
||||
tensor_type_list.append(in_type)
|
||||
self.inputs['tensor_type'] = tensor_type_list
|
||||
|
||||
return " && ".join(condition_list)
|
||||
|
||||
def gene_dispatch_code(self, kernel_name, inplace_flag=False):
|
||||
return f"""
|
||||
if ({self.get_condition_code(kernel_name)}) {{
|
||||
{self.gen_sparse_kernel_code(kernel_name, inplace_flag)}
|
||||
}}
|
||||
"""
|
||||
|
||||
def gene_base_api_code(
|
||||
self, inplace_flag=False, grad_flag=False, append_predefined_out=False
|
||||
):
|
||||
api_func_name = self.get_api_func_name()
|
||||
if inplace_flag and api_func_name[-1] != '_':
|
||||
api_func_name += '_'
|
||||
kernel_dispatch_code = f"{self.gene_kernel_select()}\n"
|
||||
for kernel_name in self.kernel['func']:
|
||||
kernel_dispatch_code += self.gene_dispatch_code(
|
||||
kernel_name, inplace_flag
|
||||
)
|
||||
|
||||
return f"""
|
||||
PADDLE_API {self.get_return_type(inplace_flag)} {api_func_name}({self.get_define_args(inplace_flag, grad_flag=grad_flag, append_predefined_out=False)}) {{
|
||||
{kernel_dispatch_code}
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"The kernel of ({self.api}) for input tensors is unimplemented, please check the type of input tensors."));
|
||||
}}
|
||||
"""
|
||||
|
||||
|
||||
def header_include():
|
||||
return """
|
||||
#include <tuple>
|
||||
|
||||
#include "paddle/phi/api/include/tensor.h"
|
||||
#include "paddle/phi/common/scalar.h"
|
||||
#include "paddle/phi/common/int_array.h"
|
||||
#include "paddle/utils/optional.h"
|
||||
"""
|
||||
|
||||
|
||||
def source_include(header_file_path):
|
||||
return f"""
|
||||
#include "{header_file_path}"
|
||||
#include <memory>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "paddle/common/flags.h"
|
||||
|
||||
#include "paddle/phi/api/lib/api_gen_utils.h"
|
||||
#include "paddle/phi/api/lib/data_transform.h"
|
||||
#include "paddle/phi/api/lib/kernel_dispatch.h"
|
||||
#include "paddle/phi/core/kernel_registry.h"
|
||||
#include "paddle/phi/infermeta/unary.h"
|
||||
#include "paddle/phi/infermeta/binary.h"
|
||||
#include "paddle/phi/infermeta/ternary.h"
|
||||
#include "paddle/phi/infermeta/multiary.h"
|
||||
#include "paddle/phi/infermeta/backward.h"
|
||||
#include "paddle/utils/none.h"
|
||||
|
||||
#include "paddle/phi/infermeta/sparse/unary.h"
|
||||
#include "paddle/phi/infermeta/sparse/binary.h"
|
||||
#include "paddle/phi/infermeta/sparse/multiary.h"
|
||||
#include "paddle/phi/infermeta/sparse/backward.h"
|
||||
|
||||
COMMON_DECLARE_int32(low_precision_op_list);
|
||||
COMMON_DECLARE_bool(benchmark);
|
||||
"""
|
||||
|
||||
|
||||
def api_namespace():
|
||||
return (
|
||||
"""
|
||||
namespace paddle {
|
||||
namespace experimental {
|
||||
namespace sparse {
|
||||
|
||||
""",
|
||||
"""
|
||||
|
||||
} // namespace sparse
|
||||
} // namespace experimental
|
||||
} // namespace paddle
|
||||
""",
|
||||
)
|
||||
|
||||
|
||||
def generate_api(
|
||||
api_yaml_path, header_file_path, source_file_path, grad_flag=False
|
||||
):
|
||||
apis = []
|
||||
|
||||
for each_api_yaml in api_yaml_path:
|
||||
with open(each_api_yaml, 'r') as f:
|
||||
api_list = yaml.load(f, Loader=yaml.FullLoader)
|
||||
if api_list:
|
||||
apis.extend(api_list)
|
||||
header_file = open(header_file_path, 'w')
|
||||
source_file = open(source_file_path, 'w')
|
||||
|
||||
namespace = api_namespace()
|
||||
|
||||
header_file.write("#pragma once\n")
|
||||
header_file.write(header_include())
|
||||
header_file.write(namespace[0])
|
||||
|
||||
include_header_file = "paddle/phi/api/include/sparse_api.h"
|
||||
source_file.write(source_include(include_header_file))
|
||||
source_file.write(namespace[0])
|
||||
|
||||
for api in apis:
|
||||
sparse_api = SparseAPI(api)
|
||||
if sparse_api.api in backward_api_black_list:
|
||||
continue
|
||||
if sparse_api.is_dygraph_api:
|
||||
sparse_api.is_dygraph_api = False
|
||||
header_file.write(
|
||||
sparse_api.gene_api_declaration(
|
||||
grad_flag=grad_flag, append_predefined_out=False
|
||||
)
|
||||
)
|
||||
source_file.write(
|
||||
sparse_api.gene_api_code(
|
||||
grad_flag=grad_flag, append_predefined_out=False
|
||||
)
|
||||
)
|
||||
|
||||
header_file.write(namespace[1])
|
||||
source_file.write(namespace[1])
|
||||
|
||||
header_file.close()
|
||||
source_file.close()
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Generate PaddlePaddle C++ Sparse API files'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--api_yaml_path',
|
||||
help='path to sparse api yaml file',
|
||||
nargs='+',
|
||||
default='paddle/phi/ops/yaml/sparse_ops.yaml',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--api_header_path',
|
||||
help='output of generated api header code file',
|
||||
default='paddle/phi/api/include/sparse_api.h',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--api_source_path',
|
||||
help='output of generated api source code file',
|
||||
default='paddle/phi/api/lib/sparse_api.cc',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--backward_api_yaml_path',
|
||||
help='path to sparse api yaml file',
|
||||
nargs='+',
|
||||
default='paddle/phi/ops/yaml/sparse_backward_ops.yaml',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--backward_api_header_path',
|
||||
help='output of generated api header code file',
|
||||
default='paddle/phi/api/backward/sparse_backward_api.h',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--backward_api_source_path',
|
||||
help='output of generated api source code file',
|
||||
default='paddle/phi/api/lib/sparse_backward_api.cc',
|
||||
)
|
||||
|
||||
options = parser.parse_args()
|
||||
|
||||
api_yaml_path = options.api_yaml_path
|
||||
header_file_path = options.api_header_path
|
||||
source_file_path = options.api_source_path
|
||||
backward_api_yaml_path = options.backward_api_yaml_path
|
||||
backward_header_file_path = options.backward_api_header_path
|
||||
backward_source_file_path = options.backward_api_source_path
|
||||
generate_api(
|
||||
api_yaml_path, header_file_path, source_file_path, grad_flag=False
|
||||
)
|
||||
generate_api(
|
||||
backward_api_yaml_path,
|
||||
backward_header_file_path,
|
||||
backward_source_file_path,
|
||||
grad_flag=True,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,244 @@
|
||||
# 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.
|
||||
|
||||
import argparse
|
||||
|
||||
import yaml
|
||||
from backward_api_gen import BackwardAPI
|
||||
from sparse_api_gen import SparseAPI
|
||||
|
||||
|
||||
class SparseBackwardAPI(SparseAPI, BackwardAPI):
|
||||
def __init__(self, bw_api_item_yaml):
|
||||
BackwardAPI.__init__(self, bw_api_item_yaml)
|
||||
|
||||
def get_api_func_name(self):
|
||||
return self.api
|
||||
|
||||
def gene_kernel_backend_select(self):
|
||||
return BackwardAPI.gene_kernel_backend_select(self)
|
||||
|
||||
def get_return_type(self, inplace_flag=False):
|
||||
return BackwardAPI.get_return_type(self)
|
||||
|
||||
def gene_return_code(self):
|
||||
return "return;"
|
||||
|
||||
def gene_api_declaration(
|
||||
self, grad_flag=False, append_predefined_out=False
|
||||
):
|
||||
return SparseAPI.gene_api_declaration(
|
||||
self, grad_flag=grad_flag, append_predefined_out=False
|
||||
)
|
||||
|
||||
def get_declare_args(
|
||||
self, inplace_flag=False, grad_flag=False, append_predefined_out=False
|
||||
):
|
||||
return BackwardAPI.get_declare_args(
|
||||
self, grad_flag=grad_flag, append_predefined_out=False
|
||||
)
|
||||
|
||||
def get_define_args(
|
||||
self, inplace_flag=False, grad_flag=False, append_predefined_out=False
|
||||
):
|
||||
return BackwardAPI.get_define_args(
|
||||
self, grad_flag=grad_flag, append_predefined_out=False
|
||||
)
|
||||
|
||||
def gene_output(
|
||||
self,
|
||||
out_dtype_list,
|
||||
out_tensor_type_list=None,
|
||||
code_indent='',
|
||||
inplace_flag=False,
|
||||
):
|
||||
kernel_output = []
|
||||
output_names = []
|
||||
output_create = ""
|
||||
output_type_map = {
|
||||
'dense': 'TensorType::DENSE_TENSOR',
|
||||
'sparse_coo': 'TensorType::SPARSE_COO',
|
||||
'sparse_csr': 'TensorType::SPARSE_CSR',
|
||||
}
|
||||
|
||||
if len(out_dtype_list) == 1:
|
||||
kernel_output.append('kernel_out')
|
||||
output_names.append('kernel_out')
|
||||
inplace_assign = (
|
||||
" = " + self.inplace_map[self.outputs['names'][0]]
|
||||
if inplace_flag
|
||||
and self.inplace_map is not None
|
||||
and self.outputs['names'][0] in self.inplace_map
|
||||
else ""
|
||||
)
|
||||
output_create = f"""
|
||||
auto kernel_out = SetSparseKernelOutput({self.outputs['names'][0]}, {output_type_map[out_dtype_list[0]]});"""
|
||||
|
||||
elif len(out_dtype_list) > 1:
|
||||
output_create = ""
|
||||
|
||||
for i, out_type_item in enumerate(out_dtype_list):
|
||||
kernel_output.append(f'kernel_out_{i}')
|
||||
output_names.append(f'kernel_out_{i}')
|
||||
if (
|
||||
inplace_flag
|
||||
and self.inplace_map is not None
|
||||
and self.outputs['names'][i] in self.inplace_map
|
||||
):
|
||||
output_create = (
|
||||
output_create
|
||||
+ f"""
|
||||
*{self.outputs['names'][i]} = {self.inplace_map[self.outputs['names'][i]]};"""
|
||||
)
|
||||
|
||||
output_create = (
|
||||
output_create
|
||||
+ f"""
|
||||
auto kernel_out_{i} = SetSparseKernelOutput({self.outputs['names'][i]}, {output_type_map[out_dtype_list[i]]});"""
|
||||
)
|
||||
|
||||
else:
|
||||
raise ValueError(
|
||||
f"{self.api} : Output error: the output should not be empty."
|
||||
)
|
||||
|
||||
return kernel_output, output_names, output_create
|
||||
|
||||
|
||||
def header_include():
|
||||
return """
|
||||
#include "paddle/phi/api/include/tensor.h"
|
||||
#include "paddle/phi/common/scalar.h"
|
||||
#include "paddle/phi/common/int_array.h"
|
||||
#include "paddle/utils/optional.h"
|
||||
"""
|
||||
|
||||
|
||||
def source_include(header_file_path):
|
||||
return f"""
|
||||
#include "{header_file_path}"
|
||||
#include <memory>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "paddle/common/flags.h"
|
||||
|
||||
#include "paddle/phi/api/include/sparse_api.h"
|
||||
#include "paddle/phi/api/lib/api_gen_utils.h"
|
||||
#include "paddle/phi/api/lib/kernel_dispatch.h"
|
||||
#include "paddle/phi/api/lib/data_transform.h"
|
||||
#include "paddle/phi/core/kernel_registry.h"
|
||||
|
||||
#include "paddle/phi/infermeta/unary.h"
|
||||
#include "paddle/phi/infermeta/binary.h"
|
||||
#include "paddle/phi/infermeta/backward.h"
|
||||
|
||||
#include "paddle/phi/infermeta/sparse/unary.h"
|
||||
#include "paddle/phi/infermeta/sparse/binary.h"
|
||||
#include "paddle/phi/infermeta/sparse/backward.h"
|
||||
|
||||
COMMON_DECLARE_int32(low_precision_op_list);
|
||||
COMMON_DECLARE_bool(benchmark);
|
||||
"""
|
||||
|
||||
|
||||
def api_namespace():
|
||||
return (
|
||||
"""
|
||||
namespace paddle {
|
||||
namespace experimental {
|
||||
namespace sparse {
|
||||
|
||||
""",
|
||||
"""
|
||||
|
||||
} // namespace sparse
|
||||
} // namespace experimental
|
||||
} // namespace paddle
|
||||
""",
|
||||
)
|
||||
|
||||
|
||||
def generate_api(
|
||||
api_yaml_path, header_file_path, source_file_path, grad_flag=False
|
||||
):
|
||||
with open(api_yaml_path, 'r') as f:
|
||||
apis = yaml.load(f, Loader=yaml.FullLoader)
|
||||
header_file = open(header_file_path, 'w')
|
||||
source_file = open(source_file_path, 'w')
|
||||
|
||||
namespace = api_namespace()
|
||||
|
||||
header_file.write("#pragma once\n")
|
||||
header_file.write(header_include())
|
||||
header_file.write(namespace[0])
|
||||
|
||||
include_header_file = "paddle/phi/api/backward/sparse_backward_api_base.h"
|
||||
source_file.write(source_include(include_header_file))
|
||||
source_file.write(namespace[0])
|
||||
|
||||
for api in apis:
|
||||
sparse_bw_api = SparseBackwardAPI(api)
|
||||
header_file.write(
|
||||
sparse_bw_api.gene_api_declaration(
|
||||
grad_flag=grad_flag, append_predefined_out=False
|
||||
)
|
||||
)
|
||||
source_file.write(
|
||||
sparse_bw_api.gene_api_code(
|
||||
grad_flag=grad_flag, append_predefined_out=False
|
||||
)
|
||||
)
|
||||
|
||||
header_file.write(namespace[1])
|
||||
source_file.write(namespace[1])
|
||||
|
||||
header_file.close()
|
||||
source_file.close()
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Generate PaddlePaddle C++ Sparse API files'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--api_yaml_path',
|
||||
help='path to sparse api yaml file',
|
||||
default='paddle/phi/ops/yaml/sparse_backward.yaml',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--api_header_path',
|
||||
help='output of generated api header code file',
|
||||
default='paddle/phi/api/backward/sparse_backward_api_base.h',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--api_source_path',
|
||||
help='output of generated api source code file',
|
||||
default='paddle/phi/api/lib/sparse_backward_api_base.cc',
|
||||
)
|
||||
|
||||
options = parser.parse_args()
|
||||
|
||||
api_yaml_path = options.api_yaml_path
|
||||
header_file_path = options.api_header_path
|
||||
source_file_path = options.api_source_path
|
||||
|
||||
generate_api(
|
||||
api_yaml_path, header_file_path, source_file_path, grad_flag=True
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,431 @@
|
||||
# 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.
|
||||
|
||||
import argparse
|
||||
|
||||
import yaml
|
||||
from api_gen import ForwardAPI
|
||||
|
||||
PREFIX_TENSOR_NAME = 'input_'
|
||||
PREFIX_META_TENSOR_NAME = 'meta_'
|
||||
|
||||
|
||||
class StringsAPI(ForwardAPI):
|
||||
def __init__(self, api_item_yaml):
|
||||
super().__init__(api_item_yaml)
|
||||
|
||||
def get_api_func_name(self):
|
||||
return self.api
|
||||
|
||||
def gene_api_declaration(self):
|
||||
return f"""
|
||||
// {", ".join(self.outputs['names'])}
|
||||
{super().gene_api_declaration(append_predefined_out=False)}
|
||||
"""
|
||||
|
||||
def get_kernel_tensor_out_type(self, output_name):
|
||||
strings_type = 'TensorType::DENSE_TENSOR'
|
||||
if output_name.endswith('@StringTensor'):
|
||||
strings_type = 'TensorType::STRING_TENSOR'
|
||||
return strings_type
|
||||
|
||||
def get_tensor_type(self, kernel_tensor_out_type):
|
||||
tensor_type_dict = {
|
||||
"TensorType::DENSE_TENSOR": "phi::DenseTensor",
|
||||
"TensorType::STRING_TENSOR": "phi::StringTensor",
|
||||
}
|
||||
return tensor_type_dict[kernel_tensor_out_type]
|
||||
|
||||
def gene_output(
|
||||
self,
|
||||
out_dtype_list,
|
||||
out_tensor_type_list=None,
|
||||
code_indent='',
|
||||
inplace_flag=False,
|
||||
):
|
||||
kernel_output = []
|
||||
output_names = []
|
||||
output_create = ""
|
||||
return_type = self.get_return_type(inplace_flag)
|
||||
|
||||
if len(out_dtype_list) == 1:
|
||||
kernel_output.append('kernel_out')
|
||||
output_names.append('kernel_out')
|
||||
kernel_tensor_out_type = self.get_kernel_tensor_out_type(
|
||||
self.outputs['names'][0]
|
||||
)
|
||||
tensor_type = self.get_tensor_type(kernel_tensor_out_type)
|
||||
inplace_assign = (
|
||||
" = " + self.inplace_map[self.outputs['names'][0]]
|
||||
if inplace_flag
|
||||
and self.inplace_map is not None
|
||||
and self.outputs['names'][0] in self.inplace_map
|
||||
else ""
|
||||
)
|
||||
output_create = f"""
|
||||
{return_type} api_output{inplace_assign};
|
||||
{tensor_type}* kernel_out = dynamic_cast<{tensor_type}*>(SetStringsKernelOutput(&api_output, {kernel_tensor_out_type}));"""
|
||||
|
||||
elif len(out_dtype_list) > 1:
|
||||
output_create = f"""
|
||||
{return_type} api_output;"""
|
||||
|
||||
for i in range(len(out_dtype_list)):
|
||||
kernel_output.append(f'kernel_out_{i}')
|
||||
output_names.append(f'kernel_out_{i}')
|
||||
kernel_tensor_out_type = self.get_kernel_tensor_out_type(
|
||||
self.outputs['names'][i]
|
||||
)
|
||||
tensor_type = self.get_tensor_type(kernel_tensor_out_type)
|
||||
if (
|
||||
inplace_flag
|
||||
and self.inplace_map is not None
|
||||
and self.outputs['names'][i] in self.inplace_map
|
||||
):
|
||||
output_create = (
|
||||
output_create
|
||||
+ f"""
|
||||
std::get<{i}>(api_output) = {self.inplace_map[self.outputs['names'][i]]};"""
|
||||
)
|
||||
|
||||
output_create = (
|
||||
output_create
|
||||
+ f"""
|
||||
{tensor_type}* kernel_out_{i} = dynamic_cast<{tensor_type}*>(SetStringsKernelOutput(&std::get<{i}>(api_output), {kernel_tensor_out_type}));"""
|
||||
)
|
||||
|
||||
else:
|
||||
raise ValueError(
|
||||
f"{self.api} : Output error: the output should not be empty."
|
||||
)
|
||||
|
||||
return kernel_output, output_names, output_create
|
||||
|
||||
def get_kernel_args(self, code_indent):
|
||||
input_trans_map = {
|
||||
'const Tensor&': 'const phi::StringTensor&',
|
||||
'const std::vector<Tensor>&': 'const std::vector<const phi::StringTensor*>&',
|
||||
'const paddle::optional<Tensor>&': 'paddle::optional<const phi::StringTensor&>',
|
||||
'const paddle::optional<std::vector<Tensor>>&': 'paddle::optional<const std::vector<phi::StringTensor>&>',
|
||||
}
|
||||
out_trans_map = {
|
||||
'Tensor': 'phi::StringTensor*',
|
||||
'std::vector<Tensor>': 'std::vector<phi::StringTensor*>&',
|
||||
}
|
||||
input_names = self.inputs['names']
|
||||
input_infos = self.inputs['input_info']
|
||||
kernel_args_type_list = ['const phi::DeviceContext&']
|
||||
|
||||
attr_names = self.attrs['names']
|
||||
kernel_param = self.kernel['param']
|
||||
if kernel_param is None:
|
||||
kernel_param = input_names + attr_names
|
||||
input_tensor_code = ""
|
||||
# set input_tensor_code
|
||||
for i, input_name in enumerate(input_names):
|
||||
input_tensor_code = (
|
||||
input_tensor_code
|
||||
+ f"""
|
||||
{code_indent} auto {PREFIX_TENSOR_NAME}{input_name} = TensorToStringTensor({input_name});"""
|
||||
)
|
||||
|
||||
# set kernel_args
|
||||
kernel_args = "*dev_ctx, "
|
||||
for param in kernel_param:
|
||||
if param in input_names:
|
||||
if param in self.optional_vars:
|
||||
kernel_args = (
|
||||
kernel_args + PREFIX_TENSOR_NAME + param + ", "
|
||||
)
|
||||
else:
|
||||
if self.inputs['input_info'][param] == "const Tensor&":
|
||||
kernel_args = (
|
||||
kernel_args
|
||||
+ "*"
|
||||
+ PREFIX_TENSOR_NAME
|
||||
+ param
|
||||
+ ", "
|
||||
)
|
||||
elif (
|
||||
self.inputs['input_info'][input_name]
|
||||
== "const std::vector<Tensor>&"
|
||||
):
|
||||
kernel_args = (
|
||||
kernel_args + PREFIX_TENSOR_NAME + param + ", "
|
||||
)
|
||||
else:
|
||||
# do nothing
|
||||
pass
|
||||
kernel_in_type = input_trans_map[input_infos[param]]
|
||||
kernel_args_type_list.append(kernel_in_type)
|
||||
elif param in attr_names:
|
||||
# set attr for kernel_context
|
||||
if 'IntArray' in self.attrs['attr_info'][param][0]:
|
||||
kernel_args_type_list.append('const phi::IntArray&')
|
||||
param = 'phi::IntArray(' + param + ')'
|
||||
elif 'Scalar' in self.attrs['attr_info'][param][0]:
|
||||
kernel_args_type_list.append('const phi::Scalar&')
|
||||
param = 'phi::Scalar(' + param + ')'
|
||||
else:
|
||||
kernel_args_type_list.append(
|
||||
self.attrs['attr_info'][param][0]
|
||||
)
|
||||
kernel_args = kernel_args + param + ", "
|
||||
elif isinstance(param, bool):
|
||||
kernel_args = kernel_args + str(param).lower() + ", "
|
||||
else:
|
||||
kernel_args = kernel_args + str(param) + ", "
|
||||
|
||||
for out_type in self.outputs['types']:
|
||||
kernel_args_type_list.append(out_trans_map[out_type])
|
||||
|
||||
# set kernel_signature
|
||||
kernel_signature = "void(*)(" + ", ".join(kernel_args_type_list) + ")"
|
||||
|
||||
return input_tensor_code, kernel_args[:-2], kernel_signature
|
||||
|
||||
def gen_string_tensor_kernel_code(self, inplace_flag=False, code_indent=""):
|
||||
input_tensors, kernel_args, kernel_signature = self.get_kernel_args(
|
||||
code_indent
|
||||
)
|
||||
outputs_args, kernel_output_names, output_create = self.gene_output(
|
||||
self.outputs['types'], None, '', inplace_flag
|
||||
)
|
||||
|
||||
return f"""
|
||||
// 1. Get kernel signature and kernel
|
||||
VLOG(6) << "{self.api} api strings kernel key: [" << kernel_backend << ", " << kernel_layout << ", "<< kernel_data_type << "]";
|
||||
auto kernel_result = phi::KernelFactory::Instance().SelectKernelOrThrowError(
|
||||
"{self.kernel['func'][0]}", {{kernel_backend, kernel_layout, kernel_data_type}});
|
||||
if (FLAGS_low_precision_op_list) {{
|
||||
phi::KernelFactory::Instance().AddToLowPrecisionKernelList("{self.api}", kernel_data_type);
|
||||
}}
|
||||
const auto& kernel = kernel_result.kernel;
|
||||
VLOG(6) << "{self.api} api strings kernel: " << kernel;
|
||||
|
||||
// 2. Get Device Context and input
|
||||
auto* dev_ctx = GetDeviceContextByBackend(kernel_result.has_fallback_cpu ? Backend::CPU : kernel_backend);
|
||||
{input_tensors}
|
||||
|
||||
// 3. Set output
|
||||
{output_create}
|
||||
{self.gene_infer_meta(kernel_output_names, code_indent)}
|
||||
|
||||
// 4. run kernel
|
||||
|
||||
{code_indent} using kernel_signature = {kernel_signature};
|
||||
{code_indent} auto* kernel_fn = kernel.GetVariadicKernelFn<kernel_signature>();
|
||||
{code_indent} (*kernel_fn)({kernel_args}, {", ".join(outputs_args)});
|
||||
{code_indent} if (FLAGS_benchmark) {{
|
||||
{code_indent} dev_ctx->Wait();
|
||||
{code_indent} std::cout << \"{self.api} kernel run finish.\" << std::endl;
|
||||
{code_indent} }}
|
||||
|
||||
{code_indent} {self.gene_return_code()}"""
|
||||
|
||||
def gene_kernel_select(self) -> str:
|
||||
api = self.api
|
||||
input_names = self.inputs['names']
|
||||
attrs = self.attrs
|
||||
kernel = self.kernel
|
||||
|
||||
kernel_key_item_init = """
|
||||
Backend kernel_backend = Backend::UNDEFINED;
|
||||
DataLayout kernel_layout = DataLayout::PSTRING_UNION;
|
||||
DataType kernel_data_type = DataType::PSTRING;
|
||||
"""
|
||||
# Check the tensor options
|
||||
attr_backend_count = 0
|
||||
attr_layout_count = 0
|
||||
attr_data_type_count = 0
|
||||
for attr_name in attrs['names']:
|
||||
if attrs['attr_info'][attr_name][0] == 'Backend':
|
||||
assert kernel['backend'] is not None, (
|
||||
f"{api} api: When there is a parameter with 'Backend' type in attributes, you must set backend of kernel manually."
|
||||
)
|
||||
attr_backend_count = attr_backend_count + 1
|
||||
|
||||
# preprocess kernel configures
|
||||
kernel_select_code = ""
|
||||
if kernel['backend'] is not None:
|
||||
if '>' in kernel['backend']:
|
||||
vars_list = kernel['backend'].split('>')
|
||||
assert len(vars_list) == 2, (
|
||||
f"{api} api: The number of params to set backend with '>' only allows 2, but received {len(vars_list)}."
|
||||
)
|
||||
assert (vars_list[0].strip() in attrs['names']) and (
|
||||
attrs['attr_info'][vars_list[0].strip()][0]
|
||||
== 'const Place&'
|
||||
), (
|
||||
f"{api} api: When use '>' to set kernel backend, the first param should be an attribute with Place type."
|
||||
)
|
||||
kernel_select_code = (
|
||||
kernel_select_code
|
||||
+ f"""
|
||||
kernel_backend = ParseBackendWithInputOrder({vars_list[0].strip()}, {vars_list[1].strip()});
|
||||
"""
|
||||
)
|
||||
|
||||
else:
|
||||
args_str = ""
|
||||
for ele in kernel['backend'].split(','):
|
||||
args_str = args_str + ele.strip() + ', '
|
||||
kernel_select_code = (
|
||||
kernel_select_code
|
||||
+ f"""
|
||||
kernel_backend = ParseBackend({args_str[:-2]});
|
||||
"""
|
||||
)
|
||||
|
||||
kernel_select_args = ""
|
||||
for input_name in input_names:
|
||||
kernel_select_args = kernel_select_args + input_name + ", "
|
||||
|
||||
if len(kernel_select_args) > 2:
|
||||
kernel_select_args = kernel_select_args[:-2]
|
||||
|
||||
kernel_select_code = kernel_key_item_init + kernel_select_code
|
||||
|
||||
if len(input_names) > 0:
|
||||
kernel_select_code = (
|
||||
kernel_select_code
|
||||
+ f"""
|
||||
auto kernel_key_set = ParseKernelKeyByInputArgs({kernel_select_args});
|
||||
auto kernel_key = kernel_key_set.GetHighestPriorityKernelKey();
|
||||
kernel_backend = kernel_key.backend();"""
|
||||
)
|
||||
|
||||
return kernel_select_code
|
||||
|
||||
def gene_base_api_code(
|
||||
self, inplace_flag=False, grad_flag=False, append_predefined_out=False
|
||||
):
|
||||
api_func_name = self.get_api_func_name()
|
||||
return f"""
|
||||
PADDLE_API {self.get_return_type(inplace_flag)} {api_func_name}({self.get_define_args(inplace_flag, grad_flag=grad_flag, append_predefined_out=False)}) {{
|
||||
{self.gene_kernel_select()}
|
||||
{self.gen_string_tensor_kernel_code(inplace_flag)}
|
||||
}}
|
||||
"""
|
||||
|
||||
|
||||
def header_include():
|
||||
return """
|
||||
#include <tuple>
|
||||
|
||||
#include "paddle/phi/api/include/tensor.h"
|
||||
#include "paddle/phi/common/scalar.h"
|
||||
#include "paddle/phi/common/int_array.h"
|
||||
#include "paddle/utils/optional.h"
|
||||
"""
|
||||
|
||||
|
||||
def source_include(header_file_path):
|
||||
return f"""
|
||||
#include "{header_file_path}"
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "paddle/common/flags.h"
|
||||
|
||||
#include "paddle/phi/api/lib/api_gen_utils.h"
|
||||
#include "paddle/phi/core/kernel_context.h"
|
||||
#include "paddle/phi/core/string_tensor.h"
|
||||
#include "paddle/phi/infermeta/strings/nullary.h"
|
||||
#include "paddle/phi/infermeta/strings/unary.h"
|
||||
#include "paddle/phi/api/lib/kernel_dispatch.h"
|
||||
#include "paddle/phi/core/kernel_registry.h"
|
||||
|
||||
COMMON_DECLARE_int32(low_precision_op_list);
|
||||
COMMON_DECLARE_bool(benchmark);
|
||||
"""
|
||||
|
||||
|
||||
def api_namespace():
|
||||
return (
|
||||
"""
|
||||
namespace paddle {
|
||||
namespace experimental {
|
||||
namespace strings {
|
||||
|
||||
""",
|
||||
"""
|
||||
|
||||
} // namespace strings
|
||||
} // namespace experimental
|
||||
} // namespace paddle
|
||||
""",
|
||||
)
|
||||
|
||||
|
||||
def generate_api(api_yaml_path, header_file_path, source_file_path):
|
||||
with open(api_yaml_path, 'r') as f:
|
||||
apis = yaml.load(f, Loader=yaml.FullLoader)
|
||||
header_file = open(header_file_path, 'w')
|
||||
source_file = open(source_file_path, 'w')
|
||||
|
||||
namespace = api_namespace()
|
||||
|
||||
header_file.write("#pragma once\n")
|
||||
header_file.write(header_include())
|
||||
header_file.write(namespace[0])
|
||||
|
||||
include_header_file = "paddle/phi/api/include/strings_api.h"
|
||||
source_file.write(source_include(include_header_file))
|
||||
source_file.write(namespace[0])
|
||||
|
||||
for api in apis:
|
||||
strings_api = StringsAPI(api)
|
||||
header_file.write(strings_api.gene_api_declaration())
|
||||
source_file.write(strings_api.gene_api_code())
|
||||
|
||||
header_file.write(namespace[1])
|
||||
source_file.write(namespace[1])
|
||||
|
||||
header_file.close()
|
||||
source_file.close()
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Generate PaddlePaddle C++ Strings API files'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--api_yaml_path',
|
||||
help='path to sparse api yaml file',
|
||||
default='paddle/phi/ops/yaml/strings_ops.yaml',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--api_header_path',
|
||||
help='output of generated api header code file',
|
||||
default='paddle/phi/api/include/strings_api.h',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--api_source_path',
|
||||
help='output of generated api source code file',
|
||||
default='paddle/phi/api/lib/strings_api.cc',
|
||||
)
|
||||
|
||||
options = parser.parse_args()
|
||||
|
||||
api_yaml_path = options.api_yaml_path
|
||||
header_file_path = options.api_header_path
|
||||
source_file_path = options.api_source_path
|
||||
|
||||
generate_api(api_yaml_path, header_file_path, source_file_path)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,808 @@
|
||||
# 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.
|
||||
|
||||
import argparse
|
||||
|
||||
import yaml
|
||||
from api_gen import ForwardAPI
|
||||
|
||||
inplace_out_type_map = {
|
||||
"Tensor": "Tensor&",
|
||||
"std::vector<Tensor>": "std::vector<Tensor>&",
|
||||
}
|
||||
|
||||
inplace_optional_out_type_map = {
|
||||
"Tensor": "paddle::optional<Tensor>&",
|
||||
"std::vector<Tensor>": "paddle::optional<std::vector<Tensor>>&",
|
||||
}
|
||||
|
||||
indent = " "
|
||||
|
||||
# E.g.: Prim uses `elementwise_pow + fill_constant` to replace `pow`, so that we use this map to generate the `pow` signature when iterating over `elementwise_pow` API.
|
||||
specific_ops_map = {"elementwise_pow": "pow"}
|
||||
|
||||
|
||||
operants_base_include = """// Generated by paddle/phi/api/generator/tensor_operants_gen.py
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "paddle/phi/api/include/tensor.h"
|
||||
#include "paddle/phi/common/scalar.h"
|
||||
#include "paddle/phi/common/int_array.h"
|
||||
|
||||
"""
|
||||
|
||||
operants_base_start = """
|
||||
namespace paddle {
|
||||
|
||||
namespace operants {
|
||||
|
||||
using Tensor = paddle::Tensor;
|
||||
using Scalar = paddle::experimental::Scalar;
|
||||
using IntArray = paddle::experimental::IntArray;
|
||||
|
||||
class TensorOperantsBase {
|
||||
public:
|
||||
virtual ~TensorOperantsBase() = default;
|
||||
|
||||
virtual Tensor add(const Tensor& x, const Scalar& y) = 0;
|
||||
|
||||
virtual Tensor divide(const Tensor& x, const Scalar& y) = 0;
|
||||
|
||||
virtual Tensor multiply(const Tensor& x, const Scalar& y) = 0;
|
||||
|
||||
virtual Tensor subtract(const Tensor& x, const Scalar& y) = 0;
|
||||
|
||||
virtual Tensor add(const Scalar& x, const Tensor& y) = 0;
|
||||
|
||||
virtual Tensor divide(const Scalar& x, const Tensor& y) = 0;
|
||||
|
||||
virtual Tensor multiply(const Scalar& x, const Tensor& y) = 0;
|
||||
|
||||
virtual Tensor subtract(const Scalar& x, const Tensor& y) = 0;
|
||||
|
||||
virtual Tensor pow(const Tensor& x, const Tensor& y) = 0;
|
||||
|
||||
virtual Tensor pow(const Tensor& x, const Scalar& y) = 0;
|
||||
"""
|
||||
|
||||
|
||||
operants_base_end = """};
|
||||
|
||||
} // namespace operants
|
||||
} // namespace paddle
|
||||
|
||||
"""
|
||||
|
||||
tensor_api_source_include = """// Generated by paddle/phi/api/generator/tensor_operants_gen.py
|
||||
|
||||
#include "paddle/phi/api/include/tensor.h"
|
||||
|
||||
#include "paddle/phi/api/include/operants_manager.h"
|
||||
|
||||
"""
|
||||
|
||||
tensor_api_source_start = """
|
||||
namespace paddle {
|
||||
|
||||
Tensor Tensor::operator+(const Tensor &other) const {
|
||||
return add(other);
|
||||
}
|
||||
|
||||
Tensor Tensor::operator-(const Tensor &other) const {
|
||||
return subtract(other);
|
||||
}
|
||||
|
||||
Tensor Tensor::operator*(const Tensor &other) const {
|
||||
return multiply(other);
|
||||
}
|
||||
|
||||
Tensor Tensor::operator/(const Tensor &other) const {
|
||||
return divide(other);
|
||||
}
|
||||
|
||||
Tensor Tensor::operator+(const Scalar &other) const {
|
||||
return add(other);
|
||||
}
|
||||
|
||||
Tensor Tensor::operator-(const Scalar &other) const {
|
||||
return subtract(other);
|
||||
}
|
||||
|
||||
Tensor Tensor::operator*(const Scalar &other) const {
|
||||
return multiply(other);
|
||||
}
|
||||
|
||||
Tensor Tensor::operator/(const Scalar &other) const {
|
||||
return divide(other);
|
||||
}
|
||||
|
||||
Tensor Tensor::add(const Scalar& y) const {
|
||||
return paddle::OperantsManager::Instance().add(static_cast<const Tensor &>(*this), y);
|
||||
}
|
||||
|
||||
Tensor Tensor::divide(const Scalar& y) const {
|
||||
return paddle::OperantsManager::Instance().divide(static_cast<const Tensor &>(*this), y);
|
||||
}
|
||||
|
||||
Tensor Tensor::multiply(const Scalar& y) const {
|
||||
return paddle::OperantsManager::Instance().multiply(static_cast<const Tensor &>(*this), y);
|
||||
}
|
||||
|
||||
Tensor Tensor::subtract(const Scalar& y) const {
|
||||
return paddle::OperantsManager::Instance().subtract(static_cast<const Tensor &>(*this), y);
|
||||
}
|
||||
|
||||
Tensor Tensor::operator<(const Tensor &other) const {
|
||||
return less_than(other);
|
||||
}
|
||||
|
||||
Tensor Tensor::operator<=(const Tensor &other) const {
|
||||
return less_equal(other);
|
||||
}
|
||||
|
||||
Tensor Tensor::operator==(const Tensor &other) const {
|
||||
return equal(other);
|
||||
}
|
||||
|
||||
Tensor Tensor::operator!=(const Tensor &other) const {
|
||||
return not_equal(other);
|
||||
}
|
||||
|
||||
Tensor Tensor::operator>(const Tensor &other) const {
|
||||
return greater_than(other);
|
||||
}
|
||||
|
||||
Tensor Tensor::operator>=(const Tensor &other) const {
|
||||
return greater_equal(other);
|
||||
}
|
||||
|
||||
Tensor Tensor::operator-() const {
|
||||
return scale(-1.0, 0.0, true);
|
||||
}
|
||||
|
||||
Tensor Tensor::operator~() const {
|
||||
return bitwise_not();
|
||||
}
|
||||
|
||||
Tensor Tensor::operator&(const Tensor &other) const {
|
||||
return bitwise_and(other);
|
||||
}
|
||||
|
||||
Tensor Tensor::operator|(const Tensor &other) const {
|
||||
return bitwise_or(other);
|
||||
}
|
||||
|
||||
Tensor Tensor::operator^(const Tensor &other) const {
|
||||
return bitwise_xor(other);
|
||||
}
|
||||
|
||||
Tensor Tensor::pow(const Tensor& y) const {
|
||||
return paddle::OperantsManager::Instance().pow(static_cast<const Tensor &>(*this), y);
|
||||
}
|
||||
|
||||
Tensor Tensor::pow(const Scalar& y) const {
|
||||
return paddle::OperantsManager::Instance().pow(static_cast<const Tensor &>(*this), y);
|
||||
}
|
||||
|
||||
PADDLE_API Tensor operator+(const Scalar& x, const Tensor& y) {
|
||||
return paddle::OperantsManager::Instance().add(x, y);
|
||||
}
|
||||
|
||||
PADDLE_API Tensor operator-(const Scalar& x, const Tensor& y) {
|
||||
return paddle::OperantsManager::Instance().subtract(x, y);
|
||||
}
|
||||
|
||||
PADDLE_API Tensor operator*(const Scalar& x, const Tensor& y) {
|
||||
return paddle::OperantsManager::Instance().multiply(x, y);
|
||||
}
|
||||
|
||||
PADDLE_API Tensor operator/(const Scalar& x, const Tensor& y) {
|
||||
return paddle::OperantsManager::Instance().divide(x, y);
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
tensor_api_source_end = """
|
||||
} // namespace paddle
|
||||
|
||||
"""
|
||||
|
||||
|
||||
operants_header_include = """// Generated by paddle/phi/api/generator/tensor_operants_gen.py
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "paddle/phi/api/include/operants_base.h"
|
||||
#include "paddle/phi/api/include/tensor.h"
|
||||
#include "paddle/phi/common/scalar.h"
|
||||
#include "paddle/phi/common/int_array.h"
|
||||
#include "paddle/common/macros.h"
|
||||
|
||||
"""
|
||||
|
||||
operants_header_start = """
|
||||
namespace paddle {
|
||||
|
||||
namespace operants {
|
||||
|
||||
using Scalar = paddle::experimental::Scalar;
|
||||
using IntArray = paddle::experimental::IntArray;
|
||||
|
||||
class PhiTensorOperants : public TensorOperantsBase {
|
||||
private:
|
||||
DISABLE_COPY_AND_ASSIGN(PhiTensorOperants);
|
||||
|
||||
public:
|
||||
PhiTensorOperants() = default;
|
||||
|
||||
PADDLE_API Tensor add(const Tensor& x, const Scalar& y);
|
||||
|
||||
PADDLE_API Tensor subtract(const Tensor& x, const Scalar& y);
|
||||
|
||||
PADDLE_API Tensor multiply(const Tensor& x, const Scalar& y);
|
||||
|
||||
PADDLE_API Tensor divide(const Tensor& x, const Scalar& y);
|
||||
|
||||
PADDLE_API Tensor add(const Scalar& x, const Tensor& y);
|
||||
|
||||
PADDLE_API Tensor subtract(const Scalar& x, const Tensor& y);
|
||||
|
||||
PADDLE_API Tensor multiply(const Scalar& x, const Tensor& y);
|
||||
|
||||
PADDLE_API Tensor divide(const Scalar& x, const Tensor& y);
|
||||
|
||||
PADDLE_API Tensor pow(const Tensor& x, const Tensor& y);
|
||||
|
||||
PADDLE_API Tensor pow(const Tensor& x, const Scalar& y);
|
||||
|
||||
"""
|
||||
|
||||
|
||||
operants_header_end = """};
|
||||
|
||||
} // namespace operants
|
||||
} // namespace paddle
|
||||
|
||||
"""
|
||||
|
||||
|
||||
operants_source_include = """// Generated by paddle/phi/api/generator/tensor_operants_gen.py
|
||||
|
||||
#include "paddle/phi/api/include/tensor_operants.h"
|
||||
|
||||
#include "paddle/phi/api/include/api.h"
|
||||
|
||||
"""
|
||||
|
||||
|
||||
operants_source_start = """
|
||||
namespace paddle {
|
||||
|
||||
namespace operants {
|
||||
|
||||
Tensor PhiTensorOperants::add(const Tensor& x, const Scalar& y) {
|
||||
return paddle::experimental::add(x, paddle::experimental::full_like(x, y));
|
||||
}
|
||||
|
||||
Tensor PhiTensorOperants::subtract(const Tensor& x, const Scalar& y) {
|
||||
return paddle::experimental::subtract(x, paddle::experimental::full_like(x, y));
|
||||
}
|
||||
|
||||
Tensor PhiTensorOperants::multiply(const Tensor& x, const Scalar& y) {
|
||||
return paddle::experimental::scale(x, y, 0.0f, true);
|
||||
}
|
||||
|
||||
Tensor PhiTensorOperants::divide(const Tensor& x, const Scalar& y) {
|
||||
return paddle::experimental::divide(x, paddle::experimental::full_like(x, y));
|
||||
}
|
||||
|
||||
Tensor PhiTensorOperants::add(const Scalar& x, const Tensor& y) {
|
||||
return paddle::experimental::add(paddle::experimental::full_like(y, x), y);
|
||||
}
|
||||
|
||||
Tensor PhiTensorOperants::subtract(const Scalar& x, const Tensor& y) {
|
||||
return paddle::experimental::subtract(paddle::experimental::full_like(y, x), y);
|
||||
}
|
||||
|
||||
Tensor PhiTensorOperants::multiply(const Scalar& x, const Tensor& y) {
|
||||
return paddle::experimental::scale(y, x, 0.0f, true);
|
||||
}
|
||||
|
||||
Tensor PhiTensorOperants::divide(const Scalar& x, const Tensor& y) {
|
||||
return paddle::experimental::divide(paddle::experimental::full_like(y, x), y);
|
||||
}
|
||||
|
||||
Tensor PhiTensorOperants::pow(const Tensor& x, const Tensor& y) {
|
||||
return paddle::experimental::elementwise_pow(x, y);
|
||||
}
|
||||
|
||||
Tensor PhiTensorOperants::pow(const Tensor& x, const Scalar& y) {
|
||||
return paddle::experimental::elementwise_pow(x, paddle::experimental::full_like(x, y));
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
operants_source_end = """
|
||||
} // namespace operants
|
||||
} // namespace paddle
|
||||
|
||||
"""
|
||||
|
||||
|
||||
operants_manager_header_include = """// Generated by paddle/phi/api/generator/tensor_operants_gen.py
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "paddle/phi/api/include/operants_base.h"
|
||||
#include "paddle/phi/api/include/tensor.h"
|
||||
#include "paddle/phi/common/scalar.h"
|
||||
#include "paddle/phi/common/int_array.h"
|
||||
#include "paddle/common/macros.h"
|
||||
#include "paddle/utils/test_macros.h"
|
||||
|
||||
"""
|
||||
|
||||
operants_manager_header_start = """
|
||||
namespace paddle {
|
||||
|
||||
using Tensor = paddle::Tensor;
|
||||
using Scalar = paddle::experimental::Scalar;
|
||||
using IntArray = paddle::experimental::IntArray;
|
||||
using TensorOperantsBase = paddle::operants::TensorOperantsBase;
|
||||
|
||||
/**
|
||||
* [ Why need OperantsManager? ]
|
||||
*
|
||||
* Ideally, overloading tensor operators should call Tensor API directly.
|
||||
* However, we faced two problems:
|
||||
*
|
||||
* 1. Support multiple modes: Tensor operator overloading needs to support
|
||||
* [static mode / autograd mode / custom operator mode] at the same time.
|
||||
*
|
||||
* 2. Decouple phi and fluid: Tensor belongs to the phi library, but it relies
|
||||
* upon functions in fluid when overloading Tensor operators.
|
||||
*
|
||||
* We design OperantsManager to solve these two problems:
|
||||
*
|
||||
* 1. use `FLAGS_tensor_operants_mode` to handle overloading mode, set this flag
|
||||
* at the entry point of each mode:
|
||||
*
|
||||
* - FLAGS_tensor_operants_mode = "static": at the construction function of
|
||||
* `CompositeGradOpMakerBase`.
|
||||
* - FLAGS_tensor_operants_mode = "eager": at the beginning of dygraph_function.
|
||||
* - FLAGS_tensor_operants_mode = "phi": at the beginning of the
|
||||
* `eager_api_run_custom_op` function in eager mode and at the beginning of
|
||||
* calling kernels in static mode.
|
||||
*
|
||||
* In order to guarantee the performance, OperantsManager holds three pointers
|
||||
* to identify each mode respectively.
|
||||
*
|
||||
* 2. Decouple phi with the help of the polymorphism mechanism,
|
||||
* TensorOperantsBase derives three child classes: PhiTensorOperants,
|
||||
* EagerTensorOperants, and StaticTensorOperants. We set eager and static tensor
|
||||
* operants at the fluid library and set phi operants at the phi library.
|
||||
*
|
||||
*/
|
||||
class OperantsManager {
|
||||
private:
|
||||
OperantsManager() = default;
|
||||
DISABLE_COPY_AND_ASSIGN(OperantsManager);
|
||||
|
||||
public:
|
||||
std::unique_ptr<TensorOperantsBase> eager_operants{nullptr};
|
||||
std::unique_ptr<TensorOperantsBase> static_operants{nullptr};
|
||||
std::unique_ptr<TensorOperantsBase> phi_operants{nullptr};
|
||||
|
||||
public:
|
||||
PADDLE_API static OperantsManager& Instance();
|
||||
|
||||
PADDLE_API Tensor add(const Tensor& x, const Scalar& y);
|
||||
|
||||
PADDLE_API Tensor subtract(const Tensor& x, const Scalar& y);
|
||||
|
||||
PADDLE_API Tensor multiply(const Tensor& x, const Scalar& y);
|
||||
|
||||
PADDLE_API Tensor divide(const Tensor& x, const Scalar& y);
|
||||
|
||||
PADDLE_API Tensor add(const Scalar& x, const Tensor& y);
|
||||
|
||||
PADDLE_API Tensor subtract(const Scalar& x, const Tensor& y);
|
||||
|
||||
PADDLE_API Tensor multiply(const Scalar& x, const Tensor& y);
|
||||
|
||||
PADDLE_API Tensor divide(const Scalar& x, const Tensor& y);
|
||||
|
||||
PADDLE_API Tensor pow(const Tensor& x, const Tensor& y);
|
||||
|
||||
PADDLE_API Tensor pow(const Tensor& x, const Scalar& y);
|
||||
|
||||
"""
|
||||
|
||||
|
||||
operants_manager_header_end = """};
|
||||
|
||||
} // namespace paddle
|
||||
|
||||
"""
|
||||
|
||||
|
||||
operants_manager_source_include = """// Generated by paddle/phi/api/generator/tensor_operants_gen.py
|
||||
|
||||
#include "paddle/phi/api/include/operants_manager.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
#include "paddle/common/errors.h"
|
||||
#include "paddle/common/flags.h"
|
||||
|
||||
"""
|
||||
|
||||
|
||||
operants_manager_source_start = """
|
||||
COMMON_DECLARE_string(tensor_operants_mode);
|
||||
|
||||
namespace paddle {
|
||||
|
||||
OperantsManager& OperantsManager::Instance() {
|
||||
static OperantsManager g_op_manager;
|
||||
return g_op_manager;
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
operants_manager_source_end = """
|
||||
} // namespace paddle
|
||||
|
||||
"""
|
||||
|
||||
|
||||
class OperantsAPI(ForwardAPI):
|
||||
def __init__(self, api_item_yaml, prims=()):
|
||||
super().__init__(api_item_yaml)
|
||||
self.is_prim_api = False
|
||||
if self.get_api_func_name() in prims:
|
||||
self.is_prim_api = True
|
||||
|
||||
def gene_operants_base(self):
|
||||
api_func_name = self.get_api_func_name()
|
||||
if api_func_name[-1] != '_':
|
||||
return f"""
|
||||
{indent}virtual {self.get_return_type()} {api_func_name}({self.get_declare_args(append_predefined_out=False)}) = 0;
|
||||
"""
|
||||
else:
|
||||
return f"""
|
||||
{indent}virtual {self.get_return_type(inplace_flag=True)} {api_func_name}({self.get_declare_args(inplace_flag=True, append_predefined_out=False)}) = 0;
|
||||
"""
|
||||
|
||||
def get_declare_args_without_first_tensor(self, inplace_flag=False):
|
||||
func_name = self.get_api_func_name()
|
||||
declare_args = self.get_input_tensor_args(inplace_flag)
|
||||
assert len(declare_args) >= 1, (
|
||||
f"Error! Api {func_name} has no Tensor inputs"
|
||||
)
|
||||
first_input_type = " ".join(declare_args[0].split(" ")[:-1])
|
||||
# NOTE(HongyuJia): Do not consider "const paddle::optional<Tensor>&"
|
||||
assert first_input_type == "const Tensor&", (
|
||||
f"Error! The first argument of Tensor Api {func_name} must be Tensor, but received {first_input_type}"
|
||||
)
|
||||
for name in self.attrs['names']:
|
||||
default_value = ''
|
||||
if self.attrs['attr_info'][name][1] is not None:
|
||||
default_value = ' = ' + self.attrs['attr_info'][name][1]
|
||||
declare_args.append(
|
||||
self.attrs['attr_info'][name][0] + ' ' + name + default_value
|
||||
)
|
||||
# remove first Tensor argument
|
||||
return ", ".join(declare_args[1:])
|
||||
|
||||
def get_define_args_without_first_tensor(self, inplace_flag=False):
|
||||
func_name = self.get_api_func_name()
|
||||
define_args = self.get_input_tensor_args(inplace_flag)
|
||||
assert len(define_args) >= 1, (
|
||||
f"Error! Api {func_name} has no Tensor inputs"
|
||||
)
|
||||
first_input_type = " ".join(define_args[0].split(" ")[:-1])
|
||||
# NOTE(HongyuJia): Do not consider "const paddle::optional<Tensor>&"
|
||||
assert first_input_type == "const Tensor&", (
|
||||
f"Error! The first argument of Tensor Api {func_name} must be Tensor, but received {first_input_type}"
|
||||
)
|
||||
for name in self.attrs['names']:
|
||||
define_args.append(self.attrs['attr_info'][name][0] + ' ' + name)
|
||||
# remove first Tensor argument
|
||||
return ", ".join(define_args[1:])
|
||||
|
||||
def gene_tensor_api_implementation(self):
|
||||
func_name = self.get_api_func_name()
|
||||
assert len(self.inputs['names']) >= 1, (
|
||||
f"Error! Api {func_name} has no Tensor inputs"
|
||||
)
|
||||
# remove first Tensor argument
|
||||
func_args = self.inputs['names'][1:] + self.attrs['names']
|
||||
if len(func_args) > 0:
|
||||
func_args_code = ", ".join(["", *func_args])
|
||||
else:
|
||||
func_args_code = ""
|
||||
# func declaration
|
||||
if func_name[-1] != '_':
|
||||
return f"""
|
||||
{self.get_return_type()} Tensor::{func_name}({self.get_define_args_without_first_tensor()}) const {{
|
||||
{indent}return paddle::OperantsManager::Instance().{func_name}(static_cast<const Tensor &>(*this){func_args_code});
|
||||
}}
|
||||
"""
|
||||
else:
|
||||
return f"""
|
||||
{self.get_return_type(inplace_flag=True)} Tensor::{func_name}({self.get_define_args_without_first_tensor(inplace_flag=True)}) const {{
|
||||
{indent}return paddle::OperantsManager::Instance().{func_name}(static_cast<const Tensor &>(*this){func_args_code});
|
||||
}}
|
||||
|
||||
"""
|
||||
|
||||
def gene_operants_declaration(self):
|
||||
api_func_name = self.get_api_func_name()
|
||||
if api_func_name[-1] != '_':
|
||||
return f"""
|
||||
{indent}PADDLE_API {self.get_return_type()} {api_func_name}({self.get_declare_args(append_predefined_out=False)});
|
||||
"""
|
||||
else:
|
||||
return f"""
|
||||
{indent}PADDLE_API {self.get_return_type(inplace_flag=True)} {api_func_name}({self.get_declare_args(inplace_flag=True, append_predefined_out=False)});
|
||||
"""
|
||||
|
||||
def gene_operants_implementation(self):
|
||||
func_name = self.get_api_func_name()
|
||||
func_args = self.inputs['names'] + self.attrs['names']
|
||||
func_args_code = ", ".join(func_args)
|
||||
# func declaration
|
||||
if func_name[-1] != '_':
|
||||
return f"""
|
||||
{self.get_return_type()} PhiTensorOperants::{func_name}({self.get_define_args(append_predefined_out=False)}) {{
|
||||
{indent}return paddle::experimental::{func_name}({func_args_code});
|
||||
}}
|
||||
"""
|
||||
else:
|
||||
return f"""
|
||||
{self.get_return_type(inplace_flag=True)} PhiTensorOperants::{func_name}({self.get_define_args(inplace_flag=True, append_predefined_out=False)}) {{
|
||||
{indent}return paddle::experimental::{func_name}({func_args_code});
|
||||
}}
|
||||
|
||||
"""
|
||||
|
||||
def gene_operants_manager_code(self, is_specific_op=False):
|
||||
func_name = self.get_api_func_name()
|
||||
if is_specific_op:
|
||||
func_name = specific_ops_map[func_name]
|
||||
func_args = self.inputs['names'] + self.attrs['names']
|
||||
func_args_code = ", ".join(func_args)
|
||||
return f"""
|
||||
if (FLAGS_tensor_operants_mode == "eager") {{
|
||||
PADDLE_ENFORCE_NE(
|
||||
this->eager_operants.get(),
|
||||
nullptr,
|
||||
common::errors::Unavailable("The eager_operants pointer of "
|
||||
"OperantsManager is not initialized"));
|
||||
VLOG(4) << "OperantsManager reusing eager mode API ::{func_name}_ad_func";
|
||||
return this->eager_operants->{func_name}({func_args_code});
|
||||
}} else if (FLAGS_tensor_operants_mode == "static") {{
|
||||
PADDLE_ENFORCE_NE(
|
||||
this->static_operants.get(),
|
||||
nullptr,
|
||||
common::errors::Unavailable("The static_operants pointer of "
|
||||
"OperantsManager is not initialized"));
|
||||
VLOG(4) << "OperantsManager reusing static mode API paddle::prim::{func_name}<DescTensor>";
|
||||
return this->static_operants->{func_name}({func_args_code});
|
||||
}} else if (FLAGS_tensor_operants_mode == "phi") {{
|
||||
PADDLE_ENFORCE_NE(
|
||||
this->phi_operants.get(),
|
||||
nullptr,
|
||||
common::errors::Unavailable(
|
||||
"The phi_operants pointer of OperantsManager is not initialized"));
|
||||
VLOG(4) << "OperantsManager reusing phi mode API paddle::experimental::{func_name}";
|
||||
return this->phi_operants->{func_name}({func_args_code});
|
||||
}} else {{
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"FLAGS_tensor_operants_mode is not nitialized, please set "
|
||||
"FLAGS_tensor_operants_mode first, which currently supports eager, "
|
||||
"phi, and static mode"));
|
||||
}}
|
||||
"""
|
||||
|
||||
def gene_operants_manager_implementation(self):
|
||||
func_name = self.get_api_func_name()
|
||||
final_code = ""
|
||||
# Codes for arthemetic operants
|
||||
if func_name in ["add", "subtract", "multiply", "divide"]:
|
||||
final_code += f"""
|
||||
{self.get_return_type()} OperantsManager::{func_name}(const Tensor& x, const Scalar& y) {{{self.gene_operants_manager_code()}}}
|
||||
|
||||
{self.get_return_type()} OperantsManager::{func_name}(const Scalar& x, const Tensor& y) {{{self.gene_operants_manager_code()}}}
|
||||
"""
|
||||
# Codes for specific operants
|
||||
if func_name in specific_ops_map.keys():
|
||||
final_code += f"""
|
||||
{self.get_return_type()} OperantsManager::{specific_ops_map[func_name]}(const Tensor& x, const Tensor& y) {{{self.gene_operants_manager_code(is_specific_op=True)}}}
|
||||
|
||||
{self.get_return_type()} OperantsManager::{specific_ops_map[func_name]}(const Tensor& x, const Scalar& y) {{{self.gene_operants_manager_code(is_specific_op=True)}}}
|
||||
"""
|
||||
# func declaration
|
||||
if func_name[-1] != '_':
|
||||
return (
|
||||
final_code
|
||||
+ f"""
|
||||
{self.get_return_type()} OperantsManager::{func_name}({self.get_define_args(append_predefined_out=False)}) {{{self.gene_operants_manager_code()}}}
|
||||
"""
|
||||
)
|
||||
else:
|
||||
return (
|
||||
final_code
|
||||
+ f"""
|
||||
{self.get_return_type(inplace_flag=True)} OperantsManager::{func_name}({self.get_define_args(inplace_flag=True, append_predefined_out=False)}) {{
|
||||
{self.gene_operants_manager_code()}
|
||||
}}
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def generate_tensor_operants_api(
|
||||
api_yaml_path,
|
||||
operants_base_path,
|
||||
tensor_api_source_path,
|
||||
operants_header_path,
|
||||
operants_source_path,
|
||||
operants_manager_header_path,
|
||||
operants_manager_source_path,
|
||||
tensor_api_yaml_path,
|
||||
):
|
||||
apis = []
|
||||
|
||||
for each_api_yaml in api_yaml_path:
|
||||
with open(each_api_yaml, 'r') as f:
|
||||
api_list = yaml.load(f, Loader=yaml.FullLoader)
|
||||
if api_list:
|
||||
apis.extend(api_list)
|
||||
|
||||
operants_base_file = open(operants_base_path, 'w')
|
||||
tensor_api_source_file = open(tensor_api_source_path, 'w')
|
||||
operants_header_file = open(operants_header_path, 'w')
|
||||
operants_source_file = open(operants_source_path, 'w')
|
||||
operants_manager_header_file = open(operants_manager_header_path, 'w')
|
||||
operants_manager_source_file = open(operants_manager_source_path, 'w')
|
||||
|
||||
operants_base_file.write(operants_base_include)
|
||||
operants_base_file.write(operants_base_start)
|
||||
tensor_api_source_file.write(tensor_api_source_include)
|
||||
tensor_api_source_file.write(tensor_api_source_start)
|
||||
operants_header_file.write(operants_header_include)
|
||||
operants_header_file.write(operants_header_start)
|
||||
operants_source_file.write(operants_source_include)
|
||||
operants_source_file.write(operants_source_start)
|
||||
operants_manager_header_file.write(operants_manager_header_include)
|
||||
operants_manager_header_file.write(operants_manager_header_start)
|
||||
operants_manager_source_file.write(operants_manager_source_include)
|
||||
operants_manager_source_file.write(operants_manager_source_start)
|
||||
|
||||
with open(tensor_api_yaml_path, 'rt') as f:
|
||||
api_prims = yaml.safe_load(f)
|
||||
|
||||
for api in apis:
|
||||
operants_api = OperantsAPI(api, api_prims)
|
||||
if operants_api.is_prim_api:
|
||||
operants_base_file.write(operants_api.gene_operants_base())
|
||||
tensor_api_source_file.write(
|
||||
operants_api.gene_tensor_api_implementation()
|
||||
)
|
||||
operants_header_file.write(operants_api.gene_operants_declaration())
|
||||
operants_source_file.write(
|
||||
operants_api.gene_operants_implementation()
|
||||
)
|
||||
operants_manager_header_file.write(
|
||||
operants_api.gene_operants_declaration()
|
||||
)
|
||||
operants_manager_source_file.write(
|
||||
operants_api.gene_operants_manager_implementation()
|
||||
)
|
||||
|
||||
operants_base_file.write(operants_base_end)
|
||||
tensor_api_source_file.write(tensor_api_source_end)
|
||||
operants_header_file.write(operants_header_end)
|
||||
operants_source_file.write(operants_source_end)
|
||||
operants_manager_header_file.write(operants_manager_header_end)
|
||||
operants_manager_source_file.write(operants_manager_source_end)
|
||||
|
||||
operants_base_file.close()
|
||||
tensor_api_source_file.close()
|
||||
operants_header_file.close()
|
||||
operants_source_file.close()
|
||||
operants_manager_header_file.close()
|
||||
operants_manager_source_file.close()
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Generate PaddlePaddle C++ API files'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--api_yaml_path',
|
||||
help='path to api yaml file',
|
||||
nargs='+',
|
||||
default=['paddle/phi/ops/yaml/ops.yaml'],
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--operants_base_path',
|
||||
help='output of generated operants_base header code file',
|
||||
default='paddle/phi/api/include/operants_base.h',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--tensor_api_source_path',
|
||||
help='output of generated tensor_api source code file',
|
||||
default='paddle/phi/api/lib/tensor_api.cc',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--phi_tensor_operants_header_path',
|
||||
help='output of generated phi_tensor_operants header code file',
|
||||
default='paddle/phi/api/include/tensor_operants.h',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--phi_tensor_operants_source_path',
|
||||
help='output of generated phi_tensor_operants source code file',
|
||||
default='paddle/phi/api/lib/tensor_operants.cc',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--operants_manager_header_path',
|
||||
help='output of generated operants_manager header code file',
|
||||
default='paddle/phi/api/include/operants_manager.h',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--operants_manager_source_path',
|
||||
help='output of generated operants_manager source code file',
|
||||
default='paddle/phi/api/lib/operants_manager.cc',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--tensor_api_yaml_path',
|
||||
help='path to tensor_api yaml file',
|
||||
default='paddle/phi/api/lib/tensor_operants.yaml',
|
||||
)
|
||||
|
||||
options = parser.parse_args()
|
||||
|
||||
api_yaml_path = options.api_yaml_path
|
||||
operants_base_path = options.operants_base_path
|
||||
tensor_api_source_path = options.tensor_api_source_path
|
||||
operants_header_path = options.phi_tensor_operants_header_path
|
||||
operants_source_path = options.phi_tensor_operants_source_path
|
||||
operants_manager_header_path = options.operants_manager_header_path
|
||||
operants_manager_source_path = options.operants_manager_source_path
|
||||
tensor_api_yaml_path = options.tensor_api_yaml_path
|
||||
|
||||
generate_tensor_operants_api(
|
||||
api_yaml_path,
|
||||
operants_base_path,
|
||||
tensor_api_source_path,
|
||||
operants_header_path,
|
||||
operants_source_path,
|
||||
operants_manager_header_path,
|
||||
operants_manager_source_path,
|
||||
tensor_api_yaml_path,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,210 @@
|
||||
# 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.
|
||||
|
||||
import argparse
|
||||
|
||||
import yaml
|
||||
from api_gen import ForwardAPI
|
||||
|
||||
kernel_func_set = set()
|
||||
|
||||
|
||||
def get_wrapped_infermeta_name(api_name):
|
||||
return api_name.capitalize() + 'InferMeta'
|
||||
|
||||
|
||||
def gene_wrapped_infermeta_and_register(api):
|
||||
if api.is_base_api and not api.is_dygraph_api:
|
||||
register_code = f"""
|
||||
PD_REGISTER_INFER_META_FN({api.kernel['func'][0]}, phi::{api.infer_meta['func']});"""
|
||||
|
||||
if api.infer_meta['param'] is not None:
|
||||
if api.kernel['func'][0] in kernel_func_set:
|
||||
return '', '', ''
|
||||
|
||||
kernel_params = api.kernel['param']
|
||||
if kernel_params is None:
|
||||
kernel_params = api.inputs['names'] + api.attrs['names']
|
||||
if kernel_params == api.infer_meta['param']:
|
||||
return '', '', register_code
|
||||
|
||||
assert len(api.infer_meta['param']) <= len(kernel_params), (
|
||||
f"{api.api} api: Parameters error. The params of infer_meta should be a subset of kernel params."
|
||||
)
|
||||
|
||||
tensor_type_map = {
|
||||
'const Tensor&': 'const MetaTensor&',
|
||||
'const std::vector<Tensor>&': 'const std::vector<const MetaTensor*>&',
|
||||
'Tensor': 'MetaTensor*',
|
||||
'std::vector<Tensor>': 'std::vector<MetaTensor*>',
|
||||
'const paddle::optional<Tensor>&': 'const MetaTensor&',
|
||||
}
|
||||
|
||||
wrapped_infermeta_name = get_wrapped_infermeta_name(
|
||||
api.kernel['func'][0]
|
||||
)
|
||||
args = []
|
||||
for input_name in api.inputs['names']:
|
||||
if input_name in kernel_params:
|
||||
args.append(
|
||||
tensor_type_map[api.inputs['input_info'][input_name]]
|
||||
+ ' '
|
||||
+ input_name
|
||||
)
|
||||
for attr_name in api.attrs['names']:
|
||||
if attr_name in kernel_params:
|
||||
args.append(
|
||||
api.attrs['attr_info'][attr_name][0] + ' ' + attr_name
|
||||
)
|
||||
for i, out_type in enumerate(api.outputs['types']):
|
||||
args.append(
|
||||
tensor_type_map[out_type] + ' ' + api.outputs['names'][i]
|
||||
)
|
||||
|
||||
invoke_param = api.infer_meta['param']
|
||||
invoke_param.extend(api.outputs['names'])
|
||||
|
||||
declare_code = f"""
|
||||
void {wrapped_infermeta_name}({", ".join(args)});
|
||||
"""
|
||||
|
||||
defined_code = f"""
|
||||
void {wrapped_infermeta_name}({", ".join(args)}) {{
|
||||
{api.infer_meta['func']}({", ".join(invoke_param)});
|
||||
}}
|
||||
"""
|
||||
|
||||
register_code = f"""
|
||||
PD_REGISTER_INFER_META_FN({api.kernel['func'][0]}, phi::{get_wrapped_infermeta_name(api.kernel['func'][0])});"""
|
||||
|
||||
kernel_func_set.add(api.kernel['func'][0])
|
||||
return declare_code, defined_code, register_code
|
||||
else:
|
||||
return '', '', register_code
|
||||
else:
|
||||
return '', '', ''
|
||||
|
||||
|
||||
def header_include():
|
||||
return """
|
||||
#include "paddle/phi/core/meta_tensor.h"
|
||||
#include "paddle/phi/common/scalar.h"
|
||||
#include "paddle/phi/common/int_array.h"
|
||||
"""
|
||||
|
||||
|
||||
def source_include(header_file_path):
|
||||
return f"""
|
||||
#include "{header_file_path}"
|
||||
#include "paddle/phi/core/infermeta_utils.h"
|
||||
#include "paddle/phi/infermeta/binary.h"
|
||||
#include "paddle/phi/infermeta/multiary.h"
|
||||
#include "paddle/phi/infermeta/nullary.h"
|
||||
#include "paddle/phi/infermeta/unary.h"
|
||||
#include "paddle/phi/infermeta/ternary.h"
|
||||
"""
|
||||
|
||||
|
||||
def api_namespace():
|
||||
return (
|
||||
"""
|
||||
namespace phi {
|
||||
""",
|
||||
"""
|
||||
} // namespace phi
|
||||
""",
|
||||
)
|
||||
|
||||
|
||||
def generate_wrapped_infermeta_and_register(
|
||||
api_yaml_path, header_file_path, source_file_path
|
||||
):
|
||||
apis = []
|
||||
for each_api_yaml in api_yaml_path:
|
||||
with open(each_api_yaml, 'r') as f:
|
||||
api_list = yaml.load(f, Loader=yaml.FullLoader)
|
||||
if api_list:
|
||||
apis.extend(api_list)
|
||||
|
||||
header_file = open(header_file_path, 'w')
|
||||
source_file = open(source_file_path, 'w')
|
||||
|
||||
namespace = api_namespace()
|
||||
|
||||
header_file.write("#pragma once\n")
|
||||
header_file.write(header_include())
|
||||
header_file.write(namespace[0])
|
||||
|
||||
include_header_file = "paddle/phi/infermeta/generated.h"
|
||||
source_file.write(source_include(include_header_file))
|
||||
source_file.write(namespace[0])
|
||||
|
||||
infermeta_register_code = ''
|
||||
|
||||
for api in apis:
|
||||
api_item = ForwardAPI(api)
|
||||
(
|
||||
declare_code,
|
||||
defined_code,
|
||||
register_code,
|
||||
) = gene_wrapped_infermeta_and_register(api_item)
|
||||
header_file.write(declare_code)
|
||||
source_file.write(defined_code)
|
||||
if infermeta_register_code.find(register_code) == -1:
|
||||
infermeta_register_code = infermeta_register_code + register_code
|
||||
|
||||
header_file.write(namespace[1])
|
||||
source_file.write(namespace[1])
|
||||
|
||||
source_file.write(infermeta_register_code)
|
||||
|
||||
header_file.close()
|
||||
source_file.close()
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Generate PaddlePaddle C++ API files'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--api_yaml_path',
|
||||
help='path to api yaml file',
|
||||
nargs='+',
|
||||
default=['paddle/phi/ops/yaml/ops.yaml'],
|
||||
)
|
||||
parser.add_argument(
|
||||
'--wrapped_infermeta_header_path',
|
||||
help='output of generated wrapped_infermeta header code file',
|
||||
default='paddle/phi/infermeta/generated.h',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--wrapped_infermeta_source_path',
|
||||
help='output of generated wrapped_infermeta source code file',
|
||||
default='paddle/phi/infermeta/generated.cc',
|
||||
)
|
||||
|
||||
options = parser.parse_args()
|
||||
|
||||
api_yaml_path = options.api_yaml_path
|
||||
header_file_path = options.wrapped_infermeta_header_path
|
||||
source_file_path = options.wrapped_infermeta_source_path
|
||||
|
||||
generate_wrapped_infermeta_and_register(
|
||||
api_yaml_path, header_file_path, source_file_path
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) 2025 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 <ATen/Device.h>
|
||||
#include <ATen/Functions.h>
|
||||
#include <ATen/Tensor.h>
|
||||
#include <ATen/TensorIndexing.h>
|
||||
#include <ATen/Utils.h>
|
||||
#include <c10/core/Device.h>
|
||||
#include <c10/core/DeviceType.h>
|
||||
#include <c10/core/MemoryFormat.h>
|
||||
#include <c10/core/Scalar.h>
|
||||
#include <c10/core/ScalarType.h>
|
||||
#include <c10/core/SymIntArrayRef.h>
|
||||
#include <c10/core/TensorOptions.h>
|
||||
#include <c10/util/ArrayRef.h>
|
||||
#include <c10/util/Exception.h>
|
||||
#include <c10/util/OptionalArrayRef.h>
|
||||
|
||||
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
|
||||
#include <c10/cuda/CUDAException.h>
|
||||
#include <c10/cuda/CUDAGuard.h>
|
||||
#include <c10/cuda/CUDAStream.h>
|
||||
#endif
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright (c) 2025 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.
|
||||
|
||||
// The file has been adapted from pytorch project
|
||||
// Licensed under BSD-style license -
|
||||
// https://github.com/pytorch/pytorch/blob/main/LICENSE
|
||||
|
||||
#include <ATen/AccumulateType.h>
|
||||
|
||||
namespace at {
|
||||
|
||||
c10::ScalarType toAccumulateType(c10::ScalarType type, c10::DeviceType device) {
|
||||
switch (type) {
|
||||
#define DEFINE_CASE(scalar_t, TypeNum) \
|
||||
case ScalarType::TypeNum: \
|
||||
switch (device) { \
|
||||
case DeviceType::CUDA: \
|
||||
return CppTypeToScalarType< \
|
||||
at::acc_type_device<scalar_t, c10::DeviceType::CUDA>>::value; \
|
||||
default: \
|
||||
return CppTypeToScalarType< \
|
||||
at::acc_type_device<scalar_t, c10::DeviceType::CPU>>::value; \
|
||||
}
|
||||
|
||||
AT_FORALL_SCALAR_TYPES_WITH_COMPLEX_EXCEPT_COMPLEX_HALF_F8NZ(DEFINE_CASE)
|
||||
#undef DEFINE_CASE
|
||||
|
||||
default:
|
||||
TORCH_INTERNAL_ASSERT(false, "Unrecognized ScalarType: ", type);
|
||||
}
|
||||
}
|
||||
|
||||
c10::ScalarType toAccumulateType(c10::ScalarType type, bool is_cuda) {
|
||||
return is_cuda ? toAccumulateType(type, c10::DeviceType::CUDA)
|
||||
: toAccumulateType(type, c10::DeviceType::CPU);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,115 @@
|
||||
// Copyright (c) 2025 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.
|
||||
|
||||
// The file has been adapted from pytorch project
|
||||
// Licensed under BSD-style license -
|
||||
// https://github.com/pytorch/pytorch/blob/main/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <c10/core/DeviceType.h>
|
||||
#include <c10/core/ScalarType.h>
|
||||
#include <c10/util/BFloat16.h>
|
||||
#include <c10/util/Float8_e4m3fn.h>
|
||||
// #include <c10/util/Float8_e4m3fnuz.h>
|
||||
#include <c10/util/Float8_e5m2.h>
|
||||
// #include <c10/util/Float8_e5m2fnuz.h>
|
||||
#include <c10/util/Half.h>
|
||||
|
||||
#if defined(__CUDACC__)
|
||||
#include <cuda.h>
|
||||
#include <cuda_fp16.h>
|
||||
#elif defined(__HIPCC__)
|
||||
#include <hip/hip_fp16.h>
|
||||
#include <hip/hip_runtime.h>
|
||||
#endif
|
||||
|
||||
namespace at {
|
||||
|
||||
template <typename T, c10::DeviceType D>
|
||||
struct AccumulateTypeDevice {};
|
||||
|
||||
template <typename T, bool>
|
||||
struct AccumulateType {};
|
||||
|
||||
template <typename T>
|
||||
struct AccumulateType<T, false> {
|
||||
using type = typename AccumulateTypeDevice<T, c10::DeviceType::CPU>::type;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct AccumulateType<T, true> {
|
||||
using type = typename AccumulateTypeDevice<T, c10::DeviceType::CUDA>::type;
|
||||
};
|
||||
|
||||
template <typename T, c10::DeviceType device>
|
||||
using acc_type_device = typename AccumulateTypeDevice<T, device>::type;
|
||||
|
||||
template <typename T, bool is_cuda>
|
||||
using acc_type = typename AccumulateType<T, is_cuda>::type;
|
||||
|
||||
#define ACC_TYPE(t, acc_t, device_type) \
|
||||
template <> \
|
||||
struct AccumulateTypeDevice<t, device_type> { \
|
||||
using type = acc_t; \
|
||||
};
|
||||
|
||||
#define CUDA_ACC_TYPE(t, acc_t) ACC_TYPE(t, acc_t, c10::DeviceType::CUDA)
|
||||
#define CPU_ACC_TYPE(t, acc_t) ACC_TYPE(t, acc_t, c10::DeviceType::CPU)
|
||||
|
||||
#if defined(__CUDACC__) || defined(__HIPCC__)
|
||||
CUDA_ACC_TYPE(half, float)
|
||||
#endif
|
||||
CUDA_ACC_TYPE(BFloat16, float)
|
||||
CUDA_ACC_TYPE(Half, float)
|
||||
CUDA_ACC_TYPE(Float8_e5m2, float)
|
||||
CUDA_ACC_TYPE(Float8_e4m3fn, float)
|
||||
// CUDA_ACC_TYPE(Float8_e5m2fnuz, float)
|
||||
// CUDA_ACC_TYPE(Float8_e4m3fnuz, float)
|
||||
CUDA_ACC_TYPE(float, float)
|
||||
CUDA_ACC_TYPE(double, double)
|
||||
CUDA_ACC_TYPE(int8_t, int64_t)
|
||||
CUDA_ACC_TYPE(uint8_t, int64_t)
|
||||
CUDA_ACC_TYPE(char, int64_t)
|
||||
CUDA_ACC_TYPE(int16_t, int64_t)
|
||||
CUDA_ACC_TYPE(int32_t, int64_t)
|
||||
CUDA_ACC_TYPE(int64_t, int64_t)
|
||||
CUDA_ACC_TYPE(bool, bool)
|
||||
CUDA_ACC_TYPE(c10::complex<Half>, c10::complex<float>)
|
||||
CUDA_ACC_TYPE(c10::complex<float>, c10::complex<float>)
|
||||
CUDA_ACC_TYPE(c10::complex<double>, c10::complex<double>)
|
||||
|
||||
CPU_ACC_TYPE(BFloat16, float)
|
||||
CPU_ACC_TYPE(Half, float)
|
||||
CPU_ACC_TYPE(Float8_e5m2, float)
|
||||
CPU_ACC_TYPE(Float8_e4m3fn, float)
|
||||
// CPU_ACC_TYPE(Float8_e5m2fnuz, float)
|
||||
// CPU_ACC_TYPE(Float8_e4m3fnuz, float)
|
||||
CPU_ACC_TYPE(float, double)
|
||||
CPU_ACC_TYPE(double, double)
|
||||
CPU_ACC_TYPE(int8_t, int64_t)
|
||||
CPU_ACC_TYPE(uint8_t, int64_t)
|
||||
CPU_ACC_TYPE(char, int64_t)
|
||||
CPU_ACC_TYPE(int16_t, int64_t)
|
||||
CPU_ACC_TYPE(int32_t, int64_t)
|
||||
CPU_ACC_TYPE(int64_t, int64_t)
|
||||
CPU_ACC_TYPE(bool, bool)
|
||||
CPU_ACC_TYPE(c10::complex<Half>, c10::complex<float>)
|
||||
CPU_ACC_TYPE(c10::complex<float>, c10::complex<double>)
|
||||
CPU_ACC_TYPE(c10::complex<double>, c10::complex<double>)
|
||||
|
||||
c10::ScalarType toAccumulateType(c10::ScalarType type, c10::DeviceType device);
|
||||
c10::ScalarType toAccumulateType(c10::ScalarType type, bool is_cuda);
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) 2025 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 <c10/core/Device.h>
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) 2025 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 <ATen/Tensor.h>
|
||||
#include <c10/core/ScalarType.h>
|
||||
#include <c10/util/Optional.h>
|
||||
|
||||
namespace at {
|
||||
|
||||
inline std::optional<Device> device_of(const Tensor& t) {
|
||||
if (t.defined()) {
|
||||
return t.device();
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
inline std::optional<Device> device_of(const std::optional<Tensor>& t) {
|
||||
return t.has_value() ? device_of(t.value()) : std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,82 @@
|
||||
// Copyright (c) 2025 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 <ATen/ops/_local_scalar_dense.h>
|
||||
#include <ATen/ops/_nnz.h>
|
||||
#include <ATen/ops/_values.h>
|
||||
#include <ATen/ops/abs.h>
|
||||
#include <ATen/ops/all.h>
|
||||
#include <ATen/ops/allclose.h>
|
||||
#include <ATen/ops/any.h>
|
||||
#include <ATen/ops/arange.h>
|
||||
#include <ATen/ops/as_strided.h>
|
||||
#include <ATen/ops/cat.h>
|
||||
#include <ATen/ops/chunk.h>
|
||||
#include <ATen/ops/clamp.h>
|
||||
#include <ATen/ops/coalesce.h>
|
||||
#include <ATen/ops/detach.h>
|
||||
#include <ATen/ops/dsplit.h>
|
||||
#include <ATen/ops/empty.h>
|
||||
#include <ATen/ops/empty_like.h>
|
||||
#include <ATen/ops/empty_strided.h>
|
||||
#include <ATen/ops/equal.h>
|
||||
#include <ATen/ops/expand.h>
|
||||
#include <ATen/ops/expand_as.h>
|
||||
#include <ATen/ops/eye.h>
|
||||
#include <ATen/ops/flatten.h>
|
||||
#include <ATen/ops/from_blob.h>
|
||||
#include <ATen/ops/full.h>
|
||||
#include <ATen/ops/hsplit.h>
|
||||
#include <ATen/ops/index.h>
|
||||
#include <ATen/ops/index_put.h>
|
||||
#include <ATen/ops/is_coalesced.h>
|
||||
#include <ATen/ops/item.h>
|
||||
#include <ATen/ops/masked_select.h>
|
||||
#include <ATen/ops/narrow.h>
|
||||
#include <ATen/ops/narrow_copy.h>
|
||||
#include <ATen/ops/new_empty.h>
|
||||
#include <ATen/ops/new_full.h>
|
||||
#include <ATen/ops/new_ones.h>
|
||||
#include <ATen/ops/new_zeros.h>
|
||||
#include <ATen/ops/ones.h>
|
||||
#include <ATen/ops/permute.h>
|
||||
#include <ATen/ops/reciprocal.h>
|
||||
#include <ATen/ops/record_stream.h>
|
||||
#include <ATen/ops/rename.h>
|
||||
#include <ATen/ops/reshape.h>
|
||||
#include <ATen/ops/resize.h>
|
||||
#include <ATen/ops/select.h>
|
||||
#include <ATen/ops/slice.h>
|
||||
#include <ATen/ops/sparse_coo_tensor.h>
|
||||
#include <ATen/ops/sparse_csr_tensor.h>
|
||||
#include <ATen/ops/split.h>
|
||||
#include <ATen/ops/split_with_sizes.h>
|
||||
#include <ATen/ops/squeeze.h>
|
||||
#include <ATen/ops/std.h>
|
||||
#include <ATen/ops/sum.h>
|
||||
#include <ATen/ops/t.h>
|
||||
#include <ATen/ops/tensor_split.h>
|
||||
#include <ATen/ops/to.h>
|
||||
#include <ATen/ops/transpose.h>
|
||||
#include <ATen/ops/unflatten.h>
|
||||
#include <ATen/ops/unsafe_split.h>
|
||||
#include <ATen/ops/unsafe_split_with_sizes.h>
|
||||
#include <ATen/ops/unsqueeze.h>
|
||||
#include <ATen/ops/view.h>
|
||||
#include <ATen/ops/view_as.h>
|
||||
#include <ATen/ops/vsplit.h>
|
||||
#include <ATen/ops/zeros.h>
|
||||
#include <ATen/ops/zeros_like.h>
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright (c) 2026 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 <c10/core/ScalarType.h>
|
||||
#include <c10/util/BFloat16.h>
|
||||
#include <c10/util/Exception.h>
|
||||
#include <c10/util/Float8_e4m3fn.h>
|
||||
#include <c10/util/Float8_e5m2.h>
|
||||
#include <c10/util/Half.h>
|
||||
|
||||
namespace at {
|
||||
|
||||
// For FP16 or BFloat16 inputs, ops should perform internal math in FP32.
|
||||
template <typename scalar_t>
|
||||
struct OpMathType {
|
||||
using type = scalar_t;
|
||||
};
|
||||
template <>
|
||||
struct OpMathType<at::Half> {
|
||||
using type = float;
|
||||
};
|
||||
template <>
|
||||
struct OpMathType<at::BFloat16> {
|
||||
using type = float;
|
||||
};
|
||||
template <>
|
||||
struct OpMathType<at::Float8_e5m2> {
|
||||
using type = float;
|
||||
};
|
||||
template <>
|
||||
struct OpMathType<at::Float8_e4m3fn> {
|
||||
using type = float;
|
||||
};
|
||||
template <>
|
||||
struct OpMathType<c10::complex<Half>> {
|
||||
using type = c10::complex<float>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using opmath_type = typename OpMathType<T>::type;
|
||||
|
||||
inline c10::ScalarType toOpMathType(const c10::ScalarType type) {
|
||||
switch (type) {
|
||||
#define DEFINE_CASE(scalar_t, TypeNum) \
|
||||
case ScalarType::TypeNum: \
|
||||
return CppTypeToScalarType<at::opmath_type<scalar_t>>::value;
|
||||
|
||||
AT_FORALL_SCALAR_TYPES_WITH_COMPLEX(DEFINE_CASE)
|
||||
#undef DEFINE_CASE
|
||||
|
||||
default:
|
||||
TORCH_INTERNAL_ASSERT(false, "Unrecognized ScalarType: ", type);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright (c) 2025 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 <ATen/core/Tensor.h>
|
||||
@@ -0,0 +1,124 @@
|
||||
// Copyright (c) 2025 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 <c10/core/SymInt.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace at {
|
||||
class Tensor;
|
||||
}
|
||||
|
||||
namespace at::indexing {
|
||||
|
||||
constexpr int64_t INDEX_MIN = std::numeric_limits<int64_t>::min();
|
||||
constexpr int64_t INDEX_MAX = std::numeric_limits<int64_t>::max();
|
||||
|
||||
enum class TensorIndexType { None, Ellipsis, SymInt, Boolean, Slice, Tensor };
|
||||
|
||||
constexpr std::nullopt_t None = std::nullopt;
|
||||
|
||||
struct EllipsisIndexType final {
|
||||
EllipsisIndexType() = default;
|
||||
};
|
||||
|
||||
const EllipsisIndexType Ellipsis = EllipsisIndexType();
|
||||
|
||||
struct Slice final {
|
||||
public:
|
||||
Slice(std::optional<c10::SymInt> start_index = std::nullopt,
|
||||
std::optional<c10::SymInt> stop_index = std::nullopt,
|
||||
std::optional<c10::SymInt> step_index = std::nullopt) {
|
||||
if (!step_index.has_value()) {
|
||||
step_ = c10::SymInt(1);
|
||||
} else {
|
||||
step_ = std::move(step_index).value();
|
||||
}
|
||||
|
||||
if (!start_index.has_value()) {
|
||||
start_ = c10::SymInt(step_ < 0 ? INDEX_MAX : 0);
|
||||
} else {
|
||||
start_ = std::move(start_index).value();
|
||||
}
|
||||
|
||||
if (!stop_index.has_value()) {
|
||||
stop_ = c10::SymInt(step_ < 0 ? INDEX_MIN : INDEX_MAX);
|
||||
} else {
|
||||
stop_ = std::move(stop_index).value();
|
||||
}
|
||||
}
|
||||
|
||||
inline c10::SymInt start() const { return start_; }
|
||||
inline c10::SymInt stop() const { return stop_; }
|
||||
inline c10::SymInt step() const { return step_; }
|
||||
|
||||
private:
|
||||
c10::SymInt start_;
|
||||
c10::SymInt stop_;
|
||||
c10::SymInt step_;
|
||||
};
|
||||
|
||||
struct TensorIndex final {
|
||||
TensorIndex(std::nullopt_t /*unused*/) : type_(TensorIndexType::None) {}
|
||||
|
||||
TensorIndex(at::indexing::EllipsisIndexType /*unused*/)
|
||||
: type_(TensorIndexType::Ellipsis) {}
|
||||
TensorIndex(const char* str) : TensorIndex(at::indexing::Ellipsis) {
|
||||
if (std::strcmp(str, "...") != 0) {
|
||||
throw std::invalid_argument(
|
||||
"Expected \"...\" to represent an ellipsis index.");
|
||||
}
|
||||
}
|
||||
|
||||
TensorIndex(c10::SymInt integer)
|
||||
: integer_(std::move(integer)), type_(TensorIndexType::SymInt) {}
|
||||
TensorIndex(int64_t integer) : TensorIndex(c10::SymInt(integer)) {}
|
||||
TensorIndex(int integer) : TensorIndex(c10::SymInt(integer)) {}
|
||||
|
||||
template <class T, class = std::enable_if_t<std::is_same_v<bool, T>>>
|
||||
TensorIndex(T boolean) : boolean_(boolean), type_(TensorIndexType::Boolean) {}
|
||||
|
||||
TensorIndex(Slice slice)
|
||||
: slice_(std::move(slice)), type_(TensorIndexType::Slice) {}
|
||||
|
||||
TensorIndex(const at::Tensor& tensor);
|
||||
|
||||
inline bool is_none() const { return type_ == TensorIndexType::None; }
|
||||
inline bool is_ellipsis() const { return type_ == TensorIndexType::Ellipsis; }
|
||||
inline bool is_integer() const { return type_ == TensorIndexType::SymInt; }
|
||||
inline c10::SymInt integer() const { return integer_; }
|
||||
inline bool is_boolean() const { return type_ == TensorIndexType::Boolean; }
|
||||
inline bool boolean() const { return boolean_; }
|
||||
inline bool is_slice() const { return type_ == TensorIndexType::Slice; }
|
||||
inline const Slice& slice() const { return slice_; }
|
||||
inline bool is_tensor() const { return type_ == TensorIndexType::Tensor; }
|
||||
const at::Tensor& tensor() const;
|
||||
|
||||
private:
|
||||
c10::SymInt integer_ = 0;
|
||||
bool boolean_ = false;
|
||||
Slice slice_;
|
||||
std::shared_ptr<at::Tensor> tensor_;
|
||||
TensorIndexType type_;
|
||||
};
|
||||
|
||||
} // namespace at::indexing
|
||||
@@ -0,0 +1,97 @@
|
||||
// Copyright (c) 2026 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 <ATen/Utils.h>
|
||||
|
||||
#include <ATen/ops/empty.h>
|
||||
#include <ATen/ops/to.h>
|
||||
#include <c10/core/Layout.h>
|
||||
#include <c10/core/ScalarType.h>
|
||||
#include <c10/util/ArrayRef.h>
|
||||
#include <c10/util/Exception.h>
|
||||
#include <c10/util/accumulate.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "paddle/common/macros.h"
|
||||
#include "paddle/phi/api/include/sparse_api.h"
|
||||
#include "paddle/phi/api/include/tensor.h"
|
||||
|
||||
namespace at {
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
Tensor tensor_cpu(ArrayRef<T> values, const TensorOptions& options) {
|
||||
constexpr auto native_scalar_type = c10::CppTypeToScalarType<T>::value;
|
||||
auto result = at::empty(values.size(), options.dtype(native_scalar_type));
|
||||
PD_CHECK(result.is_contiguous());
|
||||
std::copy(values.begin(), values.end(), result.template data_ptr<T>());
|
||||
if (options.dtype() != native_scalar_type) {
|
||||
return result.to(at::TensorOptions().dtype(options.dtype()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Tensor tensor_backend(ArrayRef<T> values, const TensorOptions& options) {
|
||||
auto cpu_tensor =
|
||||
tensor_cpu(values, options.device(c10::Device(c10::DeviceType::CPU)));
|
||||
return cpu_tensor.to(options.device());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Tensor tensor_complex_cpu(ArrayRef<T> values, const TensorOptions& options) {
|
||||
constexpr auto native_scalar_type = c10::CppTypeToScalarType<T>::value;
|
||||
auto result = at::empty(values.size(), options.dtype(native_scalar_type));
|
||||
PD_CHECK(result.is_contiguous());
|
||||
std::copy(values.begin(), values.end(), result.template data_ptr<T>());
|
||||
if (options.dtype() != native_scalar_type) {
|
||||
return result.to(at::TensorOptions().dtype(options.dtype()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Tensor tensor_complex_backend(ArrayRef<T> values,
|
||||
const TensorOptions& options) {
|
||||
auto cpu_tensor = tensor_complex_cpu(
|
||||
values, options.device(c10::Device(c10::DeviceType::CPU)));
|
||||
return cpu_tensor.to(options.device());
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
#define TENSOR(T, _1) \
|
||||
PADDLE_API Tensor tensor(ArrayRef<T> values, const TensorOptions& options) { \
|
||||
if (options.device().type() != c10::DeviceType::CPU) { \
|
||||
return at::detail::tensor_backend(values, options); \
|
||||
} else { \
|
||||
return at::detail::tensor_cpu(values, options); \
|
||||
} \
|
||||
}
|
||||
AT_FORALL_SCALAR_TYPES_AND3(Bool, Half, BFloat16, TENSOR)
|
||||
#undef TENSOR
|
||||
|
||||
#define TENSOR(T, _1) \
|
||||
PADDLE_API Tensor tensor(ArrayRef<T> values, const TensorOptions& options) { \
|
||||
if (options.device().type() != c10::DeviceType::CPU) { \
|
||||
return at::detail::tensor_complex_backend(values, options); \
|
||||
} else { \
|
||||
return at::detail::tensor_complex_cpu(values, options); \
|
||||
} \
|
||||
}
|
||||
AT_FORALL_COMPLEX_TYPES(TENSOR)
|
||||
#undef TENSOR
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) 2025 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 <ATen/core/Tensor.h>
|
||||
|
||||
namespace at {
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
Tensor tensor_cpu(ArrayRef<T> values, const TensorOptions& options);
|
||||
|
||||
template <typename T>
|
||||
Tensor tensor_backend(ArrayRef<T> values, const TensorOptions& options);
|
||||
|
||||
template <typename T>
|
||||
Tensor tensor_complex_cpu(ArrayRef<T> values, const TensorOptions& options);
|
||||
|
||||
template <typename T>
|
||||
Tensor tensor_complex_backend(ArrayRef<T> values, const TensorOptions& options);
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,197 @@
|
||||
// Copyright (c) 2026 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.
|
||||
|
||||
// The file has been adapted from pytorch project
|
||||
// Licensed under BSD-style license -
|
||||
// https://github.com/pytorch/pytorch/blob/main/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <c10/core/Device.h>
|
||||
#include <c10/core/DispatchKeySet.h>
|
||||
#include <c10/core/GeneratorImpl.h>
|
||||
#include <c10/util/Exception.h>
|
||||
#include <c10/util/intrusive_ptr.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <mutex> // NOLINT(build/c++11)
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
/**
|
||||
* Note [Generator]
|
||||
* ~~~~~~~~~~~~~~~~
|
||||
* A Pseudo Random Number Generator (PRNG) is an engine that uses an algorithm
|
||||
* to generate a seemingly random sequence of numbers, that may be later be
|
||||
* used in creating a random distribution. Such an engine almost always
|
||||
* maintains a state and requires a seed to start off the creation of random
|
||||
* numbers. Often times, users have found it beneficial to be able to
|
||||
* explicitly create, retain, and destroy PRNG states and also be able to
|
||||
* have control over the seed value.
|
||||
*
|
||||
* A Generator in ATen gives users the ability to read, write and modify a
|
||||
* PRNG engine. For instance, it does so by letting users seed a PRNG engine,
|
||||
* fork the state of the engine, etc.
|
||||
*
|
||||
* By default, there is one generator per device, and a device's generator is
|
||||
* lazily created. A user can use the torch.Generator() api to create their
|
||||
* own generator.
|
||||
*
|
||||
* This implementation wraps Paddle's phi::Generator via c10::GeneratorImpl.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Note [Acquire lock when using random generators]
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
* Generator and its derived classes are NOT thread-safe. Please use the
|
||||
* public mutex_ when using any methods from these classes, except for the
|
||||
* read-only methods.
|
||||
*/
|
||||
|
||||
// Forward declare PyObject if not already available.
|
||||
#ifndef PyObject
|
||||
struct _object;
|
||||
using PyObject = _object;
|
||||
#endif
|
||||
|
||||
namespace at {
|
||||
|
||||
using c10::Device;
|
||||
using c10::DispatchKeySet;
|
||||
|
||||
class Tensor;
|
||||
|
||||
struct Generator {
|
||||
Generator() = default;
|
||||
|
||||
explicit Generator(c10::intrusive_ptr<c10::GeneratorImpl> gen_impl)
|
||||
: impl_(std::move(gen_impl)) {
|
||||
TORCH_CHECK(impl_.get(), "GeneratorImpl with nullptr is not supported");
|
||||
}
|
||||
|
||||
bool operator==(const Generator& rhs) const {
|
||||
return this->impl_ == rhs.impl_;
|
||||
}
|
||||
|
||||
bool operator!=(const Generator& rhs) const { return !((*this) == rhs); }
|
||||
|
||||
bool defined() const { return static_cast<bool>(impl_); }
|
||||
|
||||
c10::GeneratorImpl* unsafeGetGeneratorImpl() const { return impl_.get(); }
|
||||
|
||||
c10::GeneratorImpl* unsafeReleaseGeneratorImpl() { return impl_.release(); }
|
||||
|
||||
const c10::intrusive_ptr<c10::GeneratorImpl>& getIntrusivePtr() const {
|
||||
return impl_;
|
||||
}
|
||||
|
||||
void set_current_seed(uint64_t seed) { impl_->set_current_seed(seed); }
|
||||
|
||||
/// Sets the offset of Generator state to the desired offset.
|
||||
/// Supported for Philox based Generators (CUDA / MPS).
|
||||
void set_offset(uint64_t offset) { impl_->set_offset(offset); }
|
||||
|
||||
/// Returns the offset of Generator state.
|
||||
/// Supported for Philox based Generators (CUDA / MPS).
|
||||
uint64_t get_offset() const { return impl_->get_offset(); }
|
||||
|
||||
uint64_t current_seed() const { return impl_->current_seed(); }
|
||||
|
||||
uint64_t seed() { return impl_->seed(); }
|
||||
|
||||
// ----- state transfer (not inlined to break header cycles) ----------------
|
||||
// These methods mirror PyTorch's set_state / get_state which operate on
|
||||
// serialised byte tensors. In the Paddle compat layer we provide a simpler
|
||||
// state-copy semantic through graphsafe_set_state / graphsafe_get_state.
|
||||
|
||||
/// Copy the full PRNG state from another Generator.
|
||||
void graphsafe_set_state(const Generator& src) {
|
||||
TORCH_CHECK(src.defined(), "Source generator is not defined");
|
||||
TORCH_CHECK(defined(), "Target generator is not defined");
|
||||
auto src_state = src.impl_->paddle_generator()->GetState();
|
||||
impl_->paddle_generator()->SetState(src_state);
|
||||
}
|
||||
|
||||
/// Obtain a Generator whose state is a snapshot (clone) of this one.
|
||||
Generator graphsafe_get_state() const {
|
||||
TORCH_CHECK(defined(), "Generator is not defined");
|
||||
return clone();
|
||||
}
|
||||
|
||||
std::mutex& mutex() { return impl_->mutex_; }
|
||||
|
||||
DispatchKeySet key_set() const { return impl_->key_set(); }
|
||||
|
||||
Device device() const { return impl_->device(); }
|
||||
|
||||
inline void set_pyobj(PyObject* pyobj) const noexcept {
|
||||
impl_->set_pyobj(pyobj);
|
||||
}
|
||||
|
||||
inline PyObject* pyobj() const noexcept { return impl_->pyobj(); }
|
||||
|
||||
template <typename T>
|
||||
T* get() const {
|
||||
return static_cast<T*>(impl_.get());
|
||||
}
|
||||
|
||||
Generator clone() const { return Generator(impl_->clone()); }
|
||||
|
||||
/// Access the underlying Paddle phi::Generator (convenience).
|
||||
std::shared_ptr<phi::Generator> paddle_generator() const {
|
||||
return impl_->paddle_generator();
|
||||
}
|
||||
|
||||
private:
|
||||
c10::intrusive_ptr<c10::GeneratorImpl> impl_;
|
||||
};
|
||||
|
||||
template <class Impl, class... Args>
|
||||
Generator make_generator(Args&&... args) {
|
||||
return Generator(c10::make_intrusive<Impl>(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function to static cast input Generator to
|
||||
* the backend generator type (CPUGeneratorImpl / CUDAGeneratorImpl etc.)
|
||||
*/
|
||||
template <typename T>
|
||||
inline T* check_generator(std::optional<Generator> gen) {
|
||||
TORCH_CHECK(gen.has_value(), "Expected Generator but received nullopt");
|
||||
TORCH_CHECK(gen->defined(),
|
||||
"Generator with undefined implementation is not allowed");
|
||||
TORCH_CHECK(
|
||||
T::device_type() == gen->device().type(),
|
||||
"Expected a generator for ",
|
||||
phi::AllocationTypeStr(c10::DeviceTypeToPhi(T::device_type())),
|
||||
" but found one for ",
|
||||
phi::AllocationTypeStr(c10::DeviceTypeToPhi(gen->device().type())));
|
||||
return gen->get<T>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function used in tensor implementations, which supplies the
|
||||
* default generator to tensors if an input generator is not supplied.
|
||||
* The input Generator is also static-cast to the backend generator type.
|
||||
*/
|
||||
template <typename T>
|
||||
inline T* get_generator_or_default(const std::optional<Generator>& gen,
|
||||
const Generator& default_gen) {
|
||||
return gen.has_value() && gen->defined() ? check_generator<T>(gen)
|
||||
: check_generator<T>(default_gen);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) 2025 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 <c10/core/Scalar.h>
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright (c) 2025 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 <ATen/core/TensorBody.h>
|
||||
@@ -0,0 +1,104 @@
|
||||
// Copyright (c) 2025 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.
|
||||
|
||||
// The file has been adapted from pytorch project
|
||||
// Licensed under BSD-style license -
|
||||
// https://github.com/pytorch/pytorch/blob/main/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <torch/headeronly/core/TensorAccessor.h>
|
||||
|
||||
#include <c10/macros/Macros.h>
|
||||
#include <c10/util/ArrayRef.h>
|
||||
#include <c10/util/Exception.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
namespace at {
|
||||
|
||||
using torch::headeronly::DefaultPtrTraits;
|
||||
#if defined(__CUDACC__) || defined(__HIPCC__)
|
||||
using torch::headeronly::RestrictPtrTraits;
|
||||
#endif
|
||||
|
||||
template <typename T,
|
||||
size_t N,
|
||||
template <typename U> class PtrTraits = DefaultPtrTraits,
|
||||
typename index_t = int64_t>
|
||||
using TensorAccessorBase = torch::headeronly::detail::
|
||||
TensorAccessorBase<c10::IntArrayRef, T, N, PtrTraits, index_t>;
|
||||
|
||||
template <typename T,
|
||||
size_t N,
|
||||
template <typename U> class PtrTraits = DefaultPtrTraits,
|
||||
typename index_t = int64_t>
|
||||
using TensorAccessor = torch::headeronly::detail::
|
||||
TensorAccessor<c10::IntArrayRef, T, N, PtrTraits, index_t>;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <size_t N, typename index_t>
|
||||
struct IndexBoundsCheck {
|
||||
explicit IndexBoundsCheck(index_t i) {
|
||||
TORCH_CHECK(0 <= i && i < index_t{N},
|
||||
"Index ",
|
||||
i,
|
||||
" is not within bounds of a tensor of dimension ",
|
||||
N);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename T,
|
||||
size_t N,
|
||||
template <typename U> class PtrTraits = DefaultPtrTraits,
|
||||
typename index_t = int64_t>
|
||||
using GenericPackedTensorAccessorBase =
|
||||
torch::headeronly::detail::GenericPackedTensorAccessorBase<
|
||||
detail::IndexBoundsCheck<N, index_t>,
|
||||
T,
|
||||
N,
|
||||
PtrTraits,
|
||||
index_t>;
|
||||
|
||||
template <typename T,
|
||||
size_t N,
|
||||
template <typename U> class PtrTraits = DefaultPtrTraits,
|
||||
typename index_t = int64_t>
|
||||
using GenericPackedTensorAccessor =
|
||||
torch::headeronly::detail::GenericPackedTensorAccessor<
|
||||
TensorAccessor<T, N - 1, PtrTraits, index_t>,
|
||||
detail::IndexBoundsCheck<N, index_t>,
|
||||
T,
|
||||
N,
|
||||
PtrTraits,
|
||||
index_t>;
|
||||
|
||||
template <typename T,
|
||||
size_t N,
|
||||
template <typename U> class PtrTraits = DefaultPtrTraits>
|
||||
using PackedTensorAccessor32 =
|
||||
GenericPackedTensorAccessor<T, N, PtrTraits, int32_t>;
|
||||
|
||||
template <typename T,
|
||||
size_t N,
|
||||
template <typename U> class PtrTraits = DefaultPtrTraits>
|
||||
using PackedTensorAccessor64 =
|
||||
GenericPackedTensorAccessor<T, N, PtrTraits, int64_t>;
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,578 @@
|
||||
// Copyright (c) 2025 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 <ATen/core/TensorAccessor.h>
|
||||
#include <c10/core/Device.h>
|
||||
#include <c10/core/Layout.h>
|
||||
#include <c10/core/MemoryFormat.h>
|
||||
#include <c10/core/Scalar.h>
|
||||
#include <c10/core/ScalarType.h>
|
||||
#include <c10/core/Storage.h>
|
||||
#include <c10/core/SymInt.h>
|
||||
#include <c10/core/SymIntArrayRef.h>
|
||||
#include <c10/core/TensorOptions.h>
|
||||
#include <utils/int_array_ref_conversion.h>
|
||||
#include <utils/scalar_type_conversion.h>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "paddle/common/layout.h"
|
||||
#include "paddle/phi/api/include/api.h"
|
||||
#include "paddle/phi/api/include/tensor.h"
|
||||
#include "paddle/phi/common/place.h"
|
||||
#include "paddle/phi/core/dense_tensor.h"
|
||||
|
||||
namespace at {
|
||||
using PaddleTensor = paddle::Tensor;
|
||||
|
||||
class PADDLE_API TensorBase {
|
||||
public:
|
||||
TensorBase() = default;
|
||||
explicit TensorBase(const PaddleTensor& tensor) : tensor_(tensor) {
|
||||
InitStorage();
|
||||
}
|
||||
TensorBase(const TensorBase&) = default;
|
||||
TensorBase(TensorBase&&) noexcept = default;
|
||||
~TensorBase() noexcept = default;
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
TensorBase& operator=(const TensorBase& x) & {
|
||||
tensor_ = x.tensor_;
|
||||
storage_ = x.storage_;
|
||||
return *this;
|
||||
}
|
||||
TensorBase& operator=(TensorBase&& x) & noexcept {
|
||||
tensor_ = std::move(x.tensor_);
|
||||
storage_ = std::move(x.storage_);
|
||||
return *this;
|
||||
}
|
||||
#else
|
||||
TensorBase& operator=(const TensorBase& x) & = default;
|
||||
TensorBase& operator=(TensorBase&& x) & noexcept = default;
|
||||
#endif
|
||||
|
||||
TensorBase& operator=(const TensorBase&) && = delete;
|
||||
TensorBase& operator=(TensorBase&&) && noexcept = delete;
|
||||
|
||||
bool is_same(const TensorBase& other) const noexcept {
|
||||
return tensor_.impl().get() == other.tensor_.impl().get();
|
||||
}
|
||||
size_t use_count() const noexcept { return tensor_.impl().use_count(); }
|
||||
size_t weak_use_count() const noexcept {
|
||||
// PyTorch exposes an internal self weak-reference on live TensorImpls, so
|
||||
// the observable weak count starts at 1 even without user-created refs.
|
||||
return tensor_.defined() ? 1 : 0;
|
||||
}
|
||||
|
||||
void print() const {
|
||||
if (defined()) {
|
||||
std::cerr << '[' << toString() << ' ' << sizes() << ']' << '\n';
|
||||
} else {
|
||||
std::cerr << "[UndefinedTensor]" << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
std::string toString() const {
|
||||
if (!tensor_.defined()) {
|
||||
return "UndefinedType";
|
||||
}
|
||||
|
||||
std::string backend_str;
|
||||
const auto& place = tensor_.place();
|
||||
|
||||
if (phi::is_cpu_place(place)) {
|
||||
backend_str = "CPU";
|
||||
} else if (phi::is_gpu_place(place)) {
|
||||
backend_str = "CUDA";
|
||||
} else {
|
||||
backend_str = "Undefined";
|
||||
}
|
||||
|
||||
std::string scalar_type_str = at::toString(scalar_type());
|
||||
|
||||
return backend_str + scalar_type_str + "Type";
|
||||
}
|
||||
|
||||
// Returns the tensor's current data pointer. Storage mutations flow through
|
||||
// the compat holder view, so tensor.data_ptr() stays aligned with storage()
|
||||
// while preserving tensor-specific offsets for views.
|
||||
void* data_ptr() const {
|
||||
if (!tensor_.defined()) {
|
||||
return nullptr;
|
||||
}
|
||||
return const_cast<void*>(tensor_.data());
|
||||
}
|
||||
template <typename T>
|
||||
T* data_ptr() const {
|
||||
return static_cast<T*>(data_ptr());
|
||||
}
|
||||
|
||||
const void* const_data_ptr() const { return data_ptr(); }
|
||||
|
||||
template <typename T>
|
||||
const T* const_data_ptr() const;
|
||||
|
||||
void* mutable_data_ptr() const { return data_ptr(); }
|
||||
|
||||
template <typename T>
|
||||
T* mutable_data_ptr() const;
|
||||
|
||||
int64_t stride(int64_t dim) const {
|
||||
if (dim < 0) {
|
||||
dim += tensor_.strides().size();
|
||||
}
|
||||
return tensor_.strides()[static_cast<int>(dim)];
|
||||
}
|
||||
|
||||
c10::SymInt sym_stride(int64_t dim) const {
|
||||
return static_cast<c10::SymInt>(stride(dim));
|
||||
}
|
||||
|
||||
c10::IntArrayRef strides() const {
|
||||
return compat::_PD_PhiDDimToIntArrayRef(tensor_.strides());
|
||||
}
|
||||
|
||||
c10::SymIntArrayRef sym_strides() const {
|
||||
return c10::SymIntArrayRef(
|
||||
reinterpret_cast<const c10::SymInt*>(strides().data()),
|
||||
strides().size());
|
||||
}
|
||||
|
||||
int64_t size(int64_t dim) const {
|
||||
if (dim < 0) {
|
||||
dim += tensor_.dims().size();
|
||||
}
|
||||
return tensor_.dims()[static_cast<int>(dim)];
|
||||
}
|
||||
|
||||
c10::SymInt sym_size(int64_t dim) const {
|
||||
return static_cast<c10::SymInt>(size(dim));
|
||||
}
|
||||
|
||||
c10::IntArrayRef sizes() const {
|
||||
return compat::_PD_PhiDDimToIntArrayRef(tensor_.dims());
|
||||
}
|
||||
|
||||
c10::SymIntArrayRef sym_sizes() const {
|
||||
return c10::SymIntArrayRef(
|
||||
reinterpret_cast<const c10::SymInt*>(sizes().data()), sizes().size());
|
||||
}
|
||||
|
||||
int64_t numel() const { return tensor_.numel(); }
|
||||
|
||||
c10::SymInt sym_numel() const { return static_cast<c10::SymInt>(numel()); }
|
||||
|
||||
caffe2::TypeMeta dtype() const {
|
||||
return caffe2::TypeMeta::fromScalarType(
|
||||
compat::_PD_PhiDataTypeToAtenScalarType(tensor_.dtype()));
|
||||
}
|
||||
|
||||
c10::Device device() const { return c10::Device(tensor_.place()); }
|
||||
c10::DeviceIndex get_device() const {
|
||||
return c10::Device(tensor_.place()).index();
|
||||
}
|
||||
|
||||
int64_t dim() const { return tensor_.dims().size(); }
|
||||
int64_t ndimension() const { return dim(); }
|
||||
|
||||
at::TensorBase contiguous(
|
||||
c10::MemoryFormat memory_format = c10::MemoryFormat::Contiguous) const {
|
||||
PD_CHECK(memory_format == c10::MemoryFormat::Contiguous,
|
||||
"`MemoryFormat` other than Contiguous");
|
||||
|
||||
return TensorBase(tensor_.contiguous());
|
||||
}
|
||||
|
||||
bool is_contiguous(
|
||||
at::MemoryFormat memory_format = at::MemoryFormat::Contiguous) const {
|
||||
PD_CHECK(memory_format == c10::MemoryFormat::Contiguous,
|
||||
"`MemoryFormat` other than Contiguous");
|
||||
|
||||
return tensor_.is_contiguous();
|
||||
}
|
||||
|
||||
bool is_non_overlapping_and_dense() const {
|
||||
if (numel() <= 1) {
|
||||
return true;
|
||||
}
|
||||
if (tensor_.is_contiguous()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// For non-contiguous tensors, verify sorted strides form a valid dense
|
||||
// layout without gaps or overlaps.
|
||||
auto sizes_vec = sizes();
|
||||
auto strides_vec = strides();
|
||||
int64_t ndim = dim();
|
||||
|
||||
std::vector<int64_t> perm(ndim);
|
||||
for (int64_t i = 0; i < ndim; ++i) {
|
||||
perm[i] = i;
|
||||
}
|
||||
std::sort(perm.begin(), perm.end(), [&](int64_t a, int64_t b) {
|
||||
return strides_vec[a] < strides_vec[b];
|
||||
});
|
||||
|
||||
int64_t expected_stride = 1;
|
||||
for (int64_t i = 0; i < ndim; ++i) {
|
||||
int64_t dim_idx = perm[i];
|
||||
if (sizes_vec[dim_idx] == 0) {
|
||||
return true;
|
||||
}
|
||||
if (sizes_vec[dim_idx] == 1) {
|
||||
continue;
|
||||
}
|
||||
if (strides_vec[dim_idx] != expected_stride) {
|
||||
return false;
|
||||
}
|
||||
expected_stride *= sizes_vec[dim_idx];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_contiguous_or_false(
|
||||
at::MemoryFormat memory_format = at::MemoryFormat::Contiguous) const {
|
||||
PD_CHECK(memory_format == c10::MemoryFormat::Contiguous,
|
||||
"`MemoryFormat` other than Contiguous");
|
||||
|
||||
return tensor_.is_contiguous();
|
||||
}
|
||||
|
||||
c10::ScalarType scalar_type() const {
|
||||
return compat::_PD_PhiDataTypeToAtenScalarType(tensor_.dtype());
|
||||
}
|
||||
|
||||
bool has_names() const {
|
||||
// In PyTorch, has_names() is used to check if any dimension has names.
|
||||
// In Paddle, we don't support named dimension yet, so always return false.
|
||||
return false;
|
||||
}
|
||||
|
||||
TensorOptions options() const {
|
||||
return TensorOptions().dtype(dtype()).device(device()).layout(layout());
|
||||
}
|
||||
|
||||
const TensorBase& fill_(const at::Scalar& scalar) const {
|
||||
paddle::experimental::fill_(const_cast<PaddleTensor&>(tensor_), scalar);
|
||||
return *this;
|
||||
}
|
||||
|
||||
const TensorBase& zero_() const {
|
||||
paddle::experimental::fill_(const_cast<PaddleTensor&>(tensor_), 0.0);
|
||||
return *this;
|
||||
}
|
||||
|
||||
at::TensorBase to(
|
||||
at::TensorOptions options = {},
|
||||
bool non_blocking = false,
|
||||
bool copy = false,
|
||||
std::optional<at::MemoryFormat> memory_format = std::nullopt) const {
|
||||
if (options.device_opt().has_value()) {
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"The `to` method with device option is not supported yet."));
|
||||
}
|
||||
if (memory_format.has_value()) {
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"The `to` method with memory_format option is not supported yet."));
|
||||
}
|
||||
return TensorBase(paddle::experimental::cast(
|
||||
tensor_, compat::_PD_AtenScalarTypeToPhiDataType(options.dtype())));
|
||||
}
|
||||
|
||||
bool is_complex() const { return at::isComplexType(this->scalar_type()); }
|
||||
|
||||
bool is_floating_point() const {
|
||||
return at::isFloatingType(this->scalar_type());
|
||||
}
|
||||
|
||||
bool is_signed() const { return at::isSignedType(this->scalar_type()); }
|
||||
|
||||
bool is_cpu() const { return phi::is_cpu_place(tensor_.place()); }
|
||||
bool is_cuda() const { return phi::is_gpu_place(tensor_.place()); }
|
||||
|
||||
bool is_sparse() const {
|
||||
return tensor_.is_sparse_coo_tensor() || tensor_.is_sparse_csr_tensor();
|
||||
}
|
||||
|
||||
bool is_sparse_csr() const { return tensor_.is_sparse_csr_tensor(); }
|
||||
|
||||
inline size_t nbytes() const {
|
||||
PD_CHECK(
|
||||
((tensor_.layout() != common::DataLayout::SPARSE_COO) &&
|
||||
(tensor_.layout() != common::DataLayout::SPARSE_CSR)),
|
||||
"nbytes is not defined for sparse tensors. If you want the size of "
|
||||
"the constituent "
|
||||
"tensors, add the nbytes of the indices and values. If you want the "
|
||||
"size of the "
|
||||
"equivalent dense tensor, multiply numel() by element_size()");
|
||||
return tensor_.numel() * SizeOf(tensor_.dtype());
|
||||
}
|
||||
|
||||
size_t itemsize() const { return SizeOf(tensor_.dtype()); }
|
||||
|
||||
int64_t element_size() const {
|
||||
return static_cast<int64_t>(SizeOf(tensor_.dtype()));
|
||||
}
|
||||
|
||||
bool defined() const { return tensor_.defined(); }
|
||||
|
||||
Layout layout() const {
|
||||
switch (tensor_.layout()) {
|
||||
case common::DataLayout::STRIDED:
|
||||
case common::DataLayout::NCHW:
|
||||
case common::DataLayout::NHWC:
|
||||
case common::DataLayout::NCDHW:
|
||||
case common::DataLayout::NDHWC:
|
||||
return c10::kStrided;
|
||||
case common::DataLayout::SPARSE_COO:
|
||||
return c10::kSparse;
|
||||
case common::DataLayout::SPARSE_CSR:
|
||||
return c10::kSparseCsr;
|
||||
case common::DataLayout::ONEDNN:
|
||||
return c10::kMkldnn;
|
||||
default:
|
||||
return c10::kStrided;
|
||||
}
|
||||
}
|
||||
|
||||
void reset() {
|
||||
tensor_.reset();
|
||||
storage_.reset();
|
||||
}
|
||||
|
||||
int64_t storage_offset() const {
|
||||
// Paddle DenseTensor stores offset in meta_.offset (in bytes)
|
||||
// We need to convert to element offset
|
||||
auto dense_tensor =
|
||||
std::dynamic_pointer_cast<phi::DenseTensor>(tensor_.impl());
|
||||
if (dense_tensor) {
|
||||
size_t byte_offset = dense_tensor->meta().offset;
|
||||
size_t element_size = SizeOf(tensor_.dtype());
|
||||
return element_size > 0 ? static_cast<int64_t>(byte_offset / element_size)
|
||||
: 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
c10::SymInt sym_storage_offset() const {
|
||||
return c10::SymInt(storage_offset());
|
||||
}
|
||||
|
||||
bool has_storage() const {
|
||||
SyncStorageFromTensor();
|
||||
return tensor_.defined() && storage_ && storage_->valid();
|
||||
}
|
||||
|
||||
// Returns a Storage handle backed by the shared StorageImpl for this tensor.
|
||||
const c10::Storage& storage() const {
|
||||
SyncStorageFromTensor();
|
||||
static const c10::Storage kEmptyStorage;
|
||||
return storage_ ? *storage_ : kEmptyStorage;
|
||||
}
|
||||
|
||||
bool is_alias_of(const at::TensorBase& other) const {
|
||||
return this->storage().is_alias_of(other.storage());
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename DenseT>
|
||||
static auto MaybeResetHolder(DenseT* dense,
|
||||
const std::shared_ptr<phi::Allocation>& holder,
|
||||
int)
|
||||
-> decltype(dense->ResetHolder(holder), void()) {
|
||||
dense->ResetHolder(holder);
|
||||
}
|
||||
|
||||
static void MaybeResetHolder(phi::DenseTensor* dense,
|
||||
const std::shared_ptr<phi::Allocation>& holder,
|
||||
long) { // NOLINT
|
||||
TORCH_CHECK(dense != nullptr, "DenseTensor must not be null");
|
||||
|
||||
// External custom-kernel builds do not expose ResetHolder(), but Holder()
|
||||
// still returns the live holder reference used by DenseTensor.
|
||||
if (dense->numel() == 0) {
|
||||
auto& meta = const_cast<phi::DenseTensorMeta&>(dense->meta());
|
||||
meta.offset = 0;
|
||||
const_cast<std::shared_ptr<phi::Allocation>&>(dense->Holder()) = holder;
|
||||
return;
|
||||
}
|
||||
|
||||
if (dense->Holder() && dense->meta().is_contiguous()) {
|
||||
TORCH_CHECK(holder != nullptr, "Holder must not be null.");
|
||||
const auto required_bytes =
|
||||
dense->numel() * static_cast<int64_t>(phi::SizeOf(dense->dtype())) +
|
||||
static_cast<int64_t>(dense->meta().offset);
|
||||
TORCH_CHECK(required_bytes <= static_cast<int64_t>(holder->size()),
|
||||
"The size of Holder is not enough to store the Tensor.");
|
||||
}
|
||||
const_cast<std::shared_ptr<phi::Allocation>&>(dense->Holder()) = holder;
|
||||
}
|
||||
|
||||
void InitStorage() { SyncStorageFromTensor(); }
|
||||
|
||||
static std::shared_ptr<c10::Storage> GetOrCreateCanonicalStorage(
|
||||
c10::Storage&& live_storage) {
|
||||
auto impl = live_storage.get_impl();
|
||||
if (!impl) {
|
||||
return std::make_shared<c10::Storage>(std::move(live_storage));
|
||||
}
|
||||
|
||||
static std::mutex registry_mu;
|
||||
static std::unordered_map<c10::StorageImpl*, std::weak_ptr<c10::Storage>>
|
||||
registry;
|
||||
|
||||
std::lock_guard<std::mutex> guard(registry_mu);
|
||||
auto it = registry.find(impl.get());
|
||||
if (it != registry.end()) {
|
||||
if (auto cached = it->second.lock()) {
|
||||
return cached;
|
||||
}
|
||||
registry.erase(it);
|
||||
}
|
||||
|
||||
auto created = std::make_shared<c10::Storage>(std::move(live_storage));
|
||||
registry.emplace(impl.get(), created);
|
||||
return created;
|
||||
}
|
||||
|
||||
void SyncStorageFromTensor() const {
|
||||
auto dense = std::dynamic_pointer_cast<phi::DenseTensor>(tensor_.impl());
|
||||
if (!dense) {
|
||||
storage_.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
auto holder = dense->Holder();
|
||||
if (!holder) {
|
||||
storage_.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
c10::Storage live_storage = c10::Storage::createTensorStorage(holder);
|
||||
auto compat_holder = live_storage.ensureTensorHolder();
|
||||
if (holder != compat_holder) {
|
||||
MaybeResetHolder(dense.get(), compat_holder, 0);
|
||||
}
|
||||
|
||||
if (!storage_ || storage_->get_impl() != live_storage.get_impl()) {
|
||||
storage_ = GetOrCreateCanonicalStorage(std::move(live_storage));
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
// Return a `TensorAccessor` for CPU `Tensor`s. You have to specify scalar
|
||||
// type and
|
||||
// dimension.
|
||||
template <typename T, size_t N>
|
||||
TensorAccessor<T, N> accessor() const& {
|
||||
static_assert(
|
||||
N > 0,
|
||||
"accessor is used for indexing tensor, for scalars use *data_ptr<T>()");
|
||||
TORCH_CHECK(dim() == N,
|
||||
"TensorAccessor expected ",
|
||||
N,
|
||||
" dims but tensor has ",
|
||||
dim());
|
||||
T* ptr = nullptr;
|
||||
if constexpr (std::is_const_v<T>) {
|
||||
ptr = const_data_ptr<T>();
|
||||
} else {
|
||||
ptr = mutable_data_ptr<T>();
|
||||
}
|
||||
return TensorAccessor<T, N>(ptr, sizes().data(), strides().data());
|
||||
}
|
||||
template <typename T, size_t N>
|
||||
TensorAccessor<T, N> accessor() && = delete;
|
||||
|
||||
// Return a `GenericPackedTensorAccessor` for CUDA `Tensor`s. You have to
|
||||
// specify scalar type and dimension. You can optionally specify
|
||||
// RestrictPtrTraits as a template parameter to cast the data pointer to a
|
||||
// __restrict__ pointer. In order to use this, your CUDA kernel has to take a
|
||||
// corresponding GenericPackedTensorAccessor as an argument.
|
||||
template <typename T,
|
||||
size_t N,
|
||||
template <typename U> class PtrTraits = DefaultPtrTraits,
|
||||
typename index_t = int64_t>
|
||||
GenericPackedTensorAccessor<T, N, PtrTraits, index_t>
|
||||
generic_packed_accessor() const& {
|
||||
static_assert(
|
||||
N > 0,
|
||||
"accessor is used for indexing tensor, for scalars use *data_ptr<T>()");
|
||||
TORCH_CHECK(dim() == N,
|
||||
"TensorAccessor expected ",
|
||||
N,
|
||||
" dims but tensor has ",
|
||||
dim());
|
||||
T* ptr = nullptr;
|
||||
if constexpr (std::is_const_v<T>) {
|
||||
ptr = const_data_ptr<T>();
|
||||
} else {
|
||||
ptr = mutable_data_ptr<T>();
|
||||
}
|
||||
return GenericPackedTensorAccessor<T, N, PtrTraits, index_t>(
|
||||
static_cast<typename PtrTraits<T>::PtrType>(ptr),
|
||||
sizes().data(),
|
||||
strides().data());
|
||||
}
|
||||
template <typename T,
|
||||
size_t N,
|
||||
template <typename U> class PtrTraits = DefaultPtrTraits,
|
||||
typename index_t = int64_t>
|
||||
GenericPackedTensorAccessor<T, N> generic_packed_accessor() && = delete;
|
||||
|
||||
template <typename T,
|
||||
size_t N,
|
||||
template <typename U> class PtrTraits = DefaultPtrTraits>
|
||||
PackedTensorAccessor32<T, N, PtrTraits> packed_accessor32() const& {
|
||||
TORCH_CHECK(
|
||||
numel() <= static_cast<int64_t>(std::numeric_limits<int32_t>::max()),
|
||||
"numel needs to be smaller than int32_t max; otherwise, please use "
|
||||
"packed_accessor64");
|
||||
return generic_packed_accessor<T, N, PtrTraits, int32_t>();
|
||||
}
|
||||
template <typename T,
|
||||
size_t N,
|
||||
template <typename U> class PtrTraits = DefaultPtrTraits>
|
||||
PackedTensorAccessor32<T, N, PtrTraits> packed_accessor32() && = delete;
|
||||
|
||||
template <typename T,
|
||||
size_t N,
|
||||
template <typename U> class PtrTraits = DefaultPtrTraits>
|
||||
PackedTensorAccessor64<T, N, PtrTraits> packed_accessor64() const& {
|
||||
return generic_packed_accessor<T, N, PtrTraits, int64_t>();
|
||||
}
|
||||
template <typename T,
|
||||
size_t N,
|
||||
template <typename U> class PtrTraits = DefaultPtrTraits>
|
||||
PackedTensorAccessor64<T, N, PtrTraits> packed_accessor64() && = delete;
|
||||
|
||||
const PaddleTensor& _PD_GetInner() const& { return tensor_; }
|
||||
PaddleTensor& _PD_GetInner() & { return tensor_; }
|
||||
PaddleTensor&& _PD_GetInner() && { return std::move(tensor_); }
|
||||
|
||||
protected:
|
||||
PaddleTensor tensor_;
|
||||
// Cache a canonical Storage object shared by wrappers that reference the
|
||||
// same StorageImpl. This prevents independently-constructed wrappers around
|
||||
// one tensor impl from inflating Storage::use_count().
|
||||
mutable std::shared_ptr<c10::Storage> storage_;
|
||||
};
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,846 @@
|
||||
// Copyright (c) 2025 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 <ATen/TensorIndexing.h>
|
||||
#include <ATen/core/TensorBase.h>
|
||||
#include <c10/core/Backend.h>
|
||||
#include <c10/core/List.h>
|
||||
#include <c10/core/Scalar.h>
|
||||
#include <c10/core/ScalarType.h>
|
||||
#include <c10/core/Stream.h>
|
||||
#include <c10/core/SymIntArrayRef.h>
|
||||
#include <c10/util/OptionalArrayRef.h>
|
||||
#include "paddle/phi/api/include/api.h"
|
||||
#include "paddle/phi/api/include/tensor.h"
|
||||
#include "paddle/phi/common/int_array.h"
|
||||
#include "paddle/phi/common/scalar.h"
|
||||
#include "paddle/phi/core/dense_tensor.h"
|
||||
#include "paddle/phi/core/memory/malloc.h"
|
||||
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
#include <hip/hip_runtime.h>
|
||||
#elif defined(PADDLE_WITH_CUDA)
|
||||
#include <cuda_runtime_api.h>
|
||||
#endif
|
||||
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "paddle/common/ddim.h"
|
||||
#include "paddle/phi/common/place.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
|
||||
namespace at {
|
||||
class Tensor;
|
||||
|
||||
// Type aliases for ATen compatibility
|
||||
using Scalar = c10::Scalar;
|
||||
using TensorOptions = c10::TensorOptions;
|
||||
using MemoryFormat = c10::MemoryFormat;
|
||||
using IntArrayRef = c10::IntArrayRef;
|
||||
using OptionalIntArrayRef = c10::OptionalIntArrayRef;
|
||||
using ScalarType = c10::ScalarType;
|
||||
using TensorList = c10::ArrayRef<Tensor>;
|
||||
using ITensorListRef = c10::ArrayRef<Tensor>;
|
||||
} // namespace at
|
||||
|
||||
namespace at { // NOLINT(build/namespaces)
|
||||
using PaddleTensor = paddle::Tensor;
|
||||
using PaddlePlace = phi::Place;
|
||||
|
||||
// Stub for DimnameList (not supported in Paddle)
|
||||
using DimnameList = c10::ArrayRef<std::string>;
|
||||
|
||||
using Stream = c10::Stream;
|
||||
|
||||
class Tensor : public TensorBase {
|
||||
public:
|
||||
Tensor() = default;
|
||||
Tensor(const PaddleTensor& tensor) : TensorBase(tensor){}; // NOLINT
|
||||
Tensor(const Tensor& tensor) = default;
|
||||
Tensor(Tensor&& tensor) = default;
|
||||
|
||||
// Implicitly move-constructible from TensorBase, but must be explicit to
|
||||
// increase refcount
|
||||
explicit Tensor(const TensorBase& base) : TensorBase(base) {} // NOLINT
|
||||
/*implicit*/ Tensor(TensorBase&& base) // NOLINT
|
||||
: TensorBase(std::move(base)) {}
|
||||
|
||||
Tensor& operator=(const PaddleTensor& x) & noexcept {
|
||||
tensor_ = x;
|
||||
return *this;
|
||||
}
|
||||
Tensor& operator=(const TensorBase& x) & noexcept {
|
||||
const PaddleTensor& inner = x._PD_GetInner();
|
||||
tensor_ = inner;
|
||||
return *this;
|
||||
}
|
||||
Tensor& operator=(PaddleTensor&& x) & noexcept {
|
||||
tensor_ = std::move(x);
|
||||
return *this;
|
||||
}
|
||||
Tensor& operator=(TensorBase&& x) & noexcept {
|
||||
tensor_ = std::move(x)._PD_GetInner();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Tensor& operator=(const Tensor& x) & noexcept {
|
||||
return operator=(static_cast<const TensorBase&>(x));
|
||||
}
|
||||
Tensor& operator=(Tensor&& x) & noexcept {
|
||||
return operator=(static_cast<TensorBase&&>(x));
|
||||
}
|
||||
Tensor& operator=(const Scalar& v) && {
|
||||
fill_(v);
|
||||
return *this;
|
||||
}
|
||||
Tensor& operator=(const Tensor& rhs) && {
|
||||
copy_(rhs);
|
||||
return *this;
|
||||
}
|
||||
Tensor& operator=(Tensor&& rhs) && {
|
||||
copy_(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* data() const {
|
||||
return data_ptr<T>();
|
||||
}
|
||||
|
||||
Tensor toBackend(c10::Backend b) const {
|
||||
switch (b) {
|
||||
case c10::Backend::CPU:
|
||||
return tensor_.copy_to(PaddlePlace(phi::AllocationType::CPU), true);
|
||||
case c10::Backend::CUDA:
|
||||
return tensor_.copy_to(paddle::DefaultGPUPlace(), true);
|
||||
case c10::Backend::XPU:
|
||||
return tensor_.copy_to(paddle::DefaultXPUPlace(), true);
|
||||
case c10::Backend::IPU:
|
||||
return tensor_.copy_to(PaddlePlace(phi::IPUPlace()), true);
|
||||
default:
|
||||
PD_CHECK(false, "Unsupported backend");
|
||||
}
|
||||
return tensor_;
|
||||
}
|
||||
|
||||
Tensor cpu() const {
|
||||
PaddlePlace place(phi::AllocationType::CPU);
|
||||
return tensor_.copy_to(place, true);
|
||||
}
|
||||
|
||||
Tensor cuda() const {
|
||||
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
|
||||
auto place = paddle::DefaultGPUPlace();
|
||||
return tensor_.copy_to(place, true);
|
||||
#elif defined(PADDLE_WITH_XPU)
|
||||
return tensor_.copy_to(paddle::DefaultXPUPlace(), true);
|
||||
#elif defined(PADDLE_WITH_CUSTOM_DEVICE)
|
||||
return tensor_.copy_to(paddle::DefaultCustomPlace(), true);
|
||||
#else
|
||||
PD_THROW(
|
||||
"cuda() is not supported: no GPU/XPU/Custom device enabled "
|
||||
"in this build.");
|
||||
#endif
|
||||
}
|
||||
|
||||
using TensorBase::stride;
|
||||
|
||||
using TensorBase::size;
|
||||
|
||||
at::Tensor to(
|
||||
at::TensorOptions options = {},
|
||||
bool non_blocking = false,
|
||||
bool copy = false,
|
||||
::std::optional<at::MemoryFormat> memory_format = ::std::nullopt) const;
|
||||
at::Tensor to(::std::optional<at::ScalarType> dtype,
|
||||
::std::optional<at::Layout> layout,
|
||||
::std::optional<at::Device> device,
|
||||
::std::optional<bool> pin_memory,
|
||||
bool non_blocking,
|
||||
bool copy,
|
||||
::std::optional<at::MemoryFormat> memory_format) const;
|
||||
at::Tensor to(
|
||||
at::Device device,
|
||||
at::ScalarType dtype,
|
||||
bool non_blocking = false,
|
||||
bool copy = false,
|
||||
::std::optional<at::MemoryFormat> memory_format = ::std::nullopt) const;
|
||||
at::Tensor to(
|
||||
at::ScalarType dtype,
|
||||
bool non_blocking = false,
|
||||
bool copy = false,
|
||||
::std::optional<at::MemoryFormat> memory_format = ::std::nullopt) const;
|
||||
at::Tensor to(
|
||||
const at::Tensor& other,
|
||||
bool non_blocking = false,
|
||||
bool copy = false,
|
||||
::std::optional<at::MemoryFormat> memory_format = ::std::nullopt) const;
|
||||
|
||||
Tensor meta() const {
|
||||
PD_THROW("`meta()` is not supported in this Paddle build.");
|
||||
}
|
||||
|
||||
at::Scalar item() const;
|
||||
|
||||
template <typename T>
|
||||
T item() const;
|
||||
|
||||
bool equal(const at::Tensor& other) const;
|
||||
|
||||
// Clamp functions
|
||||
at::Tensor clamp(
|
||||
const ::std::optional<at::Scalar>& min,
|
||||
const ::std::optional<at::Scalar>& max = ::std::nullopt) const;
|
||||
|
||||
at::Tensor clamp(const ::std::optional<at::Tensor>& min = {},
|
||||
const ::std::optional<at::Tensor>& max = {}) const;
|
||||
|
||||
at::Tensor& clamp_(
|
||||
const ::std::optional<at::Scalar>& min,
|
||||
const ::std::optional<at::Scalar>& max = ::std::nullopt) const;
|
||||
|
||||
at::Tensor& clamp_(const ::std::optional<at::Tensor>& min = {},
|
||||
const ::std::optional<at::Tensor>& max = {}) const;
|
||||
|
||||
at::Tensor clamp_max(const at::Scalar& max) const;
|
||||
at::Tensor clamp_max(const at::Tensor& max) const;
|
||||
at::Tensor& clamp_max_(const at::Scalar& max) const;
|
||||
at::Tensor& clamp_max_(const at::Tensor& max) const;
|
||||
|
||||
at::Tensor clamp_min(const at::Scalar& min) const;
|
||||
at::Tensor clamp_min(const at::Tensor& min) const;
|
||||
at::Tensor& clamp_min_(const at::Scalar& min) const;
|
||||
at::Tensor& clamp_min_(const at::Tensor& min) const;
|
||||
|
||||
// as_strided: Create a tensor view with custom size, stride, and
|
||||
// storage_offset
|
||||
at::Tensor as_strided(
|
||||
at::IntArrayRef size,
|
||||
at::IntArrayRef stride,
|
||||
::std::optional<int64_t> storage_offset = ::std::nullopt) const;
|
||||
|
||||
// as_strided_: Inplace version
|
||||
const at::Tensor& as_strided_(
|
||||
at::IntArrayRef size,
|
||||
at::IntArrayRef stride,
|
||||
::std::optional<int64_t> storage_offset = ::std::nullopt) const;
|
||||
|
||||
// as_strided_scatter: Scatter src into a strided view
|
||||
at::Tensor as_strided_scatter(
|
||||
const at::Tensor& src,
|
||||
at::IntArrayRef size,
|
||||
at::IntArrayRef stride,
|
||||
::std::optional<int64_t> storage_offset = ::std::nullopt) const;
|
||||
|
||||
// Standard deviation functions
|
||||
Tensor std(bool unbiased) const;
|
||||
Tensor std(at::OptionalIntArrayRef dim,
|
||||
bool unbiased,
|
||||
bool keepdim = false) const;
|
||||
Tensor std(at::OptionalIntArrayRef dim = ::std::nullopt,
|
||||
const ::std::optional<at::Scalar>& correction = ::std::nullopt,
|
||||
bool keepdim = false) const;
|
||||
Tensor std(int dim) const { return std(at::IntArrayRef{dim}); }
|
||||
|
||||
Tensor tensor_data() const {
|
||||
PaddleTensor result;
|
||||
if (tensor_.initialized()) {
|
||||
auto src_impl = tensor_.impl();
|
||||
auto* src_tensor =
|
||||
std::dynamic_pointer_cast<phi::DenseTensor>(src_impl).get();
|
||||
if (src_tensor && src_tensor->meta().is_contiguous()) {
|
||||
result.set_impl(std::make_shared<phi::DenseTensor>());
|
||||
auto* dst_tensor =
|
||||
std::dynamic_pointer_cast<phi::DenseTensor>(result.impl()).get();
|
||||
dst_tensor->ShareDataWith(*src_tensor);
|
||||
} else {
|
||||
result = paddle::experimental::assign(tensor_);
|
||||
}
|
||||
}
|
||||
// For uninitialized tensor, return an uninitialized tensor (no assign
|
||||
// needed)
|
||||
return Tensor(result);
|
||||
}
|
||||
|
||||
Tensor variable_data() const {
|
||||
PaddleTensor result;
|
||||
if (tensor_.initialized()) {
|
||||
auto src_impl = tensor_.impl();
|
||||
auto* src_tensor =
|
||||
std::dynamic_pointer_cast<phi::DenseTensor>(src_impl).get();
|
||||
if (src_tensor && src_tensor->meta().is_contiguous()) {
|
||||
result.set_impl(std::make_shared<phi::DenseTensor>());
|
||||
auto* dst_tensor =
|
||||
std::dynamic_pointer_cast<phi::DenseTensor>(result.impl()).get();
|
||||
dst_tensor->ShareDataWith(*src_tensor);
|
||||
} else {
|
||||
result = paddle::experimental::assign(tensor_);
|
||||
}
|
||||
}
|
||||
// For uninitialized tensor, return an uninitialized tensor (no assign
|
||||
// needed)
|
||||
return Tensor(result);
|
||||
}
|
||||
|
||||
// index: Get values at specified tensor indices
|
||||
at::Tensor index(const c10::List<::std::optional<at::Tensor>>& indices) const;
|
||||
|
||||
// index_put_: Set values at specified indices in-place
|
||||
at::Tensor& index_put_(const c10::List<::std::optional<at::Tensor>>& indices,
|
||||
const at::Tensor& values,
|
||||
bool accumulate = false) const;
|
||||
|
||||
// index_put: Non-inplace version of index_put_
|
||||
at::Tensor index_put(const c10::List<::std::optional<at::Tensor>>& indices,
|
||||
const at::Tensor& values,
|
||||
bool accumulate = false) const;
|
||||
|
||||
Tensor toType(ScalarType t) const {
|
||||
return Tensor(paddle::experimental::cast(
|
||||
tensor_, compat::_PD_AtenScalarTypeToPhiDataType(t)));
|
||||
}
|
||||
|
||||
at::Tensor contiguous(
|
||||
c10::MemoryFormat memory_format = c10::MemoryFormat::Contiguous) const {
|
||||
PD_CHECK(memory_format == c10::MemoryFormat::Contiguous,
|
||||
"`MemoryFormat` other than Contiguous");
|
||||
|
||||
return tensor_.contiguous();
|
||||
}
|
||||
|
||||
at::Tensor flatten(int64_t start_dim = 0, int64_t end_dim = -1) const;
|
||||
at::Tensor unflatten(int64_t dim, at::IntArrayRef sizes) const;
|
||||
at::Tensor unflatten_symint(int64_t dim, c10::SymIntArrayRef sizes) const;
|
||||
|
||||
Tensor& fill_(const at::Scalar& value) const {
|
||||
paddle::experimental::fill_(const_cast<PaddleTensor&>(tensor_), value);
|
||||
return const_cast<at::Tensor&>(*this);
|
||||
}
|
||||
|
||||
Tensor& zero_() const {
|
||||
paddle::experimental::fill_(const_cast<PaddleTensor&>(tensor_), 0.0);
|
||||
return const_cast<at::Tensor&>(*this);
|
||||
}
|
||||
|
||||
bool is_pinned(::std::optional<c10::Device> device = ::std::nullopt) const {
|
||||
if (device.has_value()) {
|
||||
phi::enforce::ThrowWarnInternal(
|
||||
"The argument 'device' of Tensor.is_pinned() is deprecated. "
|
||||
"Please do not pass this argument.");
|
||||
}
|
||||
|
||||
const PaddlePlace place = tensor_.place();
|
||||
const bool is_gpu_pinned = phi::is_cuda_pinned_place(place);
|
||||
const bool is_xpu_pinned = phi::is_xpu_pinned_place(place);
|
||||
|
||||
// Keep parity with PyTorch behavior: only host tensors are pinnable.
|
||||
if (!(phi::is_cpu_place(place) || is_gpu_pinned || is_xpu_pinned)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!device.has_value()) {
|
||||
return is_gpu_pinned || is_xpu_pinned;
|
||||
}
|
||||
|
||||
const auto device_type = device.value().type();
|
||||
if (device_type == c10::DeviceType::CUDA) {
|
||||
return is_gpu_pinned;
|
||||
}
|
||||
if (device_type == c10::DeviceType::XPU) {
|
||||
return is_xpu_pinned;
|
||||
}
|
||||
// CPU and non-accelerator devices are not valid pinned backends.
|
||||
return false;
|
||||
}
|
||||
|
||||
Tensor pin_memory(
|
||||
::std::optional<c10::Device> device = ::std::nullopt) const {
|
||||
if (device.has_value()) {
|
||||
phi::enforce::ThrowWarnInternal(
|
||||
"The argument 'device' of Tensor.pin_memory() is deprecated. "
|
||||
"Please do not pass this argument.");
|
||||
}
|
||||
|
||||
if (is_pinned(device)) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
const PaddlePlace current_place = tensor_.place();
|
||||
if (!phi::is_cpu_place(current_place)) {
|
||||
PD_THROW("cannot pin '" + this->toString() +
|
||||
"', only dense CPU tensors can be pinned");
|
||||
}
|
||||
|
||||
PaddlePlace pinned_place;
|
||||
|
||||
if (device.has_value()) {
|
||||
const auto device_type = device.value().type();
|
||||
if (device_type == c10::DeviceType::CUDA) {
|
||||
pinned_place = phi::Place(phi::GPUPinnedPlace());
|
||||
} else if (device_type == c10::DeviceType::XPU) {
|
||||
pinned_place = phi::Place(phi::XPUPinnedPlace());
|
||||
} else {
|
||||
PD_THROW("pin_memory device type must be an accelerator (GPU/XPU)");
|
||||
}
|
||||
} else {
|
||||
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
|
||||
pinned_place = phi::Place(phi::GPUPinnedPlace());
|
||||
#elif defined(PADDLE_WITH_XPU)
|
||||
pinned_place = phi::Place(phi::XPUPinnedPlace());
|
||||
#else
|
||||
PD_THROW("pin_memory is not supported: no GPU/XPU backend enabled");
|
||||
#endif
|
||||
}
|
||||
|
||||
return tensor_.copy_to(pinned_place, true);
|
||||
}
|
||||
|
||||
at::Tensor narrow_copy(int64_t dim, int64_t start, int64_t length) const;
|
||||
at::Tensor narrow_copy_symint(int64_t dim,
|
||||
c10::SymInt start,
|
||||
c10::SymInt length) const;
|
||||
|
||||
at::Tensor narrow(int64_t dim, int64_t start, int64_t length) const;
|
||||
at::Tensor narrow_symint(int64_t dim,
|
||||
c10::SymInt start,
|
||||
c10::SymInt length) const;
|
||||
at::Tensor narrow(int64_t dim, const at::Tensor& start, int64_t length) const;
|
||||
at::Tensor narrow_symint(int64_t dim,
|
||||
const at::Tensor& start,
|
||||
c10::SymInt length) const;
|
||||
|
||||
at::Tensor reshape(at::IntArrayRef shape) const;
|
||||
|
||||
at::Tensor transpose(int64_t dim0, int64_t dim1) const;
|
||||
at::Tensor& transpose_(int64_t dim0, int64_t dim1) const;
|
||||
|
||||
at::Tensor permute(at::IntArrayRef dims) const;
|
||||
|
||||
at::Tensor reciprocal() const;
|
||||
at::Tensor& reciprocal_() const;
|
||||
|
||||
at::Tensor detach() const;
|
||||
at::Tensor& detach_() const;
|
||||
|
||||
at::Tensor select(int64_t dim, int64_t index) const;
|
||||
at::Tensor select_symint(int64_t dim, c10::SymInt index) const;
|
||||
|
||||
at::Tensor& copy_(const at::Tensor& src, bool non_blocking = false) const {
|
||||
const_cast<PaddleTensor&>(tensor_).copy_(
|
||||
src._PD_GetInner(), tensor_.place(), /*blocking=*/!non_blocking);
|
||||
return const_cast<at::Tensor&>(*this);
|
||||
}
|
||||
|
||||
at::Tensor view(at::IntArrayRef size) const;
|
||||
at::Tensor view(at::ScalarType dtype) const;
|
||||
|
||||
at::Tensor squeeze() const;
|
||||
at::Tensor squeeze(int64_t dim) const;
|
||||
at::Tensor squeeze(at::IntArrayRef dim) const;
|
||||
at::Tensor& squeeze_() const;
|
||||
at::Tensor& squeeze_(int64_t dim) const;
|
||||
at::Tensor& squeeze_(at::IntArrayRef dim) const;
|
||||
|
||||
at::Tensor unsqueeze(int64_t dim) const;
|
||||
at::Tensor& unsqueeze_(int64_t dim) const;
|
||||
|
||||
at::Tensor sum(::std::optional<at::ScalarType> dtype = ::std::nullopt) const;
|
||||
at::Tensor sum(at::OptionalIntArrayRef dim,
|
||||
bool keepdim = false,
|
||||
::std::optional<at::ScalarType> dtype = ::std::nullopt) const;
|
||||
|
||||
at::Tensor t() const;
|
||||
at::Tensor& t_() const;
|
||||
|
||||
at::Tensor view_as(const at::Tensor& other) const;
|
||||
|
||||
at::Tensor coalesce() const;
|
||||
bool is_coalesced() const;
|
||||
|
||||
int64_t _nnz() const;
|
||||
at::Tensor _values() const;
|
||||
|
||||
bool is_variable() const noexcept { return true; }
|
||||
|
||||
at::Tensor index_select(int64_t dim, const at::Tensor& index) const {
|
||||
return Tensor(
|
||||
paddle::experimental::index_select(tensor_, index._PD_GetInner(), dim));
|
||||
}
|
||||
|
||||
at::Tensor masked_select(const at::Tensor& mask) const;
|
||||
|
||||
std::vector<at::Tensor> tensor_split(int64_t sections, int64_t dim) const;
|
||||
std::vector<at::Tensor> tensor_split_symint(c10::SymInt sections,
|
||||
int64_t dim) const;
|
||||
std::vector<at::Tensor> tensor_split(at::IntArrayRef indices,
|
||||
int64_t dim) const;
|
||||
std::vector<at::Tensor> tensor_split_symint(c10::SymIntArrayRef indices,
|
||||
int64_t dim) const;
|
||||
std::vector<at::Tensor> tensor_split(
|
||||
const at::Tensor& tensor_indices_or_sections, int64_t dim) const;
|
||||
|
||||
std::vector<at::Tensor> split(int64_t split_size, int64_t dim) const;
|
||||
std::vector<at::Tensor> split_symint(c10::SymInt split_size,
|
||||
int64_t dim) const;
|
||||
std::vector<at::Tensor> split(at::IntArrayRef split_sizes, int64_t dim) const;
|
||||
std::vector<at::Tensor> split_symint(c10::SymIntArrayRef split_sizes,
|
||||
int64_t dim) const;
|
||||
|
||||
std::vector<at::Tensor> unsafe_split(int64_t split_size,
|
||||
int64_t dim = 0) const;
|
||||
std::vector<at::Tensor> unsafe_split_symint(c10::SymInt split_size,
|
||||
int64_t dim = 0) const;
|
||||
|
||||
std::vector<at::Tensor> split_with_sizes(at::IntArrayRef split_sizes,
|
||||
int64_t dim = 0) const;
|
||||
std::vector<at::Tensor> split_with_sizes_symint(
|
||||
c10::SymIntArrayRef split_sizes, int64_t dim = 0) const;
|
||||
|
||||
std::vector<at::Tensor> unsafe_split_with_sizes(at::IntArrayRef split_sizes,
|
||||
int64_t dim = 0) const;
|
||||
std::vector<at::Tensor> unsafe_split_with_sizes_symint(
|
||||
c10::SymIntArrayRef split_sizes, int64_t dim = 0) const;
|
||||
|
||||
std::vector<at::Tensor> hsplit(int64_t sections) const;
|
||||
std::vector<at::Tensor> hsplit(at::IntArrayRef indices) const;
|
||||
|
||||
std::vector<at::Tensor> vsplit(int64_t sections) const;
|
||||
std::vector<at::Tensor> vsplit(at::IntArrayRef indices) const;
|
||||
|
||||
std::vector<at::Tensor> dsplit(int64_t sections) const;
|
||||
std::vector<at::Tensor> dsplit(at::IntArrayRef indices) const;
|
||||
|
||||
at::Tensor bitwise_right_shift(const Scalar& other) const {
|
||||
return Tensor(paddle::experimental::bitwise_right_shift(
|
||||
tensor_, paddle::experimental::full({}, other, other.dtype())));
|
||||
}
|
||||
|
||||
at::Tensor slice(int64_t dim = 0,
|
||||
::std::optional<int64_t> start = ::std::nullopt,
|
||||
::std::optional<int64_t> end = ::std::nullopt,
|
||||
int64_t step = 1) const;
|
||||
|
||||
at::Tensor index(ArrayRef<at::indexing::TensorIndex> indices) const;
|
||||
inline at::Tensor index(
|
||||
std::initializer_list<at::indexing::TensorIndex> indices) const {
|
||||
return index(ArrayRef<at::indexing::TensorIndex>(indices));
|
||||
}
|
||||
Tensor& index_put_(ArrayRef<at::indexing::TensorIndex> indices,
|
||||
Tensor const& rhs);
|
||||
Tensor& index_put_(ArrayRef<at::indexing::TensorIndex> indices,
|
||||
const Scalar& v);
|
||||
Tensor& index_put_(std::initializer_list<at::indexing::TensorIndex> indices,
|
||||
Tensor const& rhs);
|
||||
Tensor& index_put_(std::initializer_list<at::indexing::TensorIndex> indices,
|
||||
const Scalar& v);
|
||||
|
||||
at::Tensor& floor_divide_(const at::Scalar& other) const {
|
||||
paddle::experimental::floor_divide_(
|
||||
const_cast<PaddleTensor&>(tensor_),
|
||||
paddle::experimental::full({}, other, other.dtype()));
|
||||
return const_cast<at::Tensor&>(*this);
|
||||
}
|
||||
|
||||
// Paddle Tensor has no storage_offset, so we add it here, and it is always
|
||||
// 0.
|
||||
// int64_t storage_offset() const { return storage_offset_; }
|
||||
|
||||
inline Tensor clone(
|
||||
::std::optional<at::MemoryFormat> memory_format = ::std::nullopt) const {
|
||||
(void)memory_format;
|
||||
PaddleTensor cloned_tensor = paddle::experimental::assign(tensor_);
|
||||
return Tensor(cloned_tensor);
|
||||
}
|
||||
|
||||
// all: Check if all elements are true (non-zero)
|
||||
at::Tensor all() const;
|
||||
at::Tensor all(int64_t dim, bool keepdim = false) const;
|
||||
at::Tensor all(at::OptionalIntArrayRef dim, bool keepdim = false) const;
|
||||
|
||||
// allclose: Check if two tensors are close to each other
|
||||
bool allclose(const at::Tensor& other,
|
||||
double rtol = 1e-05,
|
||||
double atol = 1e-08,
|
||||
bool equal_nan = false) const;
|
||||
|
||||
at::Tensor abs() const;
|
||||
|
||||
at::Tensor& abs_() const;
|
||||
|
||||
at::Tensor absolute() const { return abs(); }
|
||||
|
||||
at::Tensor& absolute_() const { return abs_(); }
|
||||
|
||||
Tensor operator[](int64_t index) const {
|
||||
// Use as_strided to create a view (shares storage with original tensor)
|
||||
// This allows fill_ to modify the original tensor
|
||||
int64_t numel = tensor_.numel();
|
||||
if (numel == 0) {
|
||||
PD_THROW("operator[]: cannot index empty tensor");
|
||||
}
|
||||
|
||||
// Handle negative index
|
||||
if (index < 0) {
|
||||
index += tensor_.dims()[0];
|
||||
}
|
||||
|
||||
// Check bounds
|
||||
if (index < 0 || index >= tensor_.dims()[0]) {
|
||||
PD_THROW("operator[]: index ",
|
||||
index,
|
||||
" out of range for tensor of size ",
|
||||
tensor_.dims(),
|
||||
" at dimension 0");
|
||||
}
|
||||
|
||||
// For 1D tensor: create a scalar view (0-dim tensor) with proper offset
|
||||
// For multi-D tensor: create a view of the row at index
|
||||
std::vector<int64_t> new_sizes;
|
||||
std::vector<int64_t> new_strides;
|
||||
|
||||
auto dims = tensor_.dims();
|
||||
auto stride = tensor_.strides();
|
||||
|
||||
// Skip the first dimension (dim 0)
|
||||
for (int i = 1; i < dims.size(); ++i) {
|
||||
new_sizes.push_back(dims[i]);
|
||||
new_strides.push_back(stride[i]);
|
||||
}
|
||||
|
||||
// Calculate storage offset
|
||||
int64_t storage_offset = index * stride[0];
|
||||
|
||||
return as_strided(c10::IntArrayRef(new_sizes),
|
||||
c10::IntArrayRef(new_strides),
|
||||
storage_offset);
|
||||
}
|
||||
|
||||
void record_stream(at::Stream s) const;
|
||||
|
||||
Tensor var(int dim) const { return var(at::IntArrayRef{dim}, true, false); }
|
||||
|
||||
Tensor var(bool unbiased) const {
|
||||
std::vector<int64_t> empty_dims;
|
||||
double correction = unbiased ? 1.0 : 0.0;
|
||||
return var_impl(empty_dims, correction, false);
|
||||
}
|
||||
|
||||
Tensor var(at::OptionalIntArrayRef dim,
|
||||
bool unbiased,
|
||||
bool keepdim = false) const {
|
||||
// Convert unbiased to correction: unbiased=True means correction=1
|
||||
double correction = unbiased ? 1.0 : 0.0;
|
||||
std::vector<int64_t> dims_vec;
|
||||
if (dim.has_value() && dim.value().size() > 0) {
|
||||
dims_vec.assign(dim.value().begin(), dim.value().end());
|
||||
}
|
||||
return var_impl(dims_vec, correction, keepdim);
|
||||
}
|
||||
|
||||
Tensor var(at::OptionalIntArrayRef dim = ::std::nullopt,
|
||||
const ::std::optional<at::Scalar>& correction = ::std::nullopt,
|
||||
bool keepdim = false) const {
|
||||
double correction_value = 1.0;
|
||||
if (correction.has_value()) {
|
||||
const at::Scalar& scalar = correction.value();
|
||||
correction_value = scalar.to<double>();
|
||||
}
|
||||
std::vector<int64_t> dims_vec;
|
||||
if (dim.has_value() && dim.value().size() > 0) {
|
||||
dims_vec.assign(dim.value().begin(), dim.value().end());
|
||||
}
|
||||
return var_impl(dims_vec, correction_value, keepdim);
|
||||
}
|
||||
|
||||
private:
|
||||
Tensor var_impl(const std::vector<int64_t>& dims_vec,
|
||||
double correction_value,
|
||||
bool keepdim) const {
|
||||
phi::IntArray dims_int_array(dims_vec);
|
||||
|
||||
PaddleTensor mean_tensor;
|
||||
if (dims_vec.empty()) {
|
||||
mean_tensor = paddle::experimental::mean(
|
||||
tensor_, phi::IntArray(std::vector<int64_t>{}), true);
|
||||
} else {
|
||||
mean_tensor = paddle::experimental::mean(tensor_, dims_int_array, true);
|
||||
}
|
||||
|
||||
PaddleTensor diff = paddle::experimental::subtract(tensor_, mean_tensor);
|
||||
PaddleTensor diff_squared = paddle::experimental::multiply(diff, diff);
|
||||
|
||||
PaddleTensor sum_squared_diff;
|
||||
if (dims_vec.empty()) {
|
||||
sum_squared_diff =
|
||||
paddle::experimental::sum(diff_squared,
|
||||
phi::IntArray(std::vector<int64_t>{}),
|
||||
diff_squared.dtype(),
|
||||
keepdim);
|
||||
} else {
|
||||
sum_squared_diff = paddle::experimental::sum(
|
||||
diff_squared, dims_int_array, diff_squared.dtype(), keepdim);
|
||||
}
|
||||
|
||||
int64_t n = tensor_.numel();
|
||||
if (!dims_vec.empty()) {
|
||||
n = 1;
|
||||
for (int64_t d : dims_vec) {
|
||||
int64_t dim_idx = d < 0 ? d + tensor_.dims().size() : d;
|
||||
if (dim_idx >= 0 &&
|
||||
dim_idx < static_cast<int64_t>(tensor_.dims().size())) {
|
||||
n *= tensor_.dims()[dim_idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double corrected_n = static_cast<double>(n) - correction_value;
|
||||
if (corrected_n <= 0.0) {
|
||||
corrected_n = static_cast<double>(n);
|
||||
}
|
||||
|
||||
std::vector<int64_t> result_shape_vec;
|
||||
for (int64_t i = 0; i < sum_squared_diff.dims().size(); ++i) {
|
||||
result_shape_vec.push_back(sum_squared_diff.dims()[i]);
|
||||
}
|
||||
PaddleTensor correction_scalar =
|
||||
paddle::experimental::full(phi::IntArray(result_shape_vec),
|
||||
phi::Scalar(corrected_n),
|
||||
sum_squared_diff.dtype(),
|
||||
sum_squared_diff.place());
|
||||
PaddleTensor result =
|
||||
paddle::experimental::divide(sum_squared_diff, correction_scalar);
|
||||
|
||||
return Tensor(result);
|
||||
}
|
||||
|
||||
public:
|
||||
// Deprecated packed_accessor for compatibility with PyTorch
|
||||
// Use packed_accessor32 or packed_accessor64 instead
|
||||
template <typename T,
|
||||
size_t N,
|
||||
template <typename U> class PtrTraits = DefaultPtrTraits,
|
||||
typename index_t = int64_t>
|
||||
[[deprecated(
|
||||
"packed_accessor is deprecated, use packed_accessor32 or "
|
||||
"packed_accessor64 instead")]] GenericPackedTensorAccessor<T,
|
||||
N,
|
||||
PtrTraits,
|
||||
index_t>
|
||||
packed_accessor() const& {
|
||||
return this->template generic_packed_accessor<T, N, PtrTraits, index_t>();
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
size_t N,
|
||||
template <typename U> class PtrTraits = DefaultPtrTraits,
|
||||
typename index_t = int64_t>
|
||||
[[deprecated(
|
||||
"packed_accessor is deprecated, use packed_accessor32 or "
|
||||
"packed_accessor64 instead")]] GenericPackedTensorAccessor<T,
|
||||
N,
|
||||
PtrTraits,
|
||||
index_t>
|
||||
packed_accessor() && = delete;
|
||||
|
||||
template <typename T>
|
||||
using hook_return_void_t =
|
||||
std::enable_if_t<std::is_void_v<std::invoke_result_t<T&, Tensor>>,
|
||||
unsigned>;
|
||||
template <typename T>
|
||||
using hook_return_var_t =
|
||||
std::enable_if_t<std::is_same_v<std::invoke_result_t<T&, Tensor>, Tensor>,
|
||||
unsigned>;
|
||||
|
||||
// register_hook - throws exception for Paddle compatibility
|
||||
// Paddle does not support gradient hooks
|
||||
template <typename T>
|
||||
hook_return_void_t<T> register_hook(T&&) const {
|
||||
throw std::runtime_error(
|
||||
"register_hook is not supported in Paddle, this is an ATen "
|
||||
"compatibility API that is not available");
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
hook_return_var_t<T> register_hook(T&&) const {
|
||||
throw std::runtime_error(
|
||||
"register_hook is not supported in Paddle, this is an ATen "
|
||||
"compatibility API that is not available");
|
||||
}
|
||||
|
||||
// any - returns true if any element is non-zero
|
||||
Tensor any(int64_t dim, bool keepdim = false) const;
|
||||
Tensor any(at::OptionalIntArrayRef dim, bool keepdim = false) const;
|
||||
Tensor any() const;
|
||||
|
||||
// chunk - splits tensor into chunks
|
||||
std::vector<Tensor> chunk(int64_t chunks, int64_t dim = 0) const;
|
||||
|
||||
// rename - stub for Paddle (Dimname not supported)
|
||||
Tensor rename(::std::optional<at::DimnameList> names) const;
|
||||
|
||||
// new_empty - creates uninitialized tensor with same dtype/device
|
||||
Tensor new_empty(at::IntArrayRef size, at::TensorOptions options = {}) const;
|
||||
Tensor new_empty(at::IntArrayRef size,
|
||||
::std::optional<at::ScalarType> dtype,
|
||||
::std::optional<at::Layout> layout,
|
||||
::std::optional<at::Device> device,
|
||||
::std::optional<bool> pin_memory) const;
|
||||
|
||||
// new_full - creates tensor filled with fill_value
|
||||
Tensor new_full(at::IntArrayRef size,
|
||||
const at::Scalar& fill_value,
|
||||
at::TensorOptions options = {}) const;
|
||||
Tensor new_full(at::IntArrayRef size,
|
||||
const at::Scalar& fill_value,
|
||||
::std::optional<at::ScalarType> dtype,
|
||||
::std::optional<at::Layout> layout,
|
||||
::std::optional<at::Device> device,
|
||||
::std::optional<bool> pin_memory) const;
|
||||
|
||||
// new_zeros - creates zero tensor
|
||||
Tensor new_zeros(at::IntArrayRef size, at::TensorOptions options = {}) const;
|
||||
Tensor new_zeros(at::IntArrayRef size,
|
||||
::std::optional<at::ScalarType> dtype,
|
||||
::std::optional<at::Layout> layout,
|
||||
::std::optional<at::Device> device,
|
||||
::std::optional<bool> pin_memory) const;
|
||||
|
||||
// new_ones - creates tensor filled with ones
|
||||
Tensor new_ones(at::IntArrayRef size, at::TensorOptions options = {}) const;
|
||||
Tensor new_ones(at::IntArrayRef size,
|
||||
::std::optional<at::ScalarType> dtype,
|
||||
::std::optional<at::Layout> layout,
|
||||
::std::optional<at::Device> device,
|
||||
::std::optional<bool> pin_memory) const;
|
||||
|
||||
// resize_ - in-place resize
|
||||
const Tensor& resize_(
|
||||
at::IntArrayRef size,
|
||||
::std::optional<at::MemoryFormat> memory_format = ::std::nullopt) const;
|
||||
|
||||
// expand - expands tensor to new size
|
||||
Tensor expand(at::IntArrayRef size, bool implicit = false) const;
|
||||
|
||||
// expand_as - expands to same size as another tensor
|
||||
Tensor expand_as(const Tensor& other) const;
|
||||
|
||||
PaddleTensor _PD_GetInner() const { return tensor_; }
|
||||
PaddleTensor& _PD_GetInner() { return tensor_; }
|
||||
}; // NOLINT(readability/braces)
|
||||
} // namespace at
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright (c) 2025 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.
|
||||
|
||||
// The file has been adapted from pytorch project
|
||||
// Licensed under BSD-style license -
|
||||
// https://github.com/pytorch/pytorch/blob/main/LICENSE
|
||||
|
||||
#include <ATen/core/TensorBase.h>
|
||||
#include <ATen/core/TensorBody.h>
|
||||
#include <string_view>
|
||||
|
||||
namespace at {
|
||||
|
||||
void check_type(const TensorBase& tensor,
|
||||
ScalarType type,
|
||||
std::string_view type_name) {
|
||||
PD_CHECK(tensor.scalar_type() == type,
|
||||
"expected scalar type ",
|
||||
type_name,
|
||||
" but found ",
|
||||
compat::_PD_AtenScalarTypeToPhiDataType(tensor.scalar_type()));
|
||||
}
|
||||
|
||||
#define DEFINE_CAST(T, name) \
|
||||
template <> \
|
||||
PADDLE_API const T* TensorBase::const_data_ptr() const { \
|
||||
check_type(*this, ScalarType::name, #name); \
|
||||
return const_cast<T*>(tensor_.data<T>()); \
|
||||
} \
|
||||
\
|
||||
template <> \
|
||||
PADDLE_API const T* TensorBase::const_data_ptr<const T>() const { \
|
||||
check_type(*this, ScalarType::name, #name); \
|
||||
return const_cast<T*>(tensor_.data<std::remove_const_t<T>>()); \
|
||||
} \
|
||||
\
|
||||
template <> \
|
||||
PADDLE_API T* TensorBase::mutable_data_ptr() const { \
|
||||
check_type(*this, ScalarType::name, #name); \
|
||||
return const_cast<PaddleTensor&>(tensor_).data<T>(); \
|
||||
} \
|
||||
\
|
||||
template <> \
|
||||
PADDLE_API T* TensorBase::data_ptr() const { \
|
||||
return const_cast<T*>(tensor_.data<T>()); \
|
||||
}
|
||||
|
||||
AT_FORALL_SCALAR_TYPES_WITH_COMPLEX(DEFINE_CAST) // missing half and float16
|
||||
// AT_FORALL_QINT_TYPES(DEFINE_CAST) // missing qint
|
||||
DEFINE_CAST(uint16_t, UInt16)
|
||||
DEFINE_CAST(uint32_t, UInt32)
|
||||
DEFINE_CAST(uint64_t, UInt64)
|
||||
#undef DEFINE_CAST
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,156 @@
|
||||
// Copyright (c) 2026 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.
|
||||
|
||||
// The file has been adapted from pytorch project
|
||||
// Licensed under BSD-style license -
|
||||
// https://github.com/pytorch/pytorch/blob/main/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ostream>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace c10 {
|
||||
/**
|
||||
* class AliasInfo
|
||||
*
|
||||
* Data structure to hold aliasing information for an `Argument`. They can be
|
||||
* nested to represent aliasing information on contained types.
|
||||
*
|
||||
* There is a `beforeSet` which describes the aliasing information before the
|
||||
* operator executes, and an `afterSet` that describes aliasing info
|
||||
* after execution.
|
||||
*/
|
||||
class AliasInfo {
|
||||
public:
|
||||
AliasInfo() = default;
|
||||
AliasInfo(bool is_write,
|
||||
const std::set<std::string>& before_qual_strings,
|
||||
const std::set<std::string>& after_qual_strings)
|
||||
: isWrite_(is_write) {
|
||||
for (const auto& s : before_qual_strings) {
|
||||
beforeSets_.insert(s);
|
||||
}
|
||||
for (const auto& s : after_qual_strings) {
|
||||
afterSets_.insert(s);
|
||||
}
|
||||
}
|
||||
|
||||
bool isWrite() const { return isWrite_; }
|
||||
|
||||
const std::unordered_set<std::string>& beforeSets() const {
|
||||
return beforeSets_;
|
||||
}
|
||||
|
||||
const std::unordered_set<std::string>& afterSets() const {
|
||||
return afterSets_;
|
||||
}
|
||||
|
||||
// the alias info for the contained types of the type
|
||||
// e.g. if this is an annotation on List[T], `sets` refers to
|
||||
// the alias sets that the list may be in
|
||||
// while containedTypes()[0] refers to the sets that members of the list
|
||||
// may be in
|
||||
void addContainedType(AliasInfo aliasInfo) {
|
||||
containedTypes_.push_back(std::move(aliasInfo));
|
||||
}
|
||||
const std::vector<AliasInfo>& containedTypes() const {
|
||||
return containedTypes_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_set<std::string> beforeSets_;
|
||||
std::unordered_set<std::string> afterSets_;
|
||||
std::vector<AliasInfo> containedTypes_;
|
||||
bool isWrite_ = false;
|
||||
};
|
||||
|
||||
inline bool operator==(const AliasInfo& lhs, const AliasInfo& rhs) {
|
||||
return lhs.isWrite() == rhs.isWrite() &&
|
||||
lhs.beforeSets() == rhs.beforeSets() &&
|
||||
lhs.afterSets() == rhs.afterSets() &&
|
||||
lhs.containedTypes() == rhs.containedTypes();
|
||||
}
|
||||
|
||||
// this does match the way things are represented in the schema
|
||||
inline std::ostream& operator<<(std::ostream& out, const AliasInfo& aliasInfo) {
|
||||
out << '(';
|
||||
bool first = true;
|
||||
for (const auto& set : aliasInfo.beforeSets()) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
out << '|';
|
||||
}
|
||||
out << set;
|
||||
}
|
||||
if (aliasInfo.isWrite()) {
|
||||
out << '!';
|
||||
}
|
||||
if (aliasInfo.beforeSets() != aliasInfo.afterSets()) {
|
||||
out << " -> ";
|
||||
first = true;
|
||||
for (const auto& set : aliasInfo.afterSets()) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
out << '|';
|
||||
}
|
||||
out << set;
|
||||
}
|
||||
}
|
||||
out << ')';
|
||||
return out;
|
||||
}
|
||||
} // namespace c10
|
||||
|
||||
inline std::size_t hash_combine(std::size_t lhs, std::size_t rhs) {
|
||||
lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2);
|
||||
return lhs;
|
||||
}
|
||||
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<c10::AliasInfo> {
|
||||
size_t operator()(const c10::AliasInfo& aliasInfo) const {
|
||||
auto hash = std::hash<bool>()(aliasInfo.isWrite());
|
||||
|
||||
// NOTE: for unordered_set hashes, we couldn't use hash_combine
|
||||
// because hash_combine is order dependent. Instead, we choose to
|
||||
// use XOR as the combining function as XOR is commutative.
|
||||
size_t before_set_hash_seed = 0;
|
||||
for (auto& e : aliasInfo.beforeSets()) {
|
||||
auto symbol_hash = std::hash<std::string>()(e);
|
||||
before_set_hash_seed = before_set_hash_seed ^ symbol_hash;
|
||||
}
|
||||
size_t after_set_hash_seed = 0;
|
||||
for (auto& e : aliasInfo.afterSets()) {
|
||||
auto symbol_hash = std::hash<std::string>()(e);
|
||||
after_set_hash_seed = after_set_hash_seed ^ symbol_hash;
|
||||
}
|
||||
|
||||
hash = hash_combine(hash, before_set_hash_seed);
|
||||
hash = hash_combine(hash, after_set_hash_seed);
|
||||
for (auto& e : aliasInfo.containedTypes()) {
|
||||
auto contained_type_hash = std::hash<c10::AliasInfo>()(e);
|
||||
hash = hash_combine(hash, contained_type_hash);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
@@ -0,0 +1,201 @@
|
||||
// Copyright (c) 2026 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.
|
||||
|
||||
// The file has been adapted from pytorch project
|
||||
// Licensed under BSD-style license -
|
||||
// https://github.com/pytorch/pytorch/blob/main/LICENSE
|
||||
|
||||
#include "ATen/core/function_schema.h"
|
||||
|
||||
namespace c10 {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char kWildcardAliasSet[] = "*";
|
||||
|
||||
const char* schemaArgTypeName(SchemaArgType type) {
|
||||
if (type == SchemaArgType::input) {
|
||||
return "input";
|
||||
}
|
||||
if (type == SchemaArgType::output) {
|
||||
return "output";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
bool aliasSetsMayOverlap(const std::unordered_set<std::string>& lhs,
|
||||
const std::unordered_set<std::string>& rhs) {
|
||||
if (lhs.empty() || rhs.empty()) {
|
||||
return false;
|
||||
}
|
||||
if (lhs.count(kWildcardAliasSet) > 0 || rhs.count(kWildcardAliasSet) > 0) {
|
||||
return true;
|
||||
}
|
||||
for (const auto& set : lhs) {
|
||||
if (rhs.count(set) > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const Argument& getSchemaArgumentOrThrow(const FunctionSchema& schema,
|
||||
const SchemaArgument& argument) {
|
||||
const auto& args = schema.getCorrectList(argument);
|
||||
TORCH_CHECK(argument.index < args.size(),
|
||||
"Schema ",
|
||||
schemaArgTypeName(argument.type),
|
||||
" index ",
|
||||
argument.index,
|
||||
" is out of bounds for size ",
|
||||
args.size());
|
||||
return args.at(argument.index);
|
||||
}
|
||||
|
||||
bool aliasInfoMayContainAlias(const AliasInfo& lhs,
|
||||
const AliasInfo& rhs,
|
||||
bool bidirectional) {
|
||||
if (aliasSetsMayOverlap(lhs.afterSets(), rhs.afterSets())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const auto& child : lhs.containedTypes()) {
|
||||
if (aliasInfoMayContainAlias(child, rhs, /*bidirectional=*/true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bidirectional) {
|
||||
return false;
|
||||
}
|
||||
for (const auto& child : rhs.containedTypes()) {
|
||||
if (aliasInfoMayContainAlias(lhs, child, /*bidirectional=*/true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const Argument& arg) {
|
||||
out << arg.type()->str() << " " << arg.name();
|
||||
if (arg.default_value()) {
|
||||
out << " = " << arg.default_value();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const FunctionSchema& schema) {
|
||||
out << "(";
|
||||
bool first = true;
|
||||
for (const auto& arg : schema.arguments()) {
|
||||
if (!first) {
|
||||
out << ", ";
|
||||
}
|
||||
out << arg;
|
||||
first = false;
|
||||
}
|
||||
if (schema.is_vararg()) {
|
||||
if (!first) {
|
||||
out << ", ";
|
||||
}
|
||||
out << "...";
|
||||
}
|
||||
out << ")";
|
||||
|
||||
out << " -> ";
|
||||
|
||||
if (schema.returns().size() == 1) {
|
||||
out << schema.returns()[0];
|
||||
} else {
|
||||
out << "(";
|
||||
first = true;
|
||||
for (const auto& ret : schema.returns()) {
|
||||
if (!first) {
|
||||
out << ", ";
|
||||
}
|
||||
out << ret;
|
||||
first = false;
|
||||
}
|
||||
out << ")";
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::optional<int> FunctionSchema::argumentIndexWithName(
|
||||
const std::string& name) const {
|
||||
for (size_t i = 0; i < arguments_.size(); ++i) {
|
||||
if (arguments_[i].name() == name) {
|
||||
return static_cast<int>(i);
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const std::vector<Argument>& FunctionSchema::getCorrectList(
|
||||
const SchemaArgument& argument) const {
|
||||
if (argument.type == SchemaArgType::input) {
|
||||
return arguments();
|
||||
}
|
||||
if (argument.type == SchemaArgType::output) {
|
||||
return returns();
|
||||
}
|
||||
TORCH_INTERNAL_ASSERT(false, "Could not match argument type");
|
||||
}
|
||||
|
||||
bool FunctionSchema::is_aliasing(const SchemaArgument& argument) const {
|
||||
const auto& arg = getSchemaArgumentOrThrow(*this, argument);
|
||||
return arg.alias_info() != nullptr;
|
||||
}
|
||||
|
||||
bool FunctionSchema::is_mutable(const SchemaArgument& argument) const {
|
||||
const auto& arg = getSchemaArgumentOrThrow(*this, argument);
|
||||
return arg.alias_info() != nullptr && arg.alias_info()->isWrite();
|
||||
}
|
||||
|
||||
bool FunctionSchema::is_mutable(const std::string& name) const {
|
||||
const auto index = argumentIndexWithName(name);
|
||||
TORCH_CHECK(
|
||||
index.has_value(), "Tried to test mutability of nonexistent name ", name);
|
||||
return is_mutable({SchemaArgType::input, static_cast<size_t>(*index)});
|
||||
}
|
||||
|
||||
bool FunctionSchema::may_alias(const SchemaArgument& lhs,
|
||||
const SchemaArgument& rhs) const {
|
||||
const auto& lhs_arg = getSchemaArgumentOrThrow(*this, lhs);
|
||||
const auto& rhs_arg = getSchemaArgumentOrThrow(*this, rhs);
|
||||
const auto* lhs_alias = lhs_arg.alias_info();
|
||||
const auto* rhs_alias = rhs_arg.alias_info();
|
||||
if (lhs_alias == nullptr || rhs_alias == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return aliasSetsMayOverlap(lhs_alias->afterSets(), rhs_alias->afterSets());
|
||||
}
|
||||
|
||||
bool FunctionSchema::may_contain_alias(const SchemaArgument& lhs,
|
||||
const SchemaArgument& rhs,
|
||||
bool bidirectional) const {
|
||||
const auto& lhs_arg = getSchemaArgumentOrThrow(*this, lhs);
|
||||
const auto& rhs_arg = getSchemaArgumentOrThrow(*this, rhs);
|
||||
const auto* lhs_alias = lhs_arg.alias_info();
|
||||
const auto* rhs_alias = rhs_arg.alias_info();
|
||||
if (lhs_alias == nullptr || rhs_alias == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return aliasInfoMayContainAlias(*lhs_alias, *rhs_alias, bidirectional);
|
||||
}
|
||||
|
||||
} // namespace c10
|
||||
@@ -0,0 +1,261 @@
|
||||
// Copyright (c) 2026 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.
|
||||
|
||||
// The file has been adapted from pytorch project
|
||||
// Licensed under BSD-style license -
|
||||
// https://github.com/pytorch/pytorch/blob/main/LICENSE
|
||||
|
||||
#pragma once
|
||||
#include <ATen/core/alias_info.h>
|
||||
#include <ATen/core/ivalue.h>
|
||||
#include <ATen/core/jit_type.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "paddle/common/macros.h" // For macro PADDLE_API
|
||||
|
||||
namespace c10 {
|
||||
|
||||
struct Argument;
|
||||
struct FunctionSchema;
|
||||
enum class SchemaArgType;
|
||||
struct SchemaArgument;
|
||||
|
||||
enum class SchemaArgType {
|
||||
input,
|
||||
output,
|
||||
};
|
||||
|
||||
struct SchemaArgument {
|
||||
SchemaArgType type;
|
||||
size_t index;
|
||||
};
|
||||
|
||||
struct PADDLE_API Argument {
|
||||
Argument(std::string name = "",
|
||||
const TypePtr& type = nullptr,
|
||||
std::optional<int32_t> N = std::nullopt,
|
||||
std::optional<torch::IValue> default_value = std::nullopt,
|
||||
bool kwarg_only = false,
|
||||
std::optional<c10::AliasInfo> alias_info = std::nullopt)
|
||||
: Argument(std::move(name),
|
||||
type,
|
||||
type,
|
||||
N,
|
||||
std::move(default_value),
|
||||
kwarg_only,
|
||||
std::move(alias_info)) {}
|
||||
|
||||
Argument(std::string name,
|
||||
TypePtr fake_type,
|
||||
TypePtr real_type,
|
||||
std::optional<int32_t> N = std::nullopt,
|
||||
std::optional<torch::IValue> default_value = std::nullopt,
|
||||
bool kwarg_only = false,
|
||||
std::optional<c10::AliasInfo> alias_info = std::nullopt)
|
||||
: name_(std::move(name)),
|
||||
type_(fake_type ? std::move(fake_type) : TensorType::get()),
|
||||
real_type_(real_type ? std::move(real_type) : type_),
|
||||
N_(N),
|
||||
default_value_(std::move(default_value)),
|
||||
alias_info_(alias_info ? std::make_unique<c10::AliasInfo>(
|
||||
std::move(*alias_info))
|
||||
: nullptr),
|
||||
kwarg_only_(kwarg_only) {
|
||||
// this is an softly-enforced invariant for out arguments.
|
||||
bool is_alias = alias_info_ != nullptr && alias_info_->isWrite();
|
||||
is_out_ = kwarg_only_ && is_alias;
|
||||
}
|
||||
|
||||
Argument(Argument&& rhs) noexcept = default;
|
||||
|
||||
Argument(const Argument& rhs)
|
||||
: name_(rhs.name_),
|
||||
type_(rhs.type_),
|
||||
real_type_(rhs.real_type_),
|
||||
N_(rhs.N_),
|
||||
default_value_(rhs.default_value_),
|
||||
alias_info_(rhs.alias_info_
|
||||
? std::make_unique<c10::AliasInfo>(*rhs.alias_info_)
|
||||
: nullptr),
|
||||
kwarg_only_(rhs.kwarg_only_),
|
||||
is_out_(rhs.is_out_) {}
|
||||
|
||||
Argument& operator=(Argument&& rhs) = default;
|
||||
|
||||
Argument& operator=(const Argument& rhs) {
|
||||
if (this != &rhs) {
|
||||
name_ = rhs.name_;
|
||||
type_ = rhs.type_;
|
||||
real_type_ = rhs.real_type_;
|
||||
N_ = rhs.N_;
|
||||
default_value_ = rhs.default_value_;
|
||||
alias_info_ = rhs.alias_info_
|
||||
? std::make_unique<c10::AliasInfo>(*rhs.alias_info_)
|
||||
: nullptr;
|
||||
kwarg_only_ = rhs.kwarg_only_;
|
||||
is_out_ = rhs.is_out_;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
~Argument() = default;
|
||||
|
||||
const std::string& name() const { return name_; }
|
||||
const TypePtr& type() const { return type_; }
|
||||
// if type() is non-null, this is guaranteed to be non-null (if no real
|
||||
// type was provided, this takes on type()'s value)
|
||||
const TypePtr& real_type() const { return real_type_; }
|
||||
const std::optional<int32_t>& N() const { return N_; }
|
||||
const std::optional<torch::IValue>& default_value() const {
|
||||
return default_value_;
|
||||
}
|
||||
bool kwarg_only() const { return kwarg_only_; }
|
||||
|
||||
bool is_out() const { return is_out_; }
|
||||
|
||||
[[nodiscard]] const c10::AliasInfo* alias_info() const {
|
||||
return alias_info_.get();
|
||||
}
|
||||
|
||||
bool is_inferred_type() const {
|
||||
bool is_inferred_type = false;
|
||||
TORCH_INTERNAL_ASSERT(type_);
|
||||
if (auto pt = type_->cast<TensorType>()) {
|
||||
if (pt->isInferredType()) {
|
||||
is_inferred_type = true;
|
||||
}
|
||||
}
|
||||
return is_inferred_type;
|
||||
}
|
||||
|
||||
std::string formatTypeMismatchMsg(const std::string& actual_type) const {
|
||||
std::string inferred_type_hint;
|
||||
if (is_inferred_type()) {
|
||||
inferred_type_hint = "Inferred '";
|
||||
inferred_type_hint += name();
|
||||
inferred_type_hint += "' to be of type 'Tensor' ";
|
||||
inferred_type_hint +=
|
||||
"because it was not annotated with an explicit type.\n";
|
||||
}
|
||||
std::string message;
|
||||
message += "Expected a value of type '";
|
||||
message += type()->repr_str();
|
||||
message += "' for argument '";
|
||||
message += name();
|
||||
message += "' but instead found type '";
|
||||
message += actual_type;
|
||||
message += "'.\n";
|
||||
message += inferred_type_hint;
|
||||
return message;
|
||||
}
|
||||
|
||||
Argument cloneWithType(const TypePtr& new_type) const {
|
||||
return Argument(name_,
|
||||
new_type,
|
||||
N_,
|
||||
default_value_,
|
||||
kwarg_only_,
|
||||
alias_info_ ? std::optional<c10::AliasInfo>(*alias_info_)
|
||||
: std::nullopt);
|
||||
}
|
||||
|
||||
friend PADDLE_API std::ostream& operator<<(std::ostream& out,
|
||||
const Argument& arg);
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
TypePtr type_;
|
||||
TypePtr real_type_; // this is ScalarType, not int, e.g.
|
||||
// for list types, an optional statically known length for the list
|
||||
// e.g. for int[3]: type = ListType::ofInts(), N = 3
|
||||
// If present, this will allow scalars to be broadcast to this length to
|
||||
// become a list.
|
||||
std::optional<int32_t> N_;
|
||||
|
||||
std::optional<torch::IValue> default_value_;
|
||||
// c10::AliasInfo is huge, so let's only allocate memory for it if
|
||||
// necessary (which it isn't during schema parsing on startup, to
|
||||
// give a pertinent example).
|
||||
std::unique_ptr<c10::AliasInfo> alias_info_;
|
||||
// is this only specifiable as a keyword argument?
|
||||
bool kwarg_only_;
|
||||
// marks if the argument is out variant of the schema
|
||||
bool is_out_;
|
||||
};
|
||||
|
||||
struct PADDLE_API FunctionSchema {
|
||||
FunctionSchema(std::vector<Argument> arguments,
|
||||
std::vector<Argument> returns,
|
||||
bool is_vararg = false,
|
||||
bool is_varret = false)
|
||||
: arguments_(std::move(arguments)),
|
||||
returns_(std::move(returns)),
|
||||
is_vararg_(is_vararg),
|
||||
is_varret_(is_varret) {
|
||||
checkSchema();
|
||||
}
|
||||
|
||||
const std::vector<Argument>& arguments() const { return arguments_; }
|
||||
|
||||
void checkSchema() const {
|
||||
bool seen_default_arg = false;
|
||||
for (const auto& arg : arguments()) {
|
||||
if (arg.default_value()) {
|
||||
seen_default_arg = true;
|
||||
} else {
|
||||
TORCH_INTERNAL_ASSERT(!seen_default_arg || arg.kwarg_only(),
|
||||
"Non-default positional argument follows default "
|
||||
"argument. Parameter ",
|
||||
arg.name(),
|
||||
" in ",
|
||||
*this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<Argument>& returns() const { return returns_; }
|
||||
|
||||
bool is_vararg() const { return is_vararg_; }
|
||||
|
||||
bool is_varret() const { return is_varret_; }
|
||||
|
||||
std::optional<int> argumentIndexWithName(const std::string& name) const;
|
||||
const std::vector<Argument>& getCorrectList(
|
||||
const SchemaArgument& argument) const;
|
||||
bool is_aliasing(const SchemaArgument& argument) const;
|
||||
bool is_mutable(const SchemaArgument& argument) const;
|
||||
bool is_mutable(const std::string& name) const;
|
||||
bool may_alias(const SchemaArgument& lhs, const SchemaArgument& rhs) const;
|
||||
bool may_contain_alias(const SchemaArgument& lhs,
|
||||
const SchemaArgument& rhs,
|
||||
bool bidirectional = true) const;
|
||||
|
||||
friend PADDLE_API std::ostream& operator<<(std::ostream& out,
|
||||
const FunctionSchema& schema);
|
||||
|
||||
private:
|
||||
std::vector<Argument> arguments_;
|
||||
std::vector<Argument> returns_;
|
||||
// if true then this schema takes an arbitrary number of additional arguments
|
||||
// after the argument specified in arguments
|
||||
// currently this is used primarily to represent 'primitive' operators whose
|
||||
// arguments are not checked by schema
|
||||
bool is_vararg_;
|
||||
bool is_varret_;
|
||||
};
|
||||
|
||||
PADDLE_API std::ostream& operator<<(std::ostream& out, const Argument& arg);
|
||||
PADDLE_API std::ostream& operator<<(std::ostream& out,
|
||||
const FunctionSchema& schema);
|
||||
|
||||
} // namespace c10
|
||||
@@ -0,0 +1,72 @@
|
||||
// Copyright (c) 2026 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.
|
||||
|
||||
// The file has been adapted from pytorch project
|
||||
// Licensed under BSD-style license -
|
||||
// https://github.com/pytorch/pytorch/blob/main/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <c10/util/ArrayRef.h>
|
||||
#include <vector>
|
||||
|
||||
namespace c10 {
|
||||
|
||||
// The passed in function must take T by value (T), or by
|
||||
// const reference (const T&); taking T by non-const reference
|
||||
// will result in an error like:
|
||||
//
|
||||
// error: no type named 'type' in 'class std::invoke_result<foobar::__lambda,
|
||||
// T>'
|
||||
//
|
||||
// No explicit template parameters are required.
|
||||
|
||||
// Overload for explicit function and ArrayRef
|
||||
template <class F, class T>
|
||||
inline auto fmap(const T& inputs, const F& fn)
|
||||
-> std::vector<decltype(fn(*inputs.begin()))> {
|
||||
std::vector<decltype(fn(*inputs.begin()))> r;
|
||||
r.reserve(inputs.size());
|
||||
for (const auto& input : inputs) r.push_back(fn(input));
|
||||
return r;
|
||||
}
|
||||
|
||||
// C++ forbids taking an address of a constructor, so here's a workaround...
|
||||
// Overload for constructor (R) application
|
||||
template <typename R, typename T>
|
||||
inline std::vector<R> fmap(const T& inputs) {
|
||||
std::vector<R> r;
|
||||
r.reserve(inputs.size());
|
||||
for (auto& input : inputs) r.push_back(R(input));
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename F, typename T>
|
||||
inline std::vector<T> filter(at::ArrayRef<T> inputs, const F& fn) {
|
||||
std::vector<T> r;
|
||||
r.reserve(inputs.size());
|
||||
for (auto& input : inputs) {
|
||||
if (fn(input)) {
|
||||
r.push_back(input);
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename F, typename T>
|
||||
inline std::vector<T> filter(const std::vector<T>& inputs, const F& fn) {
|
||||
return filter<F, T>(static_cast<at::ArrayRef<T>>(inputs), fn);
|
||||
}
|
||||
|
||||
} // namespace c10
|
||||
@@ -0,0 +1,686 @@
|
||||
// Copyright (c) 2025 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.
|
||||
|
||||
// The file has been adapted from pytorch project
|
||||
// Licensed under BSD-style license -
|
||||
// https://github.com/pytorch/pytorch/blob/main/LICENSE
|
||||
|
||||
#pragma once
|
||||
#include <ATen/core/TensorBody.h>
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
namespace torch {
|
||||
|
||||
class CustomClassHolder {
|
||||
public:
|
||||
virtual ~CustomClassHolder() = default;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class intrusive_ptr {
|
||||
public:
|
||||
using element_type = T;
|
||||
using pointer = T*;
|
||||
|
||||
intrusive_ptr() : ptr_(nullptr) {}
|
||||
intrusive_ptr(T* ptr) : ptr_(std::shared_ptr<T>(ptr)) {} // NOLINT
|
||||
intrusive_ptr(std::shared_ptr<T> ptr) : ptr_(ptr) {} // NOLINT
|
||||
|
||||
template <typename... Args>
|
||||
static intrusive_ptr<T> make(Args&&... args) {
|
||||
return intrusive_ptr<T>(std::make_shared<T>(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
T* get() const { return ptr_.get(); }
|
||||
T& operator*() const { return *ptr_; }
|
||||
T* operator->() const { return ptr_.get(); }
|
||||
|
||||
// For IValue
|
||||
std::shared_ptr<T> get_shared() const { return ptr_; }
|
||||
|
||||
explicit operator bool() const { return ptr_ != nullptr; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<T> ptr_;
|
||||
};
|
||||
|
||||
template <typename T, typename... Args>
|
||||
intrusive_ptr<T> make_intrusive(Args&&... args) {
|
||||
return intrusive_ptr<T>::make(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct _fake_type {};
|
||||
|
||||
enum class TypeTag {
|
||||
None = 0,
|
||||
Bool,
|
||||
Int,
|
||||
Double,
|
||||
String,
|
||||
Tensor,
|
||||
GenericList,
|
||||
CustomClass,
|
||||
Tuple
|
||||
};
|
||||
|
||||
class IValue; // Forward declaration
|
||||
|
||||
// Forward declaration of generic_to template function
|
||||
template <typename T>
|
||||
T generic_to(const IValue& ivalue, _fake_type<T>);
|
||||
|
||||
using GenericList = std::vector<IValue>;
|
||||
|
||||
// Separate tuple wrapper to avoid ambiguity with GenericList
|
||||
struct GenericTuple {
|
||||
std::vector<IValue> elements;
|
||||
|
||||
GenericTuple();
|
||||
GenericTuple(std::vector<IValue> elems); // NOLINT
|
||||
|
||||
size_t size() const;
|
||||
IValue& operator[](size_t idx);
|
||||
const IValue& operator[](size_t idx) const;
|
||||
};
|
||||
|
||||
class IValue {
|
||||
private:
|
||||
struct CustomClassWrapper {
|
||||
std::shared_ptr<CustomClassHolder> ptr;
|
||||
std::string class_name;
|
||||
|
||||
CustomClassWrapper(std::shared_ptr<CustomClassHolder> p,
|
||||
const std::string& name)
|
||||
: ptr(std::move(p)), class_name(name) {}
|
||||
};
|
||||
|
||||
public:
|
||||
IValue() : tag_(TypeTag::None), value_(std::monostate{}) {}
|
||||
|
||||
IValue(bool val) : tag_(TypeTag::Bool), value_(val) {} // NOLINT
|
||||
IValue(int val) // NOLINT
|
||||
: tag_(TypeTag::Int), value_(static_cast<int64_t>(val)) {}
|
||||
IValue(int64_t val) : tag_(TypeTag::Int), value_(val) {} // NOLINT
|
||||
IValue(double val) : tag_(TypeTag::Double), value_(val) {} // NOLINT
|
||||
IValue(const std::string& val) // NOLINT
|
||||
: tag_(TypeTag::String), value_(val) {}
|
||||
IValue(std::string&& val) // NOLINT
|
||||
: tag_(TypeTag::String), value_(std::move(val)) {}
|
||||
IValue(const char* val) // NOLINT
|
||||
: tag_(TypeTag::String), value_(std::string(val)) {}
|
||||
IValue(at::Tensor val) : tag_(TypeTag::Tensor), value_(val) {} // NOLINT
|
||||
IValue(ScalarType val) // NOLINT
|
||||
: tag_(TypeTag::Int),
|
||||
value_(static_cast<int64_t>(
|
||||
static_cast<std::underlying_type_t<ScalarType>>(val))) {}
|
||||
template <typename T>
|
||||
IValue(intrusive_ptr<T> ptr) // NOLINT
|
||||
: tag_(TypeTag::CustomClass),
|
||||
value_(CustomClassWrapper{ptr.get_shared(), typeid(T).name()}) {}
|
||||
|
||||
template <typename T,
|
||||
typename = std::enable_if_t<std::is_constructible_v<IValue, T>>>
|
||||
IValue(const std::vector<T>& vec) // NOLINT
|
||||
: tag_(TypeTag::GenericList) {
|
||||
GenericList generic_list;
|
||||
generic_list.reserve(vec.size());
|
||||
for (const auto& item : vec) {
|
||||
generic_list.emplace_back(IValue(item));
|
||||
}
|
||||
value_ = std::move(generic_list);
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
typename = std::enable_if_t<std::is_constructible_v<IValue, T>>>
|
||||
IValue(std::vector<T>&& vec) // NOLINT
|
||||
: tag_(TypeTag::GenericList) {
|
||||
GenericList generic_list;
|
||||
generic_list.reserve(vec.size());
|
||||
for (auto&& item : vec) {
|
||||
generic_list.emplace_back(IValue(std::move(item)));
|
||||
}
|
||||
value_ = std::move(generic_list);
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
typename = std::enable_if_t<std::is_constructible_v<IValue, T>>>
|
||||
IValue(ArrayRef<T> arr) : IValue(arr.vec()) {} // NOLINT
|
||||
|
||||
template <typename T>
|
||||
IValue(const std::optional<T>& opt) { // NOLINT
|
||||
if (opt.has_value()) {
|
||||
*this = IValue(*opt);
|
||||
} else {
|
||||
tag_ = TypeTag::None;
|
||||
value_ = std::monostate{};
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
IValue(std::optional<T>&& opt) { // NOLINT
|
||||
if (opt.has_value()) {
|
||||
*this = IValue(std::move(*opt));
|
||||
} else {
|
||||
tag_ = TypeTag::None;
|
||||
value_ = std::monostate{};
|
||||
}
|
||||
}
|
||||
|
||||
// Variadic template constructor for tuple of any number of tensors or
|
||||
// IValue-convertible types
|
||||
template <typename... Args>
|
||||
IValue(const std::tuple<Args...>& tuple_val) // NOLINT
|
||||
: tag_(TypeTag::Tuple) {
|
||||
static_assert(sizeof...(Args) > 0, "Tuple must have at least one element");
|
||||
std::vector<IValue> elements;
|
||||
elements.reserve(sizeof...(Args));
|
||||
tuple_to_ivalue_vector(
|
||||
tuple_val, elements, std::index_sequence_for<Args...>{});
|
||||
value_ = GenericTuple(std::move(elements));
|
||||
}
|
||||
|
||||
// Helper function to convert tuple elements to IValue vector using index
|
||||
// sequence
|
||||
template <typename Tuple, std::size_t... I>
|
||||
void tuple_to_ivalue_vector(const Tuple& tuple_val,
|
||||
std::vector<IValue>& elements, // NOLINT
|
||||
std::index_sequence<I...>) {
|
||||
(elements.emplace_back(std::get<I>(tuple_val)), ...);
|
||||
}
|
||||
|
||||
IValue(const IValue& other) = default;
|
||||
IValue(IValue&& other) = default;
|
||||
IValue& operator=(const IValue& other) = default;
|
||||
IValue& operator=(IValue&& other) = default;
|
||||
|
||||
bool is_none() const { return tag_ == TypeTag::None; }
|
||||
bool is_bool() const { return tag_ == TypeTag::Bool; }
|
||||
bool is_int() const { return tag_ == TypeTag::Int; }
|
||||
bool is_double() const { return tag_ == TypeTag::Double; }
|
||||
bool is_string() const { return tag_ == TypeTag::String; }
|
||||
bool is_list() const { return tag_ == TypeTag::GenericList; }
|
||||
bool is_tensor() const { return tag_ == TypeTag::Tensor; }
|
||||
bool is_custom_class() const { return tag_ == TypeTag::CustomClass; }
|
||||
bool is_tuple() const { return tag_ == TypeTag::Tuple; }
|
||||
|
||||
bool isNone() const { return is_none(); }
|
||||
bool isBool() const { return is_bool(); }
|
||||
bool isInt() const { return is_int(); }
|
||||
bool isDouble() const { return is_double(); }
|
||||
bool isString() const { return is_string(); }
|
||||
bool isList() const { return is_list(); }
|
||||
bool isTensor() const { return is_tensor(); }
|
||||
bool isCustomClass() const { return is_custom_class(); }
|
||||
bool isTuple() const { return is_tuple(); }
|
||||
|
||||
bool to_bool() const {
|
||||
if (!is_bool()) throw std::runtime_error("Not a bool");
|
||||
return std::get<bool>(value_);
|
||||
}
|
||||
|
||||
int64_t to_int() const {
|
||||
if (!is_int()) throw std::runtime_error("Not an int");
|
||||
return std::get<int64_t>(value_);
|
||||
}
|
||||
|
||||
double to_double() const {
|
||||
if (!is_double()) throw std::runtime_error("Not a double");
|
||||
return std::get<double>(value_);
|
||||
}
|
||||
|
||||
const std::string& to_string() const {
|
||||
if (!is_string()) throw std::runtime_error("Not a string");
|
||||
return std::get<std::string>(value_);
|
||||
}
|
||||
|
||||
const std::string_view to_string_view() const {
|
||||
if (!is_string()) throw std::runtime_error("Not a string");
|
||||
const auto& str = std::get<std::string>(value_);
|
||||
return std::string_view(str.data(), str.size());
|
||||
}
|
||||
|
||||
const GenericList& to_list() const {
|
||||
if (!is_list()) throw std::runtime_error("Not a list");
|
||||
return std::get<GenericList>(value_);
|
||||
}
|
||||
|
||||
GenericList& to_list() {
|
||||
if (!is_list()) throw std::runtime_error("Not a list");
|
||||
return std::get<GenericList>(value_);
|
||||
}
|
||||
|
||||
at::Tensor to_tensor() const {
|
||||
if (!is_tensor()) throw std::runtime_error("Not a tensor");
|
||||
return std::get<at::Tensor>(value_);
|
||||
}
|
||||
|
||||
const GenericTuple& to_tuple() const {
|
||||
if (!is_tuple()) throw std::runtime_error("Not a tuple");
|
||||
return std::get<GenericTuple>(value_);
|
||||
}
|
||||
|
||||
GenericTuple& to_tuple() {
|
||||
if (!is_tuple()) throw std::runtime_error("Not a tuple");
|
||||
return std::get<GenericTuple>(value_);
|
||||
}
|
||||
|
||||
at::ScalarType to_scalar_type() const {
|
||||
if (!is_int()) throw std::runtime_error("Not an int");
|
||||
return static_cast<at::ScalarType>(std::get<int64_t>(value_));
|
||||
}
|
||||
|
||||
bool toBool() const { return to_bool(); }
|
||||
int64_t toInt() const { return to_int(); }
|
||||
double toDouble() const { return to_double(); }
|
||||
const std::string& toStringRef() const { return to_string(); }
|
||||
std::string_view toStringView() const { return to_string_view(); }
|
||||
at::Tensor toTensor() const { return to_tensor(); }
|
||||
at::ScalarType toScalarType() const { return to_scalar_type(); }
|
||||
|
||||
std::string tagKind() const {
|
||||
switch (tag_) {
|
||||
case TypeTag::None:
|
||||
return "None";
|
||||
case TypeTag::Bool:
|
||||
return "Bool";
|
||||
case TypeTag::Int:
|
||||
return "Int";
|
||||
case TypeTag::Double:
|
||||
return "Double";
|
||||
case TypeTag::String:
|
||||
return "String";
|
||||
case TypeTag::Tensor:
|
||||
return "Tensor";
|
||||
case TypeTag::GenericList:
|
||||
return "GenericList";
|
||||
case TypeTag::CustomClass:
|
||||
return "CustomClass";
|
||||
case TypeTag::Tuple:
|
||||
return "Tuple";
|
||||
default:
|
||||
return "InvalidTag";
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
intrusive_ptr<T> to_custom_class() const {
|
||||
if (!is_custom_class()) throw std::runtime_error("Not a custom class");
|
||||
const auto& wrapper = std::get<CustomClassWrapper>(value_);
|
||||
auto casted = std::dynamic_pointer_cast<T>(wrapper.ptr);
|
||||
if (!casted) {
|
||||
throw std::runtime_error("Cannot cast custom class to requested type");
|
||||
}
|
||||
return intrusive_ptr<T>(casted);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
struct is_intrusive_ptr : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct is_intrusive_ptr<intrusive_ptr<T>> : std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
static constexpr bool is_intrusive_ptr_v = is_intrusive_ptr<T>::value;
|
||||
|
||||
public:
|
||||
bool try_to_bool(bool& out) const { // NOLINT
|
||||
if (is_bool()) {
|
||||
out = std::get<bool>(value_);
|
||||
return true;
|
||||
} else if (is_int()) {
|
||||
out = (std::get<int64_t>(value_) != 0);
|
||||
return true;
|
||||
} else if (is_double()) {
|
||||
out = (std::get<double>(value_) != 0.0);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool try_to_int(int& out) const { // NOLINT
|
||||
if (is_int()) {
|
||||
out = static_cast<int>(std::get<int64_t>(value_));
|
||||
return true;
|
||||
} else if (is_double()) {
|
||||
double val = std::get<double>(value_);
|
||||
if (val != static_cast<int>(val)) {
|
||||
std::cout << "Warning: Converting double(" << val
|
||||
<< ") to int (precision loss)" << std::endl;
|
||||
}
|
||||
out = static_cast<int>(val);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool try_to_double(double& out) const { // NOLINT
|
||||
if (is_double()) {
|
||||
out = std::get<double>(value_);
|
||||
return true;
|
||||
} else if (is_int()) {
|
||||
out = static_cast<double>(std::get<int64_t>(value_));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool try_to_string(std::string& out) const { // NOLINT
|
||||
if (is_string()) {
|
||||
out = std::get<std::string>(value_);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool try_to_tensor(at::Tensor& out) const { // NOLINT
|
||||
if (is_tensor()) {
|
||||
out = std::get<at::Tensor>(value_);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool try_to_scalar_type(at::ScalarType& out) const { // NOLINT
|
||||
if (is_int()) {
|
||||
out = static_cast<at::ScalarType>(std::get<int64_t>(value_));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool try_to_optional_type(std::optional<T>& out) const { // NOLINT
|
||||
if (is_none()) {
|
||||
out = std::nullopt;
|
||||
return true;
|
||||
} else {
|
||||
T value;
|
||||
if (try_convert_to<T>(value)) {
|
||||
out = value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool try_to_custom_class(std::shared_ptr<CustomClassHolder>& out, // NOLINT
|
||||
const std::string& expected_class_name) const {
|
||||
if (is_custom_class()) {
|
||||
const auto& wrapper = std::get<CustomClassWrapper>(value_);
|
||||
if (wrapper.class_name == expected_class_name) {
|
||||
out = wrapper.ptr;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool try_convert_to(T& out) const { // NOLINT
|
||||
// Remove reference and cv-qualifiers from T
|
||||
using BaseType = std::remove_cv_t<std::remove_reference_t<T>>;
|
||||
|
||||
if constexpr (std::is_same_v<BaseType, bool>) {
|
||||
return try_to_bool(const_cast<bool&>(reinterpret_cast<const bool&>(out)));
|
||||
} else if constexpr (std::is_same_v<BaseType, int>) {
|
||||
return try_to_int(const_cast<int&>(reinterpret_cast<const int&>(out)));
|
||||
} else if constexpr (std::is_same_v<BaseType, double>) {
|
||||
return try_to_double(
|
||||
const_cast<double&>(reinterpret_cast<const double&>(out)));
|
||||
} else if constexpr (std::is_same_v<BaseType, std::string>) {
|
||||
return try_to_string(
|
||||
const_cast<std::string&>(reinterpret_cast<const std::string&>(out)));
|
||||
} else if constexpr (std::is_same_v<BaseType, at::Tensor>) {
|
||||
return try_to_tensor(
|
||||
const_cast<at::Tensor&>(reinterpret_cast<const at::Tensor&>(out)));
|
||||
} else if constexpr (std::is_same_v<BaseType, at::ScalarType>) {
|
||||
return try_to_scalar_type(const_cast<at::ScalarType&>(
|
||||
reinterpret_cast<const at::ScalarType&>(out)));
|
||||
} else {
|
||||
try {
|
||||
// Handle const types by removing const and using const_cast
|
||||
using NonConstType = std::remove_const_t<T>;
|
||||
NonConstType temp = this->to<BaseType>();
|
||||
const_cast<NonConstType&>(out) = std::move(temp);
|
||||
return true;
|
||||
} catch (const std::exception&) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_custom_class_name() const {
|
||||
if (!is_custom_class()) throw std::runtime_error("Not a custom class");
|
||||
const auto& wrapper = std::get<CustomClassWrapper>(value_);
|
||||
return wrapper.class_name;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T to() && {
|
||||
return generic_to(std::move(*this), _fake_type<T>{});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T to() const& {
|
||||
return generic_to(*this, _fake_type<T>{});
|
||||
}
|
||||
|
||||
std::string type_string() const {
|
||||
switch (tag_) {
|
||||
case TypeTag::None:
|
||||
return "None";
|
||||
case TypeTag::Bool:
|
||||
return "Bool";
|
||||
case TypeTag::Int:
|
||||
return "Int";
|
||||
case TypeTag::Double:
|
||||
return "Double";
|
||||
case TypeTag::String:
|
||||
return "String";
|
||||
case TypeTag::Tensor:
|
||||
return "Tensor";
|
||||
case TypeTag::GenericList:
|
||||
return "List";
|
||||
case TypeTag::Tuple:
|
||||
return "Tuple";
|
||||
case TypeTag::CustomClass:
|
||||
return "CustomClass(" + get_custom_class_name() + ")";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
std::string to_repr() const {
|
||||
switch (tag_) {
|
||||
case TypeTag::None:
|
||||
return "None";
|
||||
case TypeTag::Bool:
|
||||
return std::get<bool>(value_) ? "true" : "false";
|
||||
case TypeTag::Int:
|
||||
return std::to_string(std::get<int64_t>(value_));
|
||||
case TypeTag::Double:
|
||||
return std::to_string(std::get<double>(value_));
|
||||
case TypeTag::String:
|
||||
return "\"" + std::get<std::string>(value_) + "\"";
|
||||
case TypeTag::Tensor: {
|
||||
const auto& tensor = std::get<at::Tensor>(value_);
|
||||
return "Tensor(" + std::to_string(tensor.numel()) + " elements)";
|
||||
}
|
||||
case TypeTag::GenericList: {
|
||||
const auto& list = std::get<GenericList>(value_);
|
||||
std::string result = "[";
|
||||
for (size_t i = 0; i < list.size(); ++i) {
|
||||
if (i > 0) result += ", ";
|
||||
result += list[i].to_repr();
|
||||
}
|
||||
result += "]";
|
||||
return result;
|
||||
}
|
||||
case TypeTag::Tuple: {
|
||||
const auto& tuple = std::get<GenericTuple>(value_);
|
||||
std::string result = "(";
|
||||
for (size_t i = 0; i < tuple.size(); ++i) {
|
||||
if (i > 0) result += ", ";
|
||||
result += tuple[i].to_repr();
|
||||
}
|
||||
if (tuple.size() == 1) result += ","; // Single element tuple
|
||||
result += ")";
|
||||
return result;
|
||||
}
|
||||
case TypeTag::CustomClass: {
|
||||
const auto& wrapper = std::get<CustomClassWrapper>(value_);
|
||||
return "CustomClass(" + wrapper.class_name + ")";
|
||||
}
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const IValue& val) {
|
||||
return os << val.to_repr();
|
||||
}
|
||||
|
||||
private:
|
||||
TypeTag tag_;
|
||||
std::variant<std::monostate,
|
||||
bool,
|
||||
int64_t,
|
||||
double,
|
||||
std::string,
|
||||
at::Tensor,
|
||||
GenericList,
|
||||
CustomClassWrapper,
|
||||
GenericTuple>
|
||||
value_;
|
||||
template <typename T>
|
||||
friend T generic_to(const IValue& ivalue, _fake_type<T>);
|
||||
};
|
||||
|
||||
inline GenericTuple::GenericTuple() = default;
|
||||
inline GenericTuple::GenericTuple(std::vector<IValue> elems) // NOLINT
|
||||
: elements(std::move(elems)) {}
|
||||
|
||||
inline size_t GenericTuple::size() const { return elements.size(); }
|
||||
inline IValue& GenericTuple::operator[](size_t idx) { return elements[idx]; }
|
||||
inline const IValue& GenericTuple::operator[](size_t idx) const {
|
||||
return elements[idx];
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool generic_to(const IValue& ivalue, _fake_type<bool>) {
|
||||
return ivalue.to_bool();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline int generic_to(const IValue& ivalue, _fake_type<int>) {
|
||||
return static_cast<int>(ivalue.to_int());
|
||||
}
|
||||
|
||||
template <>
|
||||
inline int64_t generic_to(const IValue& ivalue, _fake_type<int64_t>) {
|
||||
return ivalue.to_int();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline double generic_to(const IValue& ivalue, _fake_type<double>) {
|
||||
return ivalue.to_double();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline std::string generic_to(const IValue& ivalue, _fake_type<std::string>) {
|
||||
return ivalue.to_string();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline std::string_view generic_to(const IValue& ivalue,
|
||||
_fake_type<std::string_view>) {
|
||||
return ivalue.to_string_view();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline at::Tensor generic_to(const IValue& ivalue, _fake_type<at::Tensor>) {
|
||||
return ivalue.to_tensor();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::vector<T> generic_to(const IValue& ivalue, _fake_type<std::vector<T>>) {
|
||||
auto list = ivalue.to_list();
|
||||
std::vector<T> result;
|
||||
result.reserve(list.size());
|
||||
for (const auto& item : list) {
|
||||
result.push_back(item.to<T>());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Helper for converting IValue tuple to std::tuple using index sequence
|
||||
template <typename Tuple, std::size_t... I>
|
||||
Tuple ivalue_to_tuple_impl(const IValue& ivalue, std::index_sequence<I...>) {
|
||||
const auto& generic_tuple = ivalue.to_tuple();
|
||||
if (generic_tuple.size() != sizeof...(I)) {
|
||||
throw std::runtime_error("Tuple size mismatch: expected " +
|
||||
std::to_string(sizeof...(I)) + " but got " +
|
||||
std::to_string(generic_tuple.size()));
|
||||
}
|
||||
// Use std::get<I> with index instead of type to avoid ambiguity
|
||||
// when tuple contains multiple elements of the same type
|
||||
return Tuple{generic_tuple[I].to<std::tuple_element_t<I, Tuple>>()...};
|
||||
}
|
||||
|
||||
// Generic conversion from IValue to std::tuple
|
||||
template <typename... Args>
|
||||
std::tuple<Args...> generic_to(const IValue& ivalue,
|
||||
_fake_type<std::tuple<Args...>>) {
|
||||
return ivalue_to_tuple_impl<std::tuple<Args...>>(
|
||||
ivalue, std::index_sequence_for<Args...>{});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ArrayRef<T> generic_to(const IValue& ivalue, _fake_type<ArrayRef<T>>) {
|
||||
static thread_local std::vector<T> temp_storage;
|
||||
temp_storage = ivalue.to<std::vector<T>>();
|
||||
return ArrayRef<T>(temp_storage);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::optional<T> generic_to(const IValue& ivalue,
|
||||
_fake_type<std::optional<T>>) {
|
||||
if (ivalue.is_none()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return std::optional<T>(ivalue.to<T>());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
intrusive_ptr<T> generic_to(const IValue& ivalue,
|
||||
_fake_type<intrusive_ptr<T>>) {
|
||||
return ivalue.to_custom_class<T>();
|
||||
}
|
||||
|
||||
} // namespace torch
|
||||
|
||||
namespace c10 {
|
||||
using IValue = ::torch::IValue;
|
||||
}
|
||||
@@ -0,0 +1,385 @@
|
||||
// Copyright (c) 2026 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.
|
||||
|
||||
// The file has been adapted from pytorch project
|
||||
// Licensed under BSD-style license -
|
||||
// https://github.com/pytorch/pytorch/blob/main/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ATen/core/jit_type_base.h>
|
||||
|
||||
#include <c10/util/Exception.h>
|
||||
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace c10 {
|
||||
|
||||
inline bool operator!=(const Type& lhs, const Type& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
// Lightweight runtime types used only by the compat schema parser.
|
||||
class SchemaAtomicType final : public SharedType {
|
||||
public:
|
||||
static std::shared_ptr<SchemaAtomicType> create(TypeKind kind,
|
||||
std::string repr) {
|
||||
return std::shared_ptr<SchemaAtomicType>(
|
||||
new SchemaAtomicType(kind, std::move(repr)));
|
||||
}
|
||||
|
||||
bool equals(const Type& rhs) const override { return rhs.kind() == kind(); }
|
||||
|
||||
std::string str() const override { return repr_; }
|
||||
|
||||
private:
|
||||
SchemaAtomicType(TypeKind kind, std::string repr)
|
||||
: SharedType(kind), repr_(std::move(repr)) {}
|
||||
|
||||
std::string annotation_str_impl(
|
||||
[[maybe_unused]] const TypePrinter& printer = nullptr) const override {
|
||||
return repr_;
|
||||
}
|
||||
|
||||
std::string repr_;
|
||||
};
|
||||
|
||||
class SchemaOptionalType final : public SharedType {
|
||||
public:
|
||||
static const TypeKind Kind = TypeKind::OptionalType;
|
||||
|
||||
static std::shared_ptr<SchemaOptionalType> create(TypePtr elem) {
|
||||
return std::shared_ptr<SchemaOptionalType>(
|
||||
new SchemaOptionalType(std::move(elem)));
|
||||
}
|
||||
|
||||
bool equals(const Type& rhs) const override {
|
||||
if (rhs.kind() != kind()) {
|
||||
return false;
|
||||
}
|
||||
const auto contained = rhs.containedTypes();
|
||||
return contained.size() == 1 && *elem_.front() == *contained.front();
|
||||
}
|
||||
|
||||
std::string str() const override { return elem_.front()->str() + "?"; }
|
||||
|
||||
at::ArrayRef<TypePtr> containedTypes() const override { return elem_; }
|
||||
|
||||
TypePtr createWithContained(
|
||||
std::vector<TypePtr> contained_types) const override {
|
||||
TORCH_CHECK(contained_types.size() == 1,
|
||||
"Optional type expects exactly one contained type");
|
||||
return create(std::move(contained_types.front()));
|
||||
}
|
||||
|
||||
private:
|
||||
explicit SchemaOptionalType(TypePtr elem)
|
||||
: SharedType(Kind), elem_{std::move(elem)} {}
|
||||
|
||||
std::string annotation_str_impl(
|
||||
const TypePrinter& printer = nullptr) const override {
|
||||
return "Optional[" + elem_.front()->annotation_str(printer) + "]";
|
||||
}
|
||||
|
||||
std::vector<TypePtr> elem_;
|
||||
};
|
||||
|
||||
class SchemaTupleType final : public SharedType {
|
||||
public:
|
||||
static const TypeKind Kind = TypeKind::TupleType;
|
||||
|
||||
static std::shared_ptr<SchemaTupleType> create(
|
||||
std::vector<TypePtr> elements) {
|
||||
return std::shared_ptr<SchemaTupleType>(
|
||||
new SchemaTupleType(std::move(elements)));
|
||||
}
|
||||
|
||||
bool equals(const Type& rhs) const override {
|
||||
if (rhs.kind() != kind()) {
|
||||
return false;
|
||||
}
|
||||
const auto rhs_elems = rhs.containedTypes();
|
||||
if (rhs_elems.size() != elements_.size()) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < elements_.size(); ++i) {
|
||||
if (*elements_[i] != *rhs_elems[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string str() const override {
|
||||
std::stringstream ss;
|
||||
ss << "(";
|
||||
for (size_t i = 0; i < elements_.size(); ++i) {
|
||||
if (i > 0) {
|
||||
ss << ", ";
|
||||
}
|
||||
ss << elements_[i]->str();
|
||||
}
|
||||
ss << ")";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
at::ArrayRef<TypePtr> containedTypes() const override { return elements_; }
|
||||
|
||||
TypePtr createWithContained(
|
||||
std::vector<TypePtr> contained_types) const override {
|
||||
return create(std::move(contained_types));
|
||||
}
|
||||
|
||||
private:
|
||||
explicit SchemaTupleType(std::vector<TypePtr> elements)
|
||||
: SharedType(Kind), elements_(std::move(elements)) {}
|
||||
|
||||
std::string annotation_str_impl(
|
||||
const TypePrinter& printer = nullptr) const override {
|
||||
std::stringstream ss;
|
||||
ss << "Tuple[";
|
||||
for (size_t i = 0; i < elements_.size(); ++i) {
|
||||
if (i > 0) {
|
||||
ss << ", ";
|
||||
}
|
||||
ss << elements_[i]->annotation_str(printer);
|
||||
}
|
||||
ss << "]";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::vector<TypePtr> elements_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
inline TypePtr makeSchemaAtomicType(TypeKind kind, std::string repr) {
|
||||
return detail::SchemaAtomicType::create(kind, std::move(repr));
|
||||
}
|
||||
|
||||
inline TypePtr makeSchemaOptionalType(TypePtr elem) {
|
||||
return detail::SchemaOptionalType::create(std::move(elem));
|
||||
}
|
||||
|
||||
inline TypePtr makeSchemaTupleType(std::vector<TypePtr> elements) {
|
||||
return detail::SchemaTupleType::create(std::move(elements));
|
||||
}
|
||||
|
||||
struct TensorType;
|
||||
using TensorTypePtr = SingletonTypePtr<TensorType>;
|
||||
struct PADDLE_API TensorType : public Type {
|
||||
bool equals(const Type& rhs) const override { return rhs.kind() == kind(); }
|
||||
|
||||
std::string str() const override { return "Tensor"; }
|
||||
|
||||
bool isInferredType() const { return is_inferred_; }
|
||||
|
||||
static const TypeKind Kind = TypeKind::TensorType;
|
||||
|
||||
static TensorTypePtr get() {
|
||||
static TensorType value(/*inferred=*/false);
|
||||
return TensorTypePtr(&value);
|
||||
}
|
||||
|
||||
static TensorTypePtr getInferred() {
|
||||
static TensorType value(/*inferred=*/true);
|
||||
return TensorTypePtr(&value);
|
||||
}
|
||||
|
||||
private:
|
||||
explicit TensorType(bool inferred)
|
||||
: Type(TypeKind::TensorType), is_inferred_(inferred) {}
|
||||
|
||||
bool is_inferred_;
|
||||
};
|
||||
|
||||
struct NumberType;
|
||||
using NumberTypePtr = SingletonTypePtr<NumberType>;
|
||||
struct PADDLE_API NumberType : public Type {
|
||||
bool equals(const Type& rhs) const override { return rhs.kind() == kind(); }
|
||||
|
||||
bool isSubtypeOfExt(const Type& rhs, std::ostream* why_not) const override {
|
||||
return rhs.kind() == TypeKind::NumberType ||
|
||||
Type::isSubtypeOfExt(rhs, why_not);
|
||||
}
|
||||
|
||||
std::string str() const override { return "Scalar"; }
|
||||
|
||||
static const TypeKind Kind = TypeKind::NumberType;
|
||||
|
||||
static NumberTypePtr get() {
|
||||
static NumberType value;
|
||||
return NumberTypePtr(&value);
|
||||
}
|
||||
|
||||
protected:
|
||||
explicit NumberType(TypeKind kind = TypeKind::NumberType) : Type(kind) {}
|
||||
|
||||
std::string annotation_str_impl(
|
||||
[[maybe_unused]] const TypePrinter& printer = nullptr) const override {
|
||||
return "number";
|
||||
}
|
||||
};
|
||||
|
||||
struct FloatType;
|
||||
using FloatTypePtr = SingletonTypePtr<FloatType>;
|
||||
struct PADDLE_API FloatType : public NumberType {
|
||||
bool equals(const Type& rhs) const override { return rhs.kind() == kind(); }
|
||||
|
||||
std::string str() const override { return "float"; }
|
||||
|
||||
bool isSubtypeOfExt(const Type& rhs, std::ostream* why_not) const override {
|
||||
return rhs.kind() == TypeKind::NumberType ||
|
||||
Type::isSubtypeOfExt(rhs, why_not);
|
||||
}
|
||||
|
||||
static const TypeKind Kind = TypeKind::FloatType;
|
||||
|
||||
static FloatTypePtr get() {
|
||||
static FloatType value;
|
||||
return FloatTypePtr(&value);
|
||||
}
|
||||
|
||||
private:
|
||||
FloatType() : NumberType(TypeKind::FloatType) {}
|
||||
|
||||
std::string annotation_str_impl(
|
||||
[[maybe_unused]] const TypePrinter& printer = nullptr) const override {
|
||||
return "float";
|
||||
}
|
||||
};
|
||||
|
||||
struct IntType;
|
||||
using IntTypePtr = SingletonTypePtr<IntType>;
|
||||
struct PADDLE_API IntType : public NumberType {
|
||||
bool equals(const Type& rhs) const override { return rhs.kind() == kind(); }
|
||||
|
||||
std::string str() const override { return "int"; }
|
||||
|
||||
bool isSubtypeOfExt(const Type& rhs, std::ostream* why_not) const override {
|
||||
return rhs.kind() == TypeKind::NumberType ||
|
||||
Type::isSubtypeOfExt(rhs, why_not);
|
||||
}
|
||||
|
||||
static const TypeKind Kind = TypeKind::IntType;
|
||||
|
||||
static IntTypePtr get() {
|
||||
static IntType value;
|
||||
return IntTypePtr(&value);
|
||||
}
|
||||
|
||||
private:
|
||||
IntType() : NumberType(TypeKind::IntType) {}
|
||||
|
||||
std::string annotation_str_impl(
|
||||
[[maybe_unused]] const TypePrinter& printer = nullptr) const override {
|
||||
return "int";
|
||||
}
|
||||
};
|
||||
|
||||
struct BoolType;
|
||||
using BoolTypePtr = SingletonTypePtr<BoolType>;
|
||||
struct PADDLE_API BoolType : public Type {
|
||||
bool equals(const Type& rhs) const override { return rhs.kind() == kind(); }
|
||||
|
||||
std::string str() const override { return "bool"; }
|
||||
|
||||
static const TypeKind Kind = TypeKind::BoolType;
|
||||
|
||||
static BoolTypePtr get() {
|
||||
static BoolType value;
|
||||
return BoolTypePtr(&value);
|
||||
}
|
||||
|
||||
private:
|
||||
BoolType() : Type(TypeKind::BoolType) {}
|
||||
};
|
||||
|
||||
struct StringType;
|
||||
using StringTypePtr = SingletonTypePtr<StringType>;
|
||||
struct PADDLE_API StringType : public Type {
|
||||
bool equals(const Type& rhs) const override { return rhs.kind() == kind(); }
|
||||
|
||||
std::string str() const override { return annotation_str(); }
|
||||
|
||||
std::string annotation_str_impl(
|
||||
[[maybe_unused]] const TypePrinter& printer = nullptr) const override {
|
||||
return "str";
|
||||
}
|
||||
|
||||
static const TypeKind Kind = TypeKind::StringType;
|
||||
|
||||
static StringTypePtr get() {
|
||||
static StringType value;
|
||||
return StringTypePtr(&value);
|
||||
}
|
||||
|
||||
private:
|
||||
StringType() : Type(TypeKind::StringType) {}
|
||||
};
|
||||
|
||||
struct NoneType;
|
||||
using NoneTypePtr = SingletonTypePtr<NoneType>;
|
||||
struct PADDLE_API NoneType : public Type {
|
||||
bool equals(const Type& rhs) const override { return rhs.kind() == kind(); }
|
||||
|
||||
std::string str() const override { return "NoneType"; }
|
||||
|
||||
bool isSubtypeOfExt(const Type& rhs, std::ostream* why_not) const override {
|
||||
return rhs.kind() == TypeKind::OptionalType ||
|
||||
Type::isSubtypeOfExt(rhs, why_not);
|
||||
}
|
||||
|
||||
static const TypeKind Kind = TypeKind::NoneType;
|
||||
|
||||
static NoneTypePtr get() {
|
||||
static NoneType value;
|
||||
return NoneTypePtr(&value);
|
||||
}
|
||||
|
||||
private:
|
||||
NoneType() : Type(TypeKind::NoneType) {}
|
||||
};
|
||||
|
||||
struct DeviceObjType;
|
||||
using DeviceObjTypePtr = SingletonTypePtr<DeviceObjType>;
|
||||
struct PADDLE_API DeviceObjType : public Type {
|
||||
bool equals(const Type& rhs) const override { return rhs.kind() == kind(); }
|
||||
|
||||
std::string str() const override { return "Device"; }
|
||||
|
||||
static const TypeKind Kind = TypeKind::DeviceObjType;
|
||||
|
||||
static DeviceObjTypePtr get() {
|
||||
static DeviceObjType value;
|
||||
return DeviceObjTypePtr(&value);
|
||||
}
|
||||
|
||||
private:
|
||||
DeviceObjType() : Type(TypeKind::DeviceObjType) {}
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& out, const Type& t) {
|
||||
out << t.str();
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace c10
|
||||
@@ -0,0 +1,337 @@
|
||||
// Copyright (c) 2026 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.
|
||||
|
||||
// The file has been adapted from pytorch project
|
||||
// Licensed under BSD-style license -
|
||||
// https://github.com/pytorch/pytorch/blob/main/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "ATen/core/type_ptr.h"
|
||||
#include "c10/util/ArrayRef.h"
|
||||
#include "c10/util/Exception.h"
|
||||
|
||||
namespace c10 {
|
||||
|
||||
#define C10_FORALL_TYPES(_) \
|
||||
_(TensorType) \
|
||||
_(StringType) \
|
||||
_(IntType) \
|
||||
_(FloatType) \
|
||||
_(BoolType) \
|
||||
_(NoneType) \
|
||||
_(TupleType) \
|
||||
_(NumberType) \
|
||||
_(OptionalType) \
|
||||
_(UnionType) \
|
||||
_(DeviceObjType) \
|
||||
_(DynamicType)
|
||||
|
||||
enum class TypeKind {
|
||||
#define DEFINE_TYPE(T) T,
|
||||
C10_FORALL_TYPES(DEFINE_TYPE)
|
||||
#undef DEFINE_TYPE
|
||||
};
|
||||
|
||||
struct Type;
|
||||
struct SharedType;
|
||||
using TypePrinter = std::function<std::optional<std::string>(const Type&)>;
|
||||
|
||||
namespace detail {
|
||||
template <typename T>
|
||||
struct IsSingletonType : std::false_type {};
|
||||
} // namespace detail
|
||||
#define TORCH_DECLARE_SINGLETON(Type) \
|
||||
struct Type; \
|
||||
namespace detail { \
|
||||
template <> \
|
||||
struct IsSingletonType<Type> : std::true_type {}; \
|
||||
}
|
||||
|
||||
TORCH_DECLARE_SINGLETON(NumberType)
|
||||
TORCH_DECLARE_SINGLETON(TensorType)
|
||||
TORCH_DECLARE_SINGLETON(StringType)
|
||||
TORCH_DECLARE_SINGLETON(IntType)
|
||||
TORCH_DECLARE_SINGLETON(FloatType)
|
||||
TORCH_DECLARE_SINGLETON(BoolType)
|
||||
TORCH_DECLARE_SINGLETON(NoneType)
|
||||
TORCH_DECLARE_SINGLETON(TupleType)
|
||||
TORCH_DECLARE_SINGLETON(OptionalType)
|
||||
TORCH_DECLARE_SINGLETON(DeviceObjType)
|
||||
|
||||
namespace detail {
|
||||
template <typename T, typename Enable = void>
|
||||
struct CastReturnType {
|
||||
using type = std::shared_ptr<T>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct CastReturnType<T, std::enable_if_t<IsSingletonType<T>::value>> {
|
||||
using type = SingletonTypePtr<T>;
|
||||
};
|
||||
|
||||
template <typename T, typename Enable = void>
|
||||
struct CastConstReturnType {
|
||||
using type = std::shared_ptr<const T>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct CastConstReturnType<T, std::enable_if_t<IsSingletonType<T>::value>> {
|
||||
using type = SingletonTypePtr<const T>;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
struct PADDLE_API Type {
|
||||
friend PADDLE_API bool operator==(const Type& lhs, const Type& rhs);
|
||||
|
||||
private:
|
||||
TypeKind kind_;
|
||||
|
||||
protected:
|
||||
explicit Type(TypeKind kind) : kind_(kind) {}
|
||||
|
||||
Type(const Type&) = default;
|
||||
Type& operator=(const Type&) = default;
|
||||
Type(Type&&) noexcept = default;
|
||||
Type& operator=(Type&&) noexcept = default;
|
||||
|
||||
virtual std::string annotation_str_impl(
|
||||
const TypePrinter& /*printer*/) const {
|
||||
return str();
|
||||
}
|
||||
virtual bool equals(const Type& rhs) const = 0;
|
||||
virtual bool symmetric() const { return true; }
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
class SingletonOrSharedTypePtr {
|
||||
public:
|
||||
using element_type = typename std::shared_ptr<T>::element_type;
|
||||
|
||||
SingletonOrSharedTypePtr() = default;
|
||||
|
||||
SingletonOrSharedTypePtr(std::shared_ptr<T> x) // NOLINT(runtime/explicit)
|
||||
: repr_(std::move(x)) {}
|
||||
|
||||
template <typename U,
|
||||
std::enable_if_t<std::is_convertible_v<U*, T*>, bool> = true>
|
||||
SingletonOrSharedTypePtr(std::shared_ptr<U> x) // NOLINT(runtime/explicit)
|
||||
: repr_(std::move(x)) {}
|
||||
|
||||
SingletonOrSharedTypePtr(std::nullptr_t) // NOLINT(runtime/explicit)
|
||||
: repr_(nullptr) {}
|
||||
|
||||
SingletonOrSharedTypePtr(SingletonTypePtr<T> p) // NOLINT(runtime/explicit)
|
||||
: repr_(makeSingletonSharedPtr(p.get())) {}
|
||||
|
||||
template <typename U,
|
||||
std::enable_if_t<std::is_convertible_v<U*, T*>, bool> = true>
|
||||
SingletonOrSharedTypePtr(SingletonTypePtr<U> p) // NOLINT(runtime/explicit)
|
||||
: repr_(makeSingletonSharedPtr(static_cast<T*>(p.get()))) {}
|
||||
|
||||
SingletonOrSharedTypePtr(const SingletonOrSharedTypePtr&) = default;
|
||||
SingletonOrSharedTypePtr(SingletonOrSharedTypePtr&&) noexcept = default;
|
||||
SingletonOrSharedTypePtr& operator=(const SingletonOrSharedTypePtr&) =
|
||||
default;
|
||||
SingletonOrSharedTypePtr& operator=(SingletonOrSharedTypePtr&&) noexcept =
|
||||
default;
|
||||
~SingletonOrSharedTypePtr() = default;
|
||||
|
||||
T* get() const { return repr_.get(); }
|
||||
|
||||
operator bool() const { return repr_ != nullptr; }
|
||||
|
||||
bool operator==(std::nullptr_t) const { return repr_ == nullptr; }
|
||||
|
||||
bool operator!=(std::nullptr_t) const { return repr_ != nullptr; }
|
||||
|
||||
template <typename U = T,
|
||||
std::enable_if_t<!std::is_same_v<std::remove_const_t<U>, void>,
|
||||
bool> = true>
|
||||
U& operator*() const {
|
||||
return *get();
|
||||
}
|
||||
|
||||
T* operator->() const { return get(); }
|
||||
|
||||
private:
|
||||
static std::shared_ptr<T> makeSingletonSharedPtr(T* ptr) {
|
||||
return std::shared_ptr<T>(std::shared_ptr<T>(), ptr);
|
||||
}
|
||||
|
||||
std::shared_ptr<T> repr_;
|
||||
};
|
||||
|
||||
using TypePtr = SingletonOrSharedTypePtr<Type>;
|
||||
|
||||
virtual bool isSubtypeOfExt(const Type& rhs, std::ostream* why_not) const;
|
||||
bool isSubtypeOf(const Type& rhs) const {
|
||||
return isSubtypeOfExt(rhs, nullptr);
|
||||
}
|
||||
|
||||
// Compatibility shims to accommodate existing code that passes shared_ptrs
|
||||
// around. Ideally, we would just delete this, but it should be harmless.
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_base_of_v<Type, T>, bool> isSubtypeOf(
|
||||
const std::shared_ptr<T>& rhs) const {
|
||||
return isSubtypeOf(*rhs);
|
||||
}
|
||||
|
||||
virtual std::string str() const = 0;
|
||||
|
||||
std::string annotation_str(const TypePrinter& printer) const {
|
||||
if (printer) {
|
||||
if (auto renamed = printer(*this)) {
|
||||
return *renamed;
|
||||
}
|
||||
}
|
||||
return annotation_str_impl(printer);
|
||||
}
|
||||
std::string annotation_str() const { return annotation_str(nullptr); }
|
||||
|
||||
virtual std::string repr_str() const { return annotation_str(); }
|
||||
|
||||
TypeKind kind() const { return kind_; }
|
||||
|
||||
template <typename T,
|
||||
std::enable_if_t<!detail::IsSingletonType<T>::value, bool> = true>
|
||||
typename detail::CastReturnType<T>::type cast() {
|
||||
if (auto* typed = dynamic_cast<T*>(this)) {
|
||||
return std::static_pointer_cast<T>(typed->shared_from_this());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
template <typename T,
|
||||
std::enable_if_t<detail::IsSingletonType<T>::value, bool> = true>
|
||||
typename detail::CastReturnType<T>::type cast() {
|
||||
if (auto* typed = dynamic_cast<T*>(this)) {
|
||||
return typename detail::CastReturnType<T>::type(typed);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
template <typename T,
|
||||
std::enable_if_t<!detail::IsSingletonType<T>::value, bool> = true>
|
||||
typename detail::CastConstReturnType<T>::type cast() const {
|
||||
if (auto* typed = dynamic_cast<const T*>(this)) {
|
||||
return std::static_pointer_cast<const T>(typed->shared_from_this());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
template <typename T,
|
||||
std::enable_if_t<detail::IsSingletonType<T>::value, bool> = true>
|
||||
typename detail::CastConstReturnType<T>::type cast() const {
|
||||
if (auto* typed = dynamic_cast<const T*>(this)) {
|
||||
return typename detail::CastConstReturnType<T>::type(typed);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
virtual ~Type() = default;
|
||||
virtual at::ArrayRef<TypePtr> containedTypes() const { return {}; }
|
||||
virtual TypePtr createWithContained(
|
||||
std::vector<TypePtr> /*contained_types*/) const {
|
||||
TORCH_CHECK(
|
||||
false,
|
||||
"type with contained types did not overload createWithContained: ",
|
||||
str());
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using SingletonOrSharedTypePtr = Type::SingletonOrSharedTypePtr<T>;
|
||||
|
||||
template <typename T, typename U>
|
||||
bool operator==(const SingletonOrSharedTypePtr<T>& x,
|
||||
const SingletonOrSharedTypePtr<U>& y) {
|
||||
return static_cast<const void*>(x.get()) == static_cast<const void*>(y.get());
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
bool operator==(const SingletonOrSharedTypePtr<T>& x,
|
||||
const SingletonTypePtr<U>& y) {
|
||||
return static_cast<const void*>(x.get()) == static_cast<const void*>(y.get());
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
bool operator==(const SingletonTypePtr<T>& x,
|
||||
const SingletonOrSharedTypePtr<U>& y) {
|
||||
return static_cast<const void*>(x.get()) == static_cast<const void*>(y.get());
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
bool operator!=(const SingletonOrSharedTypePtr<T>& x,
|
||||
const SingletonOrSharedTypePtr<U>& y) {
|
||||
return !(x == y);
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
bool operator!=(const SingletonOrSharedTypePtr<T>& x,
|
||||
const SingletonTypePtr<U>& y) {
|
||||
return !(x == y);
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
bool operator!=(const SingletonTypePtr<T>& x,
|
||||
const SingletonOrSharedTypePtr<U>& y) {
|
||||
return !(x == y);
|
||||
}
|
||||
|
||||
using TypePtr = SingletonOrSharedTypePtr<Type>;
|
||||
|
||||
// Base class for Types that are guaranteed to be owned by std::shared_ptr.
|
||||
struct PADDLE_API SharedType : public Type,
|
||||
public std::enable_shared_from_this<SharedType> {
|
||||
using Type::Type;
|
||||
};
|
||||
|
||||
inline bool operator==(const Type& lhs, const Type& rhs) {
|
||||
if (!rhs.symmetric()) {
|
||||
return rhs.equals(lhs);
|
||||
}
|
||||
return lhs.equals(rhs);
|
||||
}
|
||||
|
||||
inline bool Type::isSubtypeOfExt(const Type& rhs, std::ostream* why_not) const {
|
||||
if (*this == rhs) {
|
||||
return true;
|
||||
}
|
||||
if (rhs.kind() == TypeKind::OptionalType) {
|
||||
for (const auto& inner : rhs.containedTypes()) {
|
||||
if (*this == *inner) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace c10
|
||||
|
||||
namespace std {
|
||||
template <typename T>
|
||||
struct hash<c10::SingletonOrSharedTypePtr<T>> {
|
||||
size_t operator()(const c10::SingletonOrSharedTypePtr<T>& x) const {
|
||||
return std::hash<T*>()(x.get());
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
@@ -0,0 +1,70 @@
|
||||
// Copyright (c) 2026 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.
|
||||
|
||||
// The file has been adapted from pytorch project
|
||||
// Licensed under BSD-style license -
|
||||
// https://github.com/pytorch/pytorch/blob/main/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
#include "c10/util/Exception.h"
|
||||
|
||||
namespace c10 {
|
||||
|
||||
// Compatibility wrapper around a raw pointer so that existing code
|
||||
// written to deal with a shared_ptr can keep working.
|
||||
template <typename T>
|
||||
class SingletonTypePtr {
|
||||
public:
|
||||
SingletonTypePtr(T* p) : repr_(p) {} // NOLINT(runtime/explicit)
|
||||
|
||||
// We need this to satisfy Pybind11, but it shouldn't be hit.
|
||||
explicit SingletonTypePtr(std::shared_ptr<T> /*unused*/) {
|
||||
TORCH_CHECK(false);
|
||||
}
|
||||
|
||||
using element_type = typename std::shared_ptr<T>::element_type;
|
||||
|
||||
template <typename U = T,
|
||||
std::enable_if_t<!std::is_same_v<std::remove_const_t<U>, void>,
|
||||
bool> = true>
|
||||
T& operator*() const {
|
||||
return *repr_;
|
||||
}
|
||||
|
||||
T* get() const { return repr_; }
|
||||
|
||||
T* operator->() const { return repr_; }
|
||||
|
||||
operator bool() const { return repr_ != nullptr; }
|
||||
|
||||
private:
|
||||
T* repr_{nullptr};
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
bool operator==(SingletonTypePtr<T> lhs, SingletonTypePtr<U> rhs) {
|
||||
return static_cast<const void*>(lhs.get()) ==
|
||||
static_cast<const void*>(rhs.get());
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
bool operator!=(SingletonTypePtr<T> lhs, SingletonTypePtr<U> rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
} // namespace c10
|
||||
@@ -0,0 +1,201 @@
|
||||
// Copyright (c) 2026 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.
|
||||
|
||||
#if defined(PADDLE_WITH_CUDA)
|
||||
|
||||
#include <ATen/cuda/CUDABlas.h>
|
||||
|
||||
#include "paddle/phi/backends/dynload/cublas.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
|
||||
namespace at::cuda::blas {
|
||||
|
||||
namespace {
|
||||
|
||||
inline cublasOperation_t to_cublas_op(char trans) {
|
||||
switch (trans) {
|
||||
case 'T':
|
||||
case 't':
|
||||
return CUBLAS_OP_T;
|
||||
case 'N':
|
||||
case 'n':
|
||||
return CUBLAS_OP_N;
|
||||
case 'C':
|
||||
case 'c':
|
||||
return CUBLAS_OP_C;
|
||||
default:
|
||||
PADDLE_THROW(common::errors::InvalidArgument(
|
||||
"at::cuda::blas::gemm: invalid transpose character '%c'", trans));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
/* ───────────── gemm<double> ───────────── */
|
||||
template <>
|
||||
void gemm<double>(CUDABLAS_GEMM_ARGTYPES(double)) {
|
||||
cublasHandle_t handle = at::cuda::getCurrentCUDABlasHandle();
|
||||
cublasOperation_t opa = to_cublas_op(transa);
|
||||
cublasOperation_t opb = to_cublas_op(transb);
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::cublasDgemm(handle,
|
||||
opa,
|
||||
opb,
|
||||
static_cast<int>(m),
|
||||
static_cast<int>(n),
|
||||
static_cast<int>(k),
|
||||
&alpha,
|
||||
a,
|
||||
static_cast<int>(lda),
|
||||
b,
|
||||
static_cast<int>(ldb),
|
||||
&beta,
|
||||
c,
|
||||
static_cast<int>(ldc)));
|
||||
}
|
||||
|
||||
/* ───────────── gemm<float> ───────────── */
|
||||
template <>
|
||||
void gemm<float>(CUDABLAS_GEMM_ARGTYPES(float)) {
|
||||
cublasHandle_t handle = at::cuda::getCurrentCUDABlasHandle();
|
||||
cublasOperation_t opa = to_cublas_op(transa);
|
||||
cublasOperation_t opb = to_cublas_op(transb);
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::cublasSgemm(handle,
|
||||
opa,
|
||||
opb,
|
||||
static_cast<int>(m),
|
||||
static_cast<int>(n),
|
||||
static_cast<int>(k),
|
||||
&alpha,
|
||||
a,
|
||||
static_cast<int>(lda),
|
||||
b,
|
||||
static_cast<int>(ldb),
|
||||
&beta,
|
||||
c,
|
||||
static_cast<int>(ldc)));
|
||||
}
|
||||
|
||||
/* ───────────── gemm<c10::complex<double>> ───────────── */
|
||||
template <>
|
||||
void gemm<c10::complex<double>>(CUDABLAS_GEMM_ARGTYPES(c10::complex<double>)) {
|
||||
cublasHandle_t handle = at::cuda::getCurrentCUDABlasHandle();
|
||||
cublasOperation_t opa = to_cublas_op(transa);
|
||||
cublasOperation_t opb = to_cublas_op(transb);
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::cublasZgemm(
|
||||
handle,
|
||||
opa,
|
||||
opb,
|
||||
static_cast<int>(m),
|
||||
static_cast<int>(n),
|
||||
static_cast<int>(k),
|
||||
reinterpret_cast<const cuDoubleComplex *>(&alpha),
|
||||
reinterpret_cast<const cuDoubleComplex *>(a),
|
||||
static_cast<int>(lda),
|
||||
reinterpret_cast<const cuDoubleComplex *>(b),
|
||||
static_cast<int>(ldb),
|
||||
reinterpret_cast<const cuDoubleComplex *>(&beta),
|
||||
reinterpret_cast<cuDoubleComplex *>(c),
|
||||
static_cast<int>(ldc)));
|
||||
}
|
||||
|
||||
/* ───────────── gemm<c10::complex<float>> ───────────── */
|
||||
template <>
|
||||
void gemm<c10::complex<float>>(CUDABLAS_GEMM_ARGTYPES(c10::complex<float>)) {
|
||||
cublasHandle_t handle = at::cuda::getCurrentCUDABlasHandle();
|
||||
cublasOperation_t opa = to_cublas_op(transa);
|
||||
cublasOperation_t opb = to_cublas_op(transb);
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::cublasCgemm(
|
||||
handle,
|
||||
opa,
|
||||
opb,
|
||||
static_cast<int>(m),
|
||||
static_cast<int>(n),
|
||||
static_cast<int>(k),
|
||||
reinterpret_cast<const cuFloatComplex *>(&alpha),
|
||||
reinterpret_cast<const cuFloatComplex *>(a),
|
||||
static_cast<int>(lda),
|
||||
reinterpret_cast<const cuFloatComplex *>(b),
|
||||
static_cast<int>(ldb),
|
||||
reinterpret_cast<const cuFloatComplex *>(&beta),
|
||||
reinterpret_cast<cuFloatComplex *>(c),
|
||||
static_cast<int>(ldc)));
|
||||
}
|
||||
|
||||
/* ───────────── gemm<at::Half> ───────────── */
|
||||
template <>
|
||||
void gemm<at::Half>(CUDABLAS_GEMM_ARGTYPES(at::Half)) {
|
||||
cublasHandle_t handle = at::cuda::getCurrentCUDABlasHandle();
|
||||
cublasOperation_t opa = to_cublas_op(transa);
|
||||
cublasOperation_t opb = to_cublas_op(transb);
|
||||
|
||||
// Use cublasGemmEx with FP32 compute for Half inputs
|
||||
float alpha_f = alpha;
|
||||
float beta_f = beta;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cublasGemmEx(handle,
|
||||
opa,
|
||||
opb,
|
||||
static_cast<int>(m),
|
||||
static_cast<int>(n),
|
||||
static_cast<int>(k),
|
||||
&alpha_f,
|
||||
a,
|
||||
CUDA_R_16F,
|
||||
static_cast<int>(lda),
|
||||
b,
|
||||
CUDA_R_16F,
|
||||
static_cast<int>(ldb),
|
||||
&beta_f,
|
||||
c,
|
||||
CUDA_R_16F,
|
||||
static_cast<int>(ldc),
|
||||
CUDA_R_32F,
|
||||
CUBLAS_GEMM_DEFAULT_TENSOR_OP));
|
||||
}
|
||||
|
||||
/* ───────────── gemm<at::BFloat16> ───────────── */
|
||||
template <>
|
||||
void gemm<at::BFloat16>(CUDABLAS_GEMM_ARGTYPES(at::BFloat16)) {
|
||||
cublasHandle_t handle = at::cuda::getCurrentCUDABlasHandle();
|
||||
cublasOperation_t opa = to_cublas_op(transa);
|
||||
cublasOperation_t opb = to_cublas_op(transb);
|
||||
|
||||
// Use cublasGemmEx with FP32 compute for BFloat16 inputs
|
||||
float alpha_f = alpha;
|
||||
float beta_f = beta;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::cublasGemmEx(handle,
|
||||
opa,
|
||||
opb,
|
||||
static_cast<int>(m),
|
||||
static_cast<int>(n),
|
||||
static_cast<int>(k),
|
||||
&alpha_f,
|
||||
a,
|
||||
CUDA_R_16BF,
|
||||
static_cast<int>(lda),
|
||||
b,
|
||||
CUDA_R_16BF,
|
||||
static_cast<int>(ldb),
|
||||
&beta_f,
|
||||
c,
|
||||
CUDA_R_16BF,
|
||||
static_cast<int>(ldc),
|
||||
CUDA_R_32F,
|
||||
CUBLAS_GEMM_DEFAULT_TENSOR_OP));
|
||||
}
|
||||
|
||||
} // namespace at::cuda::blas
|
||||
|
||||
#endif // PADDLE_WITH_CUDA
|
||||
@@ -0,0 +1,73 @@
|
||||
// Copyright (c) 2026 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.
|
||||
|
||||
// The file has been adapted from pytorch project
|
||||
// Licensed under BSD-style license -
|
||||
// https://github.com/pytorch/pytorch/blob/main/LICENSE
|
||||
|
||||
#pragma once
|
||||
/*
|
||||
Provides a subset of CUDA BLAS functions as templates:
|
||||
|
||||
gemm<Dtype>(transa, transb, m, n, k, alpha, a, lda, b, ldb, beta, c,
|
||||
ldc)
|
||||
|
||||
where Dtype is double, float, c10::complex<double>, c10::complex<float>,
|
||||
at::Half or at::BFloat16. The functions are available in at::cuda::blas
|
||||
namespace.
|
||||
*/
|
||||
|
||||
#include <ATen/OpMathType.h>
|
||||
#include <ATen/cuda/CUDAContext.h>
|
||||
|
||||
#include "paddle/common/macros.h"
|
||||
|
||||
namespace at::cuda::blas {
|
||||
|
||||
/* LEVEL 3 BLAS FUNCTIONS */
|
||||
|
||||
#define CUDABLAS_GEMM_ARGTYPES(Dtype) \
|
||||
CUDABLAS_GEMM_ARGTYPES_AND_C_DTYPE(Dtype, Dtype)
|
||||
|
||||
#define CUDABLAS_GEMM_ARGTYPES_AND_C_DTYPE(Dtype, C_Dtype) \
|
||||
char transa, char transb, int64_t m, int64_t n, int64_t k, \
|
||||
at::opmath_type<Dtype> alpha, const Dtype *a, int64_t lda, \
|
||||
const Dtype *b, int64_t ldb, at::opmath_type<Dtype> beta, C_Dtype *c, \
|
||||
int64_t ldc
|
||||
|
||||
#define CUDABLAS_GEMM_ARGS(Dtype) \
|
||||
transa, transb, m, n, k, alpha, a, lda, b, ldb, beta, c, ldc
|
||||
|
||||
template <typename Dtype, typename C_Dtype = Dtype>
|
||||
inline void gemm(CUDABLAS_GEMM_ARGTYPES_AND_C_DTYPE(Dtype, C_Dtype)) {
|
||||
static_assert(false && sizeof(Dtype),
|
||||
"at::cuda::blas::gemm: not implemented");
|
||||
}
|
||||
|
||||
template <>
|
||||
PADDLE_API void gemm<double>(CUDABLAS_GEMM_ARGTYPES(double));
|
||||
template <>
|
||||
PADDLE_API void gemm<float>(CUDABLAS_GEMM_ARGTYPES(float));
|
||||
template <>
|
||||
PADDLE_API void gemm<c10::complex<double>>(
|
||||
CUDABLAS_GEMM_ARGTYPES(c10::complex<double>));
|
||||
template <>
|
||||
PADDLE_API void gemm<c10::complex<float>>(
|
||||
CUDABLAS_GEMM_ARGTYPES(c10::complex<float>));
|
||||
template <>
|
||||
PADDLE_API void gemm<at::Half>(CUDABLAS_GEMM_ARGTYPES(at::Half));
|
||||
template <>
|
||||
PADDLE_API void gemm<at::BFloat16>(CUDABLAS_GEMM_ARGTYPES(at::BFloat16));
|
||||
|
||||
} // namespace at::cuda::blas
|
||||
@@ -0,0 +1,214 @@
|
||||
// Copyright (c) 2026 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.
|
||||
|
||||
// The file has been adapted from pytorch project
|
||||
// Licensed under BSD-style license -
|
||||
// https://github.com/pytorch/pytorch/blob/main/LICENSE
|
||||
|
||||
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
|
||||
|
||||
#include <ATen/cuda/CUDAContext.h>
|
||||
|
||||
#include <c10/core/Allocator.h>
|
||||
#include <mutex>
|
||||
|
||||
#include "paddle/phi/backends/context_pool.h"
|
||||
#include "paddle/phi/backends/gpu/gpu_context.h"
|
||||
#include "paddle/phi/backends/gpu/gpu_info.h"
|
||||
#include "paddle/phi/core/memory/allocation/allocator_facade.h"
|
||||
|
||||
namespace at::cuda {
|
||||
|
||||
namespace {
|
||||
|
||||
inline void ensureDeviceContextPoolInitialized() {
|
||||
static std::once_flag init_pool_once;
|
||||
std::call_once(init_pool_once, []() {
|
||||
if (phi::DeviceContextPool::IsInitialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<phi::Place> places;
|
||||
int gpu_count = phi::backends::gpu::GetGPUDeviceCount();
|
||||
for (int device = 0; device < gpu_count; ++device) {
|
||||
places.emplace_back(phi::GPUPlace(device));
|
||||
}
|
||||
places.emplace_back(phi::CPUPlace());
|
||||
places.emplace_back(phi::GPUPinnedPlace());
|
||||
phi::DeviceContextPool::Init(places);
|
||||
});
|
||||
}
|
||||
|
||||
/// Returns the GPUContext for the current device.
|
||||
inline phi::GPUContext* getCurrentGPUContext() {
|
||||
ensureDeviceContextPoolInitialized();
|
||||
int device_id = phi::backends::gpu::GetCurrentDeviceId();
|
||||
return static_cast<phi::GPUContext*>(
|
||||
phi::DeviceContextPool::Instance().Get(phi::GPUPlace(device_id)));
|
||||
}
|
||||
|
||||
/// Frees a phi::Allocation that was released with .release() during allocate().
|
||||
static void deletePaddleCUDAAllocation(void* p) {
|
||||
delete static_cast<phi::Allocation*>(p);
|
||||
}
|
||||
|
||||
/// Adapter class that wraps Paddle's AllocatorFacade as a c10::Allocator.
|
||||
/// This provides a bridge between Paddle's allocation interface and PyTorch's
|
||||
/// c10::Allocator interface for the CUDA compatibility layer.
|
||||
class PaddleCUDAAllocatorAdapter : public c10::Allocator {
|
||||
public:
|
||||
c10::DataPtr allocate(size_t n) override {
|
||||
int device_id = phi::backends::gpu::GetCurrentDeviceId();
|
||||
if (n == 0) {
|
||||
// Return a DataPtr that carries the current CUDA device without
|
||||
// allocating any memory. Callers that probe device identity via
|
||||
// DataPtr::device() (e.g. zero-byte tensor construction) will therefore
|
||||
// observe the correct CUDA device rather than a default CPU device.
|
||||
// NOTE: For HIP/ROCm builds, PyTorch's compatibility layer still
|
||||
// exposes DeviceType::CUDA (kCUDA) rather than a separate HIP device
|
||||
// type, so we follow the same convention here.
|
||||
return c10::DataPtr(nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
c10::Device(c10::DeviceType::CUDA, device_id));
|
||||
}
|
||||
auto* alloc = paddle::memory::allocation::AllocatorFacade::Instance()
|
||||
.GetAllocator(phi::GPUPlace(device_id))
|
||||
.get();
|
||||
auto phi_alloc = alloc->Allocate(n);
|
||||
void* ptr = phi_alloc->ptr();
|
||||
phi::Place place = phi_alloc->place();
|
||||
// Transfer ownership of phi_alloc to the DataPtr's context.
|
||||
auto* raw_alloc = phi_alloc.release();
|
||||
return c10::DataPtr(
|
||||
ptr, raw_alloc, deletePaddleCUDAAllocation, c10::Device(place));
|
||||
}
|
||||
|
||||
void copy_data(void* dst, const void* src, size_t n) const override {
|
||||
if (n == 0) return;
|
||||
// Use GPU device-to-device copy. std::memcpy is not valid for device
|
||||
// memory; callers such as c10::Allocator::clone() rely on this method to
|
||||
// perform correct D2D copies on CUDA/HIP memory.
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(hipMemcpy(dst, src, n, hipMemcpyDeviceToDevice));
|
||||
#else
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
cudaMemcpy(dst, src, n, cudaMemcpyDeviceToDevice));
|
||||
#endif
|
||||
}
|
||||
|
||||
c10::DeleterFnPtr raw_deleter() const override {
|
||||
// allocate() returns data=device_ptr, context=phi::Allocation*, so
|
||||
// get() != get_context() and the raw_allocate/raw_deallocate API is
|
||||
// unsafe for this allocator. Returning nullptr signals that.
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
CUDAContextDeviceProp* getCurrentDeviceProperties() {
|
||||
int device = phi::backends::gpu::GetCurrentDeviceId();
|
||||
return getDeviceProperties(device);
|
||||
}
|
||||
|
||||
int warp_size() { return getCurrentDeviceProperties()->warpSize; }
|
||||
|
||||
CUDAContextDeviceProp* getDeviceProperties(c10::DeviceIndex device) {
|
||||
return const_cast<CUDAContextDeviceProp*>(
|
||||
&phi::backends::gpu::GetDeviceProperties(device));
|
||||
}
|
||||
|
||||
bool canDeviceAccessPeer(c10::DeviceIndex device,
|
||||
c10::DeviceIndex peer_device) {
|
||||
int can_access = 0;
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
hipDeviceCanAccessPeer(&can_access, device, peer_device);
|
||||
#else
|
||||
cudaDeviceCanAccessPeer(&can_access, device, peer_device);
|
||||
#endif
|
||||
return can_access != 0;
|
||||
}
|
||||
|
||||
/* Handles */
|
||||
|
||||
CUDAContextSparseHandle getCurrentCUDASparseHandle() {
|
||||
return getCurrentGPUContext()->cusparse_handle();
|
||||
}
|
||||
|
||||
CUDAContextBlasHandle getCurrentCUDABlasHandle() {
|
||||
return getCurrentGPUContext()->cublas_handle();
|
||||
}
|
||||
|
||||
CUDAContextBlasLtHandle getCurrentCUDABlasLtHandle() {
|
||||
return getCurrentGPUContext()->cublaslt_handle();
|
||||
}
|
||||
|
||||
void clearCublasWorkspaces() {
|
||||
// Workspaces are owned and managed by phi::GPUContext; no explicit
|
||||
// cleanup is required here.
|
||||
}
|
||||
|
||||
WorkspaceMapWithMutex& cublas_handle_stream_to_workspace() {
|
||||
static WorkspaceMapWithMutex workspace_map;
|
||||
return workspace_map;
|
||||
}
|
||||
|
||||
WorkspaceMapWithMutex& cublaslt_handle_stream_to_workspace() {
|
||||
static WorkspaceMapWithMutex workspace_map;
|
||||
return workspace_map;
|
||||
}
|
||||
|
||||
// Default workspace size consistent with PyTorch's chosen default (32 MiB).
|
||||
static constexpr size_t kDefaultWorkspaceSize = 32UL * 1024UL * 1024UL;
|
||||
|
||||
size_t getChosenWorkspaceSize() { return kDefaultWorkspaceSize; }
|
||||
|
||||
size_t getCUDABlasLtWorkspaceSize() {
|
||||
// Probe the context with the default size and return what was actually
|
||||
// allocated.
|
||||
auto [ptr, size] =
|
||||
getCurrentGPUContext()->cublaslt_workspace(kDefaultWorkspaceSize);
|
||||
(void)ptr;
|
||||
return size;
|
||||
}
|
||||
|
||||
void* getCUDABlasLtWorkspace() {
|
||||
return getCurrentGPUContext()
|
||||
->cublaslt_workspace(kDefaultWorkspaceSize)
|
||||
.first;
|
||||
}
|
||||
|
||||
CUDAContextSolverHandle getCurrentCUDASolverDnHandle() {
|
||||
return getCurrentGPUContext()->cusolver_dn_handle();
|
||||
}
|
||||
|
||||
#if defined(USE_CUDSS)
|
||||
cudssHandle_t getCurrentCudssHandle() {
|
||||
// cudss is not yet integrated into phi::GPUContext; not implemented.
|
||||
PADDLE_THROW(
|
||||
common::errors::Unimplemented("getCurrentCudssHandle() is not "
|
||||
"implemented in the Paddle compat layer."));
|
||||
return nullptr;
|
||||
}
|
||||
#endif // USE_CUDSS
|
||||
|
||||
c10::Allocator* getCUDADeviceAllocator() {
|
||||
static PaddleCUDAAllocatorAdapter adapter;
|
||||
return &adapter;
|
||||
}
|
||||
|
||||
} // namespace at::cuda
|
||||
|
||||
#endif // PADDLE_WITH_CUDA || PADDLE_WITH_HIP
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) 2025 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.
|
||||
|
||||
// The file has been adapted from pytorch project
|
||||
// Licensed under BSD-style license -
|
||||
// https://github.com/pytorch/pytorch/blob/main/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ATen/cuda/CUDAContextLight.h>
|
||||
|
||||
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
|
||||
#include <ATen/cuda/Exceptions.h>
|
||||
#include <c10/cuda/CUDAStream.h>
|
||||
#endif
|
||||
@@ -0,0 +1,136 @@
|
||||
// Copyright (c) 2026 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.
|
||||
|
||||
// The file has been adapted from pytorch project
|
||||
// Licensed under BSD-style license -
|
||||
// https://github.com/pytorch/pytorch/blob/main/LICENSE
|
||||
|
||||
#pragma once
|
||||
// Light-weight version of CUDAContext.h with fewer transitive includes
|
||||
|
||||
// cublasLT was introduced in CUDA 10.1 but we enable only for 11.1 that also
|
||||
// added bf16 support
|
||||
|
||||
#if defined(PADDLE_WITH_HIP)
|
||||
#include <hip/hip_runtime.h>
|
||||
#elif defined(PADDLE_WITH_CUDA)
|
||||
#if defined(USE_CUDSS)
|
||||
#include <cudss.h>
|
||||
#endif
|
||||
#include <driver_types.h>
|
||||
#endif
|
||||
|
||||
#include <c10/core/Allocator.h>
|
||||
#include <c10/cuda/CUDAFunctions.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <shared_mutex>
|
||||
#include <tuple>
|
||||
|
||||
#include "paddle/common/macros.h"
|
||||
#include "paddle/phi/backends/gpu/forwards.h"
|
||||
|
||||
namespace c10 {
|
||||
struct Allocator;
|
||||
}
|
||||
|
||||
namespace at::cuda {
|
||||
|
||||
#if defined(PADDLE_WITH_HIP)
|
||||
using CUDAContextDeviceProp = phi::gpuDeviceProp;
|
||||
using CUDAContextSparseHandle = phi::sparseHandle_t;
|
||||
using CUDAContextBlasHandle = phi::blasHandle_t;
|
||||
using CUDAContextBlasLtHandle = phi::blasLtHandle_t;
|
||||
using CUDAContextSolverHandle = phi::solverHandle_t;
|
||||
#elif defined(PADDLE_WITH_CUDA)
|
||||
using CUDAContextDeviceProp = cudaDeviceProp;
|
||||
using CUDAContextSparseHandle = cusparseHandle_t;
|
||||
using CUDAContextBlasHandle = cublasHandle_t;
|
||||
using CUDAContextBlasLtHandle = cublasLtHandle_t;
|
||||
using CUDAContextSolverHandle = cusolverDnHandle_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
A common CUDA interface for ATen.
|
||||
|
||||
This interface is distinct from CUDAHooks, which defines an interface that links
|
||||
to both CPU-only and CUDA builds. That interface is intended for runtime
|
||||
dispatch and should be used from files that are included in both CPU-only and
|
||||
CUDA builds.
|
||||
|
||||
CUDAContext, on the other hand, should be preferred by files only included in
|
||||
CUDA builds. It is intended to expose CUDA functionality in a consistent
|
||||
manner.
|
||||
|
||||
This means there is some overlap between the CUDAContext and CUDAHooks, but
|
||||
the choice of which to use is simple: use CUDAContext when in a CUDA-only file,
|
||||
use CUDAHooks otherwise.
|
||||
|
||||
Note that CUDAContext simply defines an interface with no associated class.
|
||||
It is expected that the modules whose functions compose this interface will
|
||||
manage their own state. There is only a single CUDA context/state.
|
||||
*/
|
||||
|
||||
/**
|
||||
* DEPRECATED: use device_count() instead
|
||||
*/
|
||||
inline int64_t getNumGPUs() { return c10::cuda::device_count(); }
|
||||
|
||||
/**
|
||||
* CUDA is available if we compiled with CUDA, and there are one or more
|
||||
* devices. If we compiled with CUDA but there is a driver problem, etc.,
|
||||
* this function will report CUDA is not available (rather than raise an error.)
|
||||
*/
|
||||
inline bool is_available() { return c10::cuda::device_count() > 0; }
|
||||
|
||||
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
|
||||
PADDLE_API CUDAContextDeviceProp* getCurrentDeviceProperties();
|
||||
|
||||
PADDLE_API int warp_size();
|
||||
|
||||
PADDLE_API CUDAContextDeviceProp* getDeviceProperties(c10::DeviceIndex device);
|
||||
|
||||
PADDLE_API bool canDeviceAccessPeer(c10::DeviceIndex device,
|
||||
c10::DeviceIndex peer_device);
|
||||
|
||||
/* Handles */
|
||||
PADDLE_API CUDAContextSparseHandle getCurrentCUDASparseHandle();
|
||||
PADDLE_API CUDAContextBlasHandle getCurrentCUDABlasHandle();
|
||||
PADDLE_API CUDAContextBlasLtHandle getCurrentCUDABlasLtHandle();
|
||||
|
||||
PADDLE_API void clearCublasWorkspaces();
|
||||
struct WorkspaceMapWithMutex {
|
||||
std::map<std::tuple<void*, void*>, at::DataPtr> map;
|
||||
std::shared_mutex mutex;
|
||||
};
|
||||
|
||||
PADDLE_API WorkspaceMapWithMutex& cublas_handle_stream_to_workspace();
|
||||
PADDLE_API WorkspaceMapWithMutex& cublaslt_handle_stream_to_workspace();
|
||||
PADDLE_API size_t getChosenWorkspaceSize();
|
||||
PADDLE_API size_t getCUDABlasLtWorkspaceSize();
|
||||
PADDLE_API void* getCUDABlasLtWorkspace();
|
||||
|
||||
PADDLE_API CUDAContextSolverHandle getCurrentCUDASolverDnHandle();
|
||||
|
||||
#if defined(USE_CUDSS)
|
||||
PADDLE_API cudssHandle_t getCurrentCudssHandle();
|
||||
#endif
|
||||
|
||||
// Get the CUDA device allocator for the current device.
|
||||
// Returns a pointer to a c10::Allocator that allocates GPU memory.
|
||||
PADDLE_API c10::Allocator* getCUDADeviceAllocator();
|
||||
#endif
|
||||
|
||||
} // namespace at::cuda
|
||||
@@ -0,0 +1,164 @@
|
||||
// Copyright (c) 2025 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.
|
||||
|
||||
// The file has been adapted from pytorch project
|
||||
// Licensed under BSD-style license -
|
||||
// https://github.com/pytorch/pytorch/blob/main/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <c10/core/ScalarType.h>
|
||||
|
||||
#if defined(PADDLE_WITH_HIP)
|
||||
#include <hip/hip_runtime.h>
|
||||
#include <hip/library_types.h>
|
||||
#elif defined(PADDLE_WITH_CUDA)
|
||||
#include <cuda.h>
|
||||
#include <library_types.h>
|
||||
#endif
|
||||
|
||||
namespace at::cuda {
|
||||
|
||||
#if defined(PADDLE_WITH_HIP)
|
||||
using cudaDataType = hipDataType;
|
||||
#define CUDA_R_16F HIP_R_16F
|
||||
#define CUDA_R_32F HIP_R_32F
|
||||
#define CUDA_R_64F HIP_R_64F
|
||||
#define CUDA_C_16F HIP_C_16F
|
||||
#define CUDA_C_32F HIP_C_32F
|
||||
#define CUDA_C_64F HIP_C_64F
|
||||
#define CUDA_R_8U HIP_R_8U
|
||||
#define CUDA_R_8I HIP_R_8I
|
||||
#define CUDA_R_32I HIP_R_32I
|
||||
#define CUDA_R_16I HIP_R_16I
|
||||
#define CUDA_R_64I HIP_R_64I
|
||||
#define CUDA_R_16BF HIP_R_16BF
|
||||
#define CUDA_R_8F_E4M3 HIP_R_8F_E4M3
|
||||
#define CUDA_R_8F_E5M2 HIP_R_8F_E5M2
|
||||
#elif defined(PADDLE_WITH_CUDA)
|
||||
using cudaDataType = cudaDataType;
|
||||
#endif
|
||||
|
||||
template <typename scalar_t>
|
||||
cudaDataType getCudaDataType() {
|
||||
static_assert(false && sizeof(scalar_t),
|
||||
"Cannot convert type to cudaDataType.");
|
||||
return {};
|
||||
}
|
||||
|
||||
template <>
|
||||
inline cudaDataType getCudaDataType<at::Half>() {
|
||||
return CUDA_R_16F;
|
||||
}
|
||||
template <>
|
||||
inline cudaDataType getCudaDataType<float>() {
|
||||
return CUDA_R_32F;
|
||||
}
|
||||
template <>
|
||||
inline cudaDataType getCudaDataType<double>() {
|
||||
return CUDA_R_64F;
|
||||
}
|
||||
template <>
|
||||
inline cudaDataType getCudaDataType<c10::complex<c10::Half>>() {
|
||||
return CUDA_C_16F;
|
||||
}
|
||||
template <>
|
||||
inline cudaDataType getCudaDataType<c10::complex<float>>() {
|
||||
return CUDA_C_32F;
|
||||
}
|
||||
template <>
|
||||
inline cudaDataType getCudaDataType<c10::complex<double>>() {
|
||||
return CUDA_C_64F;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline cudaDataType getCudaDataType<uint8_t>() {
|
||||
return CUDA_R_8U;
|
||||
}
|
||||
template <>
|
||||
inline cudaDataType getCudaDataType<int8_t>() {
|
||||
return CUDA_R_8I;
|
||||
}
|
||||
template <>
|
||||
inline cudaDataType getCudaDataType<int>() {
|
||||
return CUDA_R_32I;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline cudaDataType getCudaDataType<int16_t>() {
|
||||
return CUDA_R_16I;
|
||||
}
|
||||
template <>
|
||||
inline cudaDataType getCudaDataType<int64_t>() {
|
||||
return CUDA_R_64I;
|
||||
}
|
||||
template <>
|
||||
inline cudaDataType getCudaDataType<at::BFloat16>() {
|
||||
return CUDA_R_16BF;
|
||||
}
|
||||
|
||||
inline cudaDataType ScalarTypeToCudaDataType(
|
||||
const c10::ScalarType& scalar_type) {
|
||||
switch (scalar_type) {
|
||||
case c10::ScalarType::Byte:
|
||||
return CUDA_R_8U;
|
||||
case c10::ScalarType::Char:
|
||||
return CUDA_R_8I;
|
||||
case c10::ScalarType::Int:
|
||||
return CUDA_R_32I;
|
||||
case c10::ScalarType::Half:
|
||||
return CUDA_R_16F;
|
||||
case c10::ScalarType::Float:
|
||||
return CUDA_R_32F;
|
||||
case c10::ScalarType::Double:
|
||||
return CUDA_R_64F;
|
||||
// case c10::ScalarType::ComplexHalf:
|
||||
// return CUDA_C_16F;
|
||||
case c10::ScalarType::ComplexFloat:
|
||||
return CUDA_C_32F;
|
||||
case c10::ScalarType::ComplexDouble:
|
||||
return CUDA_C_64F;
|
||||
case c10::ScalarType::Short:
|
||||
return CUDA_R_16I;
|
||||
case c10::ScalarType::Long:
|
||||
return CUDA_R_64I;
|
||||
case c10::ScalarType::BFloat16:
|
||||
return CUDA_R_16BF;
|
||||
#if defined(PADDLE_WITH_HIP)
|
||||
case c10::ScalarType::Float8_e4m3fn:
|
||||
return CUDA_R_8F_E4M3;
|
||||
case c10::ScalarType::Float8_e5m2:
|
||||
return CUDA_R_8F_E5M2;
|
||||
case c10::ScalarType::Float8_e4m3fnuz:
|
||||
return HIP_R_8F_E4M3_FNUZ;
|
||||
case c10::ScalarType::Float8_e5m2fnuz:
|
||||
return HIP_R_8F_E5M2_FNUZ;
|
||||
#elif !defined(USE_ROCM) || ROCM_VERSION >= 60300
|
||||
case c10::ScalarType::Float8_e4m3fn:
|
||||
return CUDA_R_8F_E4M3;
|
||||
case c10::ScalarType::Float8_e5m2:
|
||||
return CUDA_R_8F_E5M2;
|
||||
#endif
|
||||
// #if (defined(CUDA_VERSION) && CUDA_VERSION >= 12080) ||
|
||||
// (defined(USE_ROCM) && ROCM_VERSION >= 70000)
|
||||
// case c10::ScalarType::Float4_e2m1fn_x2:
|
||||
// return CUDA_R_4F_E2M1;
|
||||
// #endif
|
||||
default:
|
||||
TORCH_INTERNAL_ASSERT(
|
||||
false, "Cannot convert ScalarType ", scalar_type, " to cudaDataType.")
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace at::cuda
|
||||
@@ -0,0 +1,204 @@
|
||||
// Copyright (c) 2026 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.
|
||||
|
||||
// The file has been adapted from pytorch project
|
||||
// Licensed under BSD-style license -
|
||||
// https://github.com/pytorch/pytorch/blob/main/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(PADDLE_WITH_HIP)
|
||||
#include <hip/hip_runtime.h>
|
||||
#elif defined(PADDLE_WITH_CUDA)
|
||||
#include <cuda_runtime_api.h>
|
||||
#endif
|
||||
|
||||
#include <c10/core/Device.h>
|
||||
#include <c10/cuda/CUDAException.h>
|
||||
#include <c10/cuda/CUDAGuard.h>
|
||||
#include <c10/cuda/CUDAStream.h>
|
||||
#include <c10/util/Exception.h>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
namespace at::cuda {
|
||||
|
||||
/**
|
||||
* CUDAEvent is a movable, non-copyable wrapper around CUDA events.
|
||||
* Provides compatibility with PyTorch's CUDAEvent API.
|
||||
*/
|
||||
struct CUDAEvent {
|
||||
CUDAEvent() noexcept = default;
|
||||
|
||||
explicit CUDAEvent(unsigned int flags) noexcept : flags_(flags) {}
|
||||
|
||||
~CUDAEvent() {
|
||||
if (is_created_) {
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
hipEventDestroy(event_);
|
||||
#else
|
||||
cudaEventDestroy(event_);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
CUDAEvent(const CUDAEvent&) = delete;
|
||||
CUDAEvent& operator=(const CUDAEvent&) = delete;
|
||||
|
||||
CUDAEvent(CUDAEvent&& other) noexcept { moveHelper(std::move(other)); }
|
||||
CUDAEvent& operator=(CUDAEvent&& other) noexcept {
|
||||
if (this != &other) {
|
||||
moveHelper(std::move(other));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
operator hipEvent_t() const { return event(); }
|
||||
|
||||
hipEvent_t event() const { return event_; }
|
||||
#else
|
||||
operator cudaEvent_t() const { return event(); }
|
||||
|
||||
cudaEvent_t event() const { return event_; }
|
||||
#endif
|
||||
|
||||
bool isCreated() const { return is_created_; }
|
||||
|
||||
c10::DeviceIndex device_index() const { return device_index_; }
|
||||
|
||||
bool query() const {
|
||||
if (!is_created_) return true;
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
hipError_t err = hipEventQuery(event_);
|
||||
if (err == hipSuccess) return true;
|
||||
if (err != hipErrorNotReady) C10_CUDA_CHECK(err);
|
||||
#else
|
||||
cudaError_t err = cudaEventQuery(event_);
|
||||
if (err == cudaSuccess) return true;
|
||||
if (err != cudaErrorNotReady) C10_CUDA_CHECK(err);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
void record() { record(getCurrentCUDAStream()); }
|
||||
|
||||
void record(const CUDAStream& stream) {
|
||||
if (!is_created_) {
|
||||
createEvent(stream.unwrap().device_index());
|
||||
}
|
||||
TORCH_CHECK(device_index_ == stream.unwrap().device_index(),
|
||||
"Event device ",
|
||||
device_index_,
|
||||
" does not match recording stream's device ",
|
||||
stream.unwrap().device_index(),
|
||||
".");
|
||||
c10::cuda::CUDAGuard guard(device_index_);
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
C10_CUDA_CHECK(hipEventRecord(event_, stream.stream()));
|
||||
#else
|
||||
C10_CUDA_CHECK(cudaEventRecord(event_, stream.stream()));
|
||||
#endif
|
||||
}
|
||||
|
||||
void recordOnce(const CUDAStream& stream) {
|
||||
if (!was_recorded_) {
|
||||
record(stream);
|
||||
was_recorded_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void block(const CUDAStream& stream) {
|
||||
if (is_created_) {
|
||||
c10::cuda::CUDAGuard guard(stream.unwrap().device_index());
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
C10_CUDA_CHECK(hipStreamWaitEvent(stream.stream(), event_, 0));
|
||||
#else
|
||||
C10_CUDA_CHECK(cudaStreamWaitEvent(stream.stream(), event_, 0));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void synchronize() const {
|
||||
if (is_created_) {
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
C10_CUDA_CHECK(hipEventSynchronize(event_));
|
||||
#else
|
||||
C10_CUDA_CHECK(cudaEventSynchronize(event_));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
float elapsed_time(const CUDAEvent& other) const {
|
||||
TORCH_CHECK(
|
||||
is_created_ && other.isCreated(),
|
||||
"Both events must be recorded before calculating elapsed time.");
|
||||
TORCH_CHECK(
|
||||
query() && other.query(),
|
||||
"Both events must be completed before calculating elapsed time.");
|
||||
float time_ms = 0;
|
||||
c10::cuda::CUDAGuard guard(device_index_);
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
C10_CUDA_CHECK(hipEventElapsedTime(&time_ms, event_, other.event_));
|
||||
#else
|
||||
C10_CUDA_CHECK(cudaEventElapsedTime(&time_ms, event_, other.event_));
|
||||
#endif
|
||||
return time_ms;
|
||||
}
|
||||
|
||||
private:
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
unsigned int flags_ = hipEventDisableTiming;
|
||||
#else
|
||||
unsigned int flags_ = cudaEventDisableTiming;
|
||||
#endif
|
||||
bool is_created_ = false;
|
||||
bool was_recorded_ = false;
|
||||
c10::DeviceIndex device_index_ = -1;
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
hipEvent_t event_{};
|
||||
#else
|
||||
cudaEvent_t event_{};
|
||||
#endif
|
||||
|
||||
void createEvent(c10::DeviceIndex device_index) {
|
||||
device_index_ = device_index;
|
||||
c10::cuda::CUDAGuard guard(device_index_);
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
C10_CUDA_CHECK(hipEventCreateWithFlags(&event_, flags_));
|
||||
#else
|
||||
C10_CUDA_CHECK(cudaEventCreateWithFlags(&event_, flags_));
|
||||
#endif
|
||||
is_created_ = true;
|
||||
}
|
||||
|
||||
void moveHelper(CUDAEvent&& other) {
|
||||
flags_ = other.flags_;
|
||||
is_created_ = std::exchange(other.is_created_, false);
|
||||
was_recorded_ = other.was_recorded_;
|
||||
device_index_ = other.device_index_;
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
event_ = std::exchange(other.event_, hipEvent_t{});
|
||||
#else
|
||||
event_ = std::exchange(other.event_, cudaEvent_t{});
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace at::cuda
|
||||
|
||||
namespace torch {
|
||||
using at::cuda::CUDAEvent;
|
||||
using at::cuda::CUDAStream;
|
||||
} // namespace torch
|
||||
@@ -0,0 +1,156 @@
|
||||
// Copyright (c) 2026 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.
|
||||
|
||||
// The file has been adapted from pytorch project
|
||||
// Licensed under BSD-style license -
|
||||
// https://github.com/pytorch/pytorch/blob/main/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ATen/core/Generator.h>
|
||||
#include <ATen/cuda/PhiloxCudaState.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "paddle/phi/backends/gpu/gpu_info.h"
|
||||
#include "paddle/phi/core/generator.h"
|
||||
|
||||
namespace at {
|
||||
|
||||
// Forward declaration
|
||||
struct CUDAGeneratorImpl;
|
||||
|
||||
inline DeviceIndex resolve_device_index(DeviceIndex idx) {
|
||||
if (idx < 0) {
|
||||
return static_cast<DeviceIndex>(phi::backends::gpu::GetCurrentDeviceId());
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
struct CUDAGeneratorImpl : public c10::GeneratorImpl {
|
||||
explicit CUDAGeneratorImpl(
|
||||
DeviceIndex device_index = -1, // NOLINT(runtime/int)
|
||||
bool use_default_gen = true)
|
||||
: c10::GeneratorImpl(
|
||||
c10::Device(c10::kCUDA, resolve_device_index(device_index)),
|
||||
use_default_gen
|
||||
? get_default_paddle_gen(resolve_device_index(device_index))
|
||||
: create_new_paddle_gen(resolve_device_index(device_index))),
|
||||
philox_offset_per_thread_(0) {}
|
||||
|
||||
~CUDAGeneratorImpl() override = default;
|
||||
|
||||
void set_current_seed(uint64_t seed) override {
|
||||
gen_->SetCurrentSeed(seed);
|
||||
philox_offset_per_thread_ = 0;
|
||||
}
|
||||
|
||||
uint64_t current_seed() const override { return gen_->GetCurrentSeed(); }
|
||||
|
||||
uint64_t seed() override {
|
||||
auto s = gen_->Seed();
|
||||
philox_offset_per_thread_ = 0;
|
||||
return s;
|
||||
}
|
||||
|
||||
void set_offset(uint64_t offset) override {
|
||||
philox_offset_per_thread_ = offset;
|
||||
}
|
||||
|
||||
uint64_t get_offset() const override { return philox_offset_per_thread_; }
|
||||
|
||||
void set_philox_offset_per_thread(uint64_t offset) {
|
||||
philox_offset_per_thread_ = offset;
|
||||
}
|
||||
|
||||
uint64_t philox_offset_per_thread() const {
|
||||
return philox_offset_per_thread_;
|
||||
}
|
||||
|
||||
PhiloxCudaState philox_cuda_state(uint64_t increment) {
|
||||
PhiloxCudaState state(gen_->GetCurrentSeed(), philox_offset_per_thread_);
|
||||
philox_offset_per_thread_ += increment;
|
||||
return state;
|
||||
}
|
||||
|
||||
std::pair<uint64_t, uint64_t> philox_engine_inputs(uint64_t increment) {
|
||||
uint64_t offset = philox_offset_per_thread_;
|
||||
philox_offset_per_thread_ += increment;
|
||||
return {gen_->GetCurrentSeed(), offset};
|
||||
}
|
||||
|
||||
c10::intrusive_ptr<c10::GeneratorImpl> clone() const override {
|
||||
auto new_gen = std::make_shared<phi::Generator>(gen_->GetCurrentSeed());
|
||||
auto state = gen_->GetState();
|
||||
new_gen->SetState(state);
|
||||
|
||||
auto impl = c10::make_intrusive<CUDAGeneratorImpl>(
|
||||
static_cast<DeviceIndex>(device_.index()));
|
||||
impl->gen_ = new_gen;
|
||||
impl->philox_offset_per_thread_ = philox_offset_per_thread_;
|
||||
return impl;
|
||||
}
|
||||
|
||||
static c10::DeviceType device_type() { return c10::kCUDA; }
|
||||
|
||||
private:
|
||||
uint64_t philox_offset_per_thread_;
|
||||
|
||||
static std::shared_ptr<phi::Generator> get_default_paddle_gen(
|
||||
DeviceIndex device_index) {
|
||||
return phi::DefaultCUDAGenerator(static_cast<int64_t>(device_index));
|
||||
}
|
||||
|
||||
static std::shared_ptr<phi::Generator> create_new_paddle_gen(
|
||||
DeviceIndex /*device_index*/) {
|
||||
return std::make_shared<phi::Generator>();
|
||||
}
|
||||
};
|
||||
|
||||
namespace cuda {
|
||||
namespace detail {
|
||||
|
||||
inline const Generator& getDefaultCUDAGenerator(DeviceIndex device_index = -1) {
|
||||
auto idx = resolve_device_index(device_index);
|
||||
static std::vector<Generator> generators;
|
||||
static std::once_flag init_flag;
|
||||
static int64_t num_devices = 0;
|
||||
|
||||
std::call_once(init_flag, []() {
|
||||
num_devices = phi::backends::gpu::GetGPUDeviceCount();
|
||||
generators.reserve(num_devices);
|
||||
for (int64_t i = 0; i < num_devices; ++i) {
|
||||
generators.emplace_back(c10::make_intrusive<CUDAGeneratorImpl>(
|
||||
static_cast<DeviceIndex>(i), /*use_default_gen=*/true));
|
||||
}
|
||||
});
|
||||
|
||||
TORCH_CHECK(idx < static_cast<DeviceIndex>(num_devices),
|
||||
"CUDA device index out of range: ",
|
||||
idx);
|
||||
return generators[static_cast<size_t>(idx)];
|
||||
}
|
||||
|
||||
inline Generator createCUDAGenerator(DeviceIndex device_index = -1) {
|
||||
return Generator(c10::make_intrusive<CUDAGeneratorImpl>(
|
||||
device_index, /*use_default_gen=*/false));
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace cuda
|
||||
} // namespace at
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright (c) 2025 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 <ATen/cuda/EmptyTensor.h>
|
||||
|
||||
#include "paddle/phi/api/include/api.h"
|
||||
#include "paddle/phi/common/place.h"
|
||||
|
||||
namespace at::detail {
|
||||
|
||||
at::Tensor empty_cuda(IntArrayRef size,
|
||||
ScalarType dtype,
|
||||
std::optional<Device> device_opt,
|
||||
std::optional<c10::MemoryFormat> memory_format_opt) {
|
||||
PD_CHECK(!(memory_format_opt.has_value() &&
|
||||
memory_format_opt.value() != c10::MemoryFormat::Contiguous),
|
||||
"`MemoryFormat` other than Contiguous is not supported now.");
|
||||
return paddle::experimental::empty(
|
||||
size._PD_ToPaddleIntArray(),
|
||||
compat::_PD_AtenScalarTypeToPhiDataType(dtype),
|
||||
device_opt && device_opt->has_index() ? phi::GPUPlace(device_opt->index())
|
||||
: paddle::DefaultGPUPlace());
|
||||
}
|
||||
|
||||
at::Tensor empty_cuda(IntArrayRef size, const TensorOptions &options) {
|
||||
auto place = options.has_device() && options.device().has_index()
|
||||
? phi::GPUPlace(options.device().index())
|
||||
: paddle::DefaultGPUPlace();
|
||||
return paddle::experimental::empty(
|
||||
size._PD_ToPaddleIntArray(),
|
||||
compat::_PD_AtenScalarTypeToPhiDataType(options.dtype_opt().value()),
|
||||
place);
|
||||
}
|
||||
|
||||
} // namespace at::detail
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) 2025 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 <ATen/core/TensorBody.h>
|
||||
|
||||
#include "paddle/common/macros.h"
|
||||
|
||||
namespace at::detail {
|
||||
|
||||
using at::Tensor;
|
||||
PADDLE_API at::Tensor empty_cuda(
|
||||
IntArrayRef size,
|
||||
ScalarType dtype,
|
||||
std::optional<Device> device_opt,
|
||||
std::optional<c10::MemoryFormat> memory_format_opt);
|
||||
|
||||
PADDLE_API at::Tensor empty_cuda(IntArrayRef size,
|
||||
const TensorOptions &options);
|
||||
|
||||
} // namespace at::detail
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) 2025 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 <c10/util/Exception.h>
|
||||
@@ -0,0 +1,56 @@
|
||||
// Copyright (c) 2026 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.
|
||||
|
||||
// The file has been adapted from pytorch project
|
||||
// Licensed under BSD-style license -
|
||||
// https://github.com/pytorch/pytorch/blob/main/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace at {
|
||||
|
||||
struct PhiloxCudaState {
|
||||
PhiloxCudaState() = default;
|
||||
// Called if graph capture is not underway
|
||||
PhiloxCudaState(uint64_t seed, uint64_t offset) {
|
||||
seed_.val = seed;
|
||||
offset_.val = offset;
|
||||
}
|
||||
// Called if graph capture is underway
|
||||
PhiloxCudaState(int64_t* seed,
|
||||
int64_t* offset_extragraph,
|
||||
uint64_t offset_intragraph) {
|
||||
seed_.ptr = seed;
|
||||
offset_.ptr = offset_extragraph;
|
||||
offset_intragraph_ = offset_intragraph;
|
||||
captured_ = true;
|
||||
}
|
||||
|
||||
// Public members, directly accessible by at::cuda::philox::unpack.
|
||||
// If we made them private with getters/setters, the getters/setters
|
||||
// would have to be __device__, and we can't declare __device__ in ATen.
|
||||
union Payload {
|
||||
uint64_t val;
|
||||
int64_t* ptr;
|
||||
};
|
||||
|
||||
Payload seed_{};
|
||||
Payload offset_{};
|
||||
uint64_t offset_intragraph_ = 0;
|
||||
bool captured_ = false;
|
||||
};
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright (c) 2026 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.
|
||||
|
||||
// The file has been adapted from pytorch project
|
||||
// Licensed under BSD-style license -
|
||||
// https://github.com/pytorch/pytorch/blob/main/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ATen/cuda/PhiloxCudaState.h>
|
||||
#include <tuple>
|
||||
|
||||
namespace at::cuda::philox {
|
||||
|
||||
// In-kernel call to retrieve philox seed and offset from a PhiloxCudaState
|
||||
// instance whether that instance was created with graph capture underway or
|
||||
// not. See Note [CUDA Graph-safe RNG states].
|
||||
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
|
||||
__host__ __device__ __forceinline__ std::tuple<uint64_t, uint64_t> unpack(
|
||||
at::PhiloxCudaState arg) {
|
||||
#else
|
||||
inline std::tuple<uint64_t, uint64_t> unpack(at::PhiloxCudaState arg) {
|
||||
#endif
|
||||
if (arg.captured_) {
|
||||
// static_cast avoids "warning: invalid narrowing conversion from "long" to
|
||||
// "unsigned long".
|
||||
// *(arg.offset_.ptr) is a broadcast load of a single int64_t to the entire
|
||||
// kernel. For most threads' reads it will hit in cache, so it shouldn't
|
||||
// hurt performance.
|
||||
return std::make_tuple(
|
||||
static_cast<uint64_t>(*arg.seed_.ptr),
|
||||
static_cast<uint64_t>(*(arg.offset_.ptr) + arg.offset_intragraph_));
|
||||
} else {
|
||||
return std::make_tuple(arg.seed_.val, arg.offset_.val);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace at::cuda::philox
|
||||
@@ -0,0 +1,75 @@
|
||||
// Copyright (c) 2026 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 <ATen/AccumulateType.h>
|
||||
#include <c10/core/Scalar.h>
|
||||
#include <limits>
|
||||
|
||||
namespace at::native {
|
||||
|
||||
inline void arange_check_bounds(const c10::Scalar& start,
|
||||
const c10::Scalar& end,
|
||||
const c10::Scalar& step) {
|
||||
// use double precision for validation to avoid precision issues
|
||||
double dstart = start.to<double>();
|
||||
double dend = end.to<double>();
|
||||
double dstep = step.to<double>();
|
||||
|
||||
TORCH_CHECK(dstep > 0 || dstep < 0, "step must be nonzero");
|
||||
TORCH_CHECK(std::isfinite(dstart) && std::isfinite(dend),
|
||||
"unsupported range: ",
|
||||
dstart,
|
||||
" -> ",
|
||||
dend);
|
||||
TORCH_CHECK(
|
||||
((dstep > 0) && (dend >= dstart)) || ((dstep < 0) && (dend <= dstart)),
|
||||
"upper bound and lower bound inconsistent with step sign");
|
||||
}
|
||||
|
||||
template <typename scalar_t>
|
||||
int64_t compute_arange_size(const Scalar& start,
|
||||
const Scalar& end,
|
||||
const Scalar& step) {
|
||||
arange_check_bounds(start, end, step);
|
||||
|
||||
// we use double precision for (start - end) / step
|
||||
// to compute size_d for consistency across devices.
|
||||
// The problem with using accscalar_t is that accscalar_t might be float32 on
|
||||
// gpu for a float32 scalar_t, but double on cpu for the same, and the
|
||||
// effective output size starts differing on CPU vs GPU because of precision
|
||||
// issues, which we dont want. the corner-case we do want to take into account
|
||||
// is int64_t, which has higher precision than double
|
||||
double size_d;
|
||||
if constexpr (std::is_same_v<scalar_t, int64_t>) {
|
||||
using accscalar_t = at::acc_type<scalar_t, false>;
|
||||
auto xstart = start.to<accscalar_t>();
|
||||
auto xend = end.to<accscalar_t>();
|
||||
auto xstep = step.to<accscalar_t>();
|
||||
int64_t sgn = (xstep > 0) - (xstep < 0);
|
||||
size_d = std::ceil((xend - xstart + xstep - sgn) / xstep);
|
||||
} else {
|
||||
size_d =
|
||||
std::ceil((end.to<double>() - start.to<double>()) / step.to<double>());
|
||||
}
|
||||
|
||||
TORCH_CHECK(size_d >= 0 && size_d <= static_cast<double>(
|
||||
std::numeric_limits<int64_t>::max()),
|
||||
"invalid size, possible overflow?");
|
||||
|
||||
return static_cast<int64_t>(size_d);
|
||||
}
|
||||
|
||||
} // namespace at::native
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2025 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
|
||||
|
||||
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
|
||||
#include <c10/cuda/CUDAGuard.h>
|
||||
#endif
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright (c) 2026 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.
|
||||
|
||||
// The file has been adapted from pytorch project
|
||||
// Licensed under BSD-style license -
|
||||
// https://github.com/pytorch/pytorch/blob/main/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ATen/core/Tensor.h>
|
||||
#include <c10/core/Scalar.h>
|
||||
|
||||
#include "paddle/phi/api/include/tensor.h"
|
||||
#include "paddle/phi/common/data_type.h"
|
||||
#include "paddle/phi/common/place.h"
|
||||
|
||||
namespace at {
|
||||
|
||||
/// Extracts a scalar value from a single-element dense tensor.
|
||||
/// Mirrors PyTorch's at::_local_scalar_dense: copies the tensor to CPU if
|
||||
/// needed, then reads the first element according to its dtype.
|
||||
inline at::Scalar _local_scalar_dense(const at::Tensor& self) {
|
||||
PD_CHECK(self.numel() > 0, "_local_scalar_dense: Empty tensor not supported");
|
||||
|
||||
// Move to CPU if necessary (for compatibility with PyTorch behavior)
|
||||
const PaddleTensor& inner = self._PD_GetInner();
|
||||
PaddleTensor cpu_tensor = inner;
|
||||
if (!phi::is_cpu_place(inner.place())) {
|
||||
PaddlePlace place(phi::AllocationType::CPU);
|
||||
cpu_tensor = inner.copy_to(place, /*blocking=*/true);
|
||||
}
|
||||
|
||||
auto dtype = cpu_tensor.dtype();
|
||||
switch (dtype) {
|
||||
case phi::DataType::FLOAT32:
|
||||
return at::Scalar(*(cpu_tensor.data<float>()));
|
||||
case phi::DataType::FLOAT64:
|
||||
return at::Scalar(*(cpu_tensor.data<double>()));
|
||||
case phi::DataType::FLOAT16:
|
||||
return at::Scalar(
|
||||
static_cast<float>(*(cpu_tensor.data<phi::dtype::float16>())));
|
||||
case phi::DataType::BFLOAT16:
|
||||
return at::Scalar(
|
||||
static_cast<float>(*(cpu_tensor.data<phi::dtype::bfloat16>())));
|
||||
case phi::DataType::INT8:
|
||||
return at::Scalar(*(cpu_tensor.data<int8_t>()));
|
||||
case phi::DataType::INT16:
|
||||
return at::Scalar(*(cpu_tensor.data<int16_t>()));
|
||||
case phi::DataType::INT32:
|
||||
return at::Scalar(*(cpu_tensor.data<int32_t>()));
|
||||
case phi::DataType::INT64:
|
||||
return at::Scalar(*(cpu_tensor.data<int64_t>()));
|
||||
case phi::DataType::UINT8:
|
||||
return at::Scalar(*(cpu_tensor.data<uint8_t>()));
|
||||
case phi::DataType::BOOL:
|
||||
return at::Scalar(*(cpu_tensor.data<bool>()));
|
||||
case phi::DataType::COMPLEX64:
|
||||
return at::Scalar(*(cpu_tensor.data<phi::dtype::complex<float>>()));
|
||||
case phi::DataType::COMPLEX128:
|
||||
return at::Scalar(*(cpu_tensor.data<phi::dtype::complex<double>>()));
|
||||
default:
|
||||
PD_THROW("_local_scalar_dense: Unsupported data type");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2026 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 <ATen/core/Tensor.h>
|
||||
#include "paddle/phi/core/sparse_coo_tensor.h"
|
||||
#include "paddle/phi/core/sparse_csr_tensor.h"
|
||||
|
||||
namespace at {} // namespace at
|
||||
|
||||
namespace at {
|
||||
|
||||
inline int64_t Tensor::_nnz() const {
|
||||
PD_CHECK(this->is_sparse(),
|
||||
"_nnz expected sparse tensor layout but got ",
|
||||
layout());
|
||||
if (tensor_.layout() == common::DataLayout::SPARSE_COO) {
|
||||
auto sparse_coo_tensor =
|
||||
std::dynamic_pointer_cast<phi::SparseCooTensor>(tensor_.impl());
|
||||
PD_CHECK(sparse_coo_tensor != nullptr,
|
||||
"_nnz: failed to cast tensor impl to SparseCooTensor");
|
||||
return sparse_coo_tensor->nnz();
|
||||
} else {
|
||||
auto sparse_csr_tensor =
|
||||
std::dynamic_pointer_cast<phi::SparseCsrTensor>(tensor_.impl());
|
||||
PD_CHECK(sparse_csr_tensor != nullptr,
|
||||
"_nnz: failed to cast tensor impl to SparseCsrTensor");
|
||||
return sparse_csr_tensor->nnz();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2026 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 <ATen/core/Tensor.h>
|
||||
#include "paddle/phi/core/sparse_coo_tensor.h"
|
||||
#include "paddle/phi/core/sparse_csr_tensor.h"
|
||||
|
||||
namespace at {} // namespace at
|
||||
|
||||
namespace at {
|
||||
|
||||
inline at::Tensor Tensor::_values() const {
|
||||
PD_CHECK(this->is_sparse(),
|
||||
"_values expected sparse tensor layout but got ",
|
||||
layout());
|
||||
if (tensor_.layout() == common::DataLayout::SPARSE_COO) {
|
||||
auto sparse_coo_tensor =
|
||||
std::dynamic_pointer_cast<phi::SparseCooTensor>(tensor_.impl());
|
||||
PD_CHECK(sparse_coo_tensor != nullptr,
|
||||
"_values: failed to cast tensor impl to SparseCooTensor");
|
||||
return paddle::Tensor(
|
||||
std::make_shared<phi::DenseTensor>(sparse_coo_tensor->values()));
|
||||
} else {
|
||||
PD_THROW("_values is not implemented for SparseCsr tensors");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) 2025 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 <ATen/core/Tensor.h>
|
||||
#include <c10/core/TensorOptions.h>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
#include "paddle/phi/api/include/api.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
|
||||
namespace at {
|
||||
|
||||
inline at::Tensor abs(const at::Tensor& self) {
|
||||
if (!self.is_contiguous()) {
|
||||
phi::enforce::ThrowWarnInternal(
|
||||
"at::abs: input tensor is non-contiguous. PyTorch and Paddle handle "
|
||||
"non-contiguous tensors differently, which may produce logically "
|
||||
"incorrect results even though the code is syntactically valid. "
|
||||
"See https://github.com/PaddlePaddle/Paddle/pull/78099 for details.");
|
||||
}
|
||||
return paddle::experimental::abs(self._PD_GetInner());
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
|
||||
namespace at {
|
||||
|
||||
inline at::Tensor Tensor::abs() const { return at::abs(*this); }
|
||||
|
||||
inline at::Tensor& Tensor::abs_() const {
|
||||
if (!is_contiguous()) {
|
||||
phi::enforce::ThrowWarnInternal(
|
||||
"Tensor::abs_: tensor is non-contiguous. PyTorch and Paddle handle "
|
||||
"non-contiguous tensors differently, which may produce logically "
|
||||
"incorrect results even though the code is syntactically valid. "
|
||||
"See https://github.com/PaddlePaddle/Paddle/pull/78099 for details.");
|
||||
}
|
||||
PaddleTensor& inner = const_cast<PaddleTensor&>(tensor_);
|
||||
paddle::experimental::abs_(inner);
|
||||
return const_cast<at::Tensor&>(*this);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright (c) 2026 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 <ATen/core/Tensor.h>
|
||||
#include <c10/util/OptionalArrayRef.h>
|
||||
|
||||
#include "paddle/phi/api/include/api.h"
|
||||
|
||||
namespace at {
|
||||
|
||||
// all: Check if all elements are true (non-zero)
|
||||
// Version 1: all() - check all elements in the tensor
|
||||
inline at::Tensor all(const at::Tensor& self) {
|
||||
return paddle::experimental::all(self._PD_GetInner(), {}, false);
|
||||
}
|
||||
|
||||
// Version 2: all(dim, keepdim) - check along a specific dimension
|
||||
inline at::Tensor all(const at::Tensor& self,
|
||||
int64_t dim,
|
||||
bool keepdim = false) {
|
||||
return paddle::experimental::all(self._PD_GetInner(), {dim}, keepdim);
|
||||
}
|
||||
|
||||
// Version 3: all(dim, keepdim) - check along optional dimensions
|
||||
inline at::Tensor all(const at::Tensor& self,
|
||||
at::OptionalIntArrayRef dim,
|
||||
bool keepdim = false) {
|
||||
std::vector<int64_t> axis_vec;
|
||||
if (dim.has_value()) {
|
||||
axis_vec.assign(dim.value().begin(), dim.value().end());
|
||||
}
|
||||
return paddle::experimental::all(self._PD_GetInner(), axis_vec, keepdim);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
|
||||
namespace at {
|
||||
|
||||
// Tensor member function implementations
|
||||
inline at::Tensor Tensor::all() const { return at::all(*this); }
|
||||
|
||||
inline at::Tensor Tensor::all(int64_t dim, bool keepdim) const {
|
||||
return at::all(*this, dim, keepdim);
|
||||
}
|
||||
|
||||
inline at::Tensor Tensor::all(at::OptionalIntArrayRef dim, bool keepdim) const {
|
||||
return at::all(*this, dim, keepdim);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) 2026 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 <ATen/core/Tensor.h>
|
||||
|
||||
#include "paddle/phi/api/include/api.h"
|
||||
#include "paddle/phi/common/data_type.h"
|
||||
#include "paddle/phi/common/scalar.h"
|
||||
|
||||
namespace at {
|
||||
|
||||
// allclose: Check if two tensors are close to each other
|
||||
inline bool allclose(const at::Tensor& self,
|
||||
const at::Tensor& other,
|
||||
double rtol = 1e-05,
|
||||
double atol = 1e-08,
|
||||
bool equal_nan = false) {
|
||||
// Paddle's allclose returns a Tensor, but PyTorch's allclose returns bool.
|
||||
// The allclose kernel always sets output dtype to phi::DataType::BOOL via
|
||||
// kernel->OutputAt(0).SetDataType(phi::DataType::BOOL), so we read BOOL
|
||||
// directly.
|
||||
PaddleTensor result = paddle::experimental::allclose(self._PD_GetInner(),
|
||||
other._PD_GetInner(),
|
||||
phi::Scalar(rtol),
|
||||
phi::Scalar(atol),
|
||||
equal_nan);
|
||||
auto* result_tensor =
|
||||
std::dynamic_pointer_cast<phi::DenseTensor>(result.impl()).get();
|
||||
return *result_tensor->data<bool>();
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
|
||||
namespace at {
|
||||
|
||||
// Tensor member function implementation
|
||||
inline bool Tensor::allclose(const at::Tensor& other,
|
||||
double rtol,
|
||||
double atol,
|
||||
bool equal_nan) const {
|
||||
return at::allclose(*this, other, rtol, atol, equal_nan);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,61 @@
|
||||
// Copyright (c) 2026 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 <ATen/core/Tensor.h>
|
||||
#include <c10/core/Scalar.h>
|
||||
#include <c10/util/OptionalArrayRef.h>
|
||||
|
||||
#include "paddle/phi/api/include/api.h"
|
||||
|
||||
namespace at {
|
||||
|
||||
// any - free functions
|
||||
inline Tensor any(const Tensor& self, int64_t dim, bool keepdim = false) {
|
||||
return paddle::experimental::any(self._PD_GetInner(), {dim}, keepdim);
|
||||
}
|
||||
|
||||
inline Tensor any(const Tensor& self,
|
||||
at::OptionalIntArrayRef dim,
|
||||
bool keepdim = false) {
|
||||
std::vector<int64_t> dims_vec;
|
||||
if (dim.has_value() && dim.value().size() > 0) {
|
||||
dims_vec.assign(dim.value().begin(), dim.value().end());
|
||||
}
|
||||
return paddle::experimental::any(self._PD_GetInner(), dims_vec, keepdim);
|
||||
}
|
||||
|
||||
inline Tensor any(const Tensor& self) {
|
||||
return paddle::experimental::any(self._PD_GetInner());
|
||||
}
|
||||
|
||||
// any - member function implementations
|
||||
inline Tensor Tensor::any(int64_t dim, bool keepdim) const {
|
||||
return paddle::experimental::any(_PD_GetInner(), {dim}, keepdim);
|
||||
}
|
||||
|
||||
inline Tensor Tensor::any(at::OptionalIntArrayRef dim, bool keepdim) const {
|
||||
std::vector<int64_t> dims_vec;
|
||||
if (dim.has_value() && dim.value().size() > 0) {
|
||||
dims_vec.assign(dim.value().begin(), dim.value().end());
|
||||
}
|
||||
return paddle::experimental::any(_PD_GetInner(), dims_vec, keepdim);
|
||||
}
|
||||
|
||||
inline Tensor Tensor::any() const {
|
||||
return paddle::experimental::any(_PD_GetInner());
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,157 @@
|
||||
// Copyright (c) 2025 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.
|
||||
|
||||
// The file has been adapted from pytorch project
|
||||
// Licensed under BSD-style license -
|
||||
// https://github.com/pytorch/pytorch/blob/main/LICENSE
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ATen/core/Tensor.h>
|
||||
#include <ATen/native/RangeUtils.h>
|
||||
#include <c10/core/TensorOptions.h>
|
||||
#include <utils/pinned_place.h>
|
||||
#include <optional>
|
||||
|
||||
#include "paddle/phi/api/include/api.h"
|
||||
#include "paddle/phi/common/place.h"
|
||||
|
||||
namespace at {
|
||||
|
||||
namespace detail {
|
||||
|
||||
inline bool _PD_IsIntegralArangeScalar(const at::Scalar& scalar) {
|
||||
switch (scalar.dtype()) {
|
||||
case phi::DataType::BOOL:
|
||||
case phi::DataType::UINT8:
|
||||
case phi::DataType::INT8:
|
||||
case phi::DataType::UINT16:
|
||||
case phi::DataType::INT16:
|
||||
case phi::DataType::UINT32:
|
||||
case phi::DataType::INT32:
|
||||
case phi::DataType::UINT64:
|
||||
case phi::DataType::INT64:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline at::ScalarType _PD_ResolveArangeDtype(const at::Scalar& start,
|
||||
const at::Scalar& end,
|
||||
const at::Scalar& step,
|
||||
const at::TensorOptions& options) {
|
||||
if (options.has_dtype()) {
|
||||
return options.dtype().toScalarType();
|
||||
}
|
||||
if (_PD_IsIntegralArangeScalar(start) && _PD_IsIntegralArangeScalar(end) &&
|
||||
_PD_IsIntegralArangeScalar(step)) {
|
||||
return at::kLong;
|
||||
}
|
||||
return c10::get_default_dtype_as_scalartype();
|
||||
}
|
||||
|
||||
inline paddle::Tensor _PD_MakeArangeScalarTensor(const at::Scalar& scalar,
|
||||
phi::DataType dtype) {
|
||||
return paddle::experimental::full({}, scalar, dtype, phi::CPUPlace());
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
inline at::Tensor arange(const at::Scalar& start,
|
||||
const at::Scalar& end,
|
||||
const at::Scalar& step,
|
||||
at::TensorOptions options = {}) {
|
||||
// Match PyTorch: step must be non-zero and consistent with (end - start).
|
||||
at::native::arange_check_bounds(start, end, step);
|
||||
auto dtype = detail::_PD_ResolveArangeDtype(start, end, step, options);
|
||||
auto pd_dtype = compat::_PD_AtenScalarTypeToPhiDataType(dtype);
|
||||
if (options.pinned_memory()) {
|
||||
// Pinning memory is only supported for CPU tensors
|
||||
if (options.has_device() && !options.device().is_cpu()) {
|
||||
PD_THROW(
|
||||
"pin_memory=true requires device to be CPU, but got non-CPU device");
|
||||
}
|
||||
phi::Place base_place = options._PD_GetPlace();
|
||||
phi::Place pinned_place = compat::_PD_GetCreatePinnedPlace(base_place);
|
||||
auto dense = paddle::experimental::arange(
|
||||
detail::_PD_MakeArangeScalarTensor(start, pd_dtype),
|
||||
detail::_PD_MakeArangeScalarTensor(end, pd_dtype),
|
||||
detail::_PD_MakeArangeScalarTensor(step, pd_dtype),
|
||||
pd_dtype,
|
||||
phi::CPUPlace());
|
||||
return dense.copy_to(pinned_place, /*blocking=*/true);
|
||||
}
|
||||
return paddle::experimental::arange(
|
||||
detail::_PD_MakeArangeScalarTensor(start, pd_dtype),
|
||||
detail::_PD_MakeArangeScalarTensor(end, pd_dtype),
|
||||
detail::_PD_MakeArangeScalarTensor(step, pd_dtype),
|
||||
pd_dtype,
|
||||
options._PD_GetPlace());
|
||||
}
|
||||
|
||||
inline at::Tensor arange(const at::Scalar& end,
|
||||
at::TensorOptions options = {}) {
|
||||
return arange(/*start=*/0, end, /*step=*/1, options);
|
||||
}
|
||||
|
||||
inline at::Tensor arange(const at::Scalar& end,
|
||||
::std::optional<at::ScalarType> dtype,
|
||||
::std::optional<at::Layout> layout,
|
||||
::std::optional<at::Device> device,
|
||||
::std::optional<bool> pin_memory) {
|
||||
auto options = at::TensorOptions()
|
||||
.dtype(dtype)
|
||||
.layout(layout)
|
||||
.device(device)
|
||||
.pinned_memory(pin_memory);
|
||||
return arange(/*start=*/0, end, /*step=*/1, options);
|
||||
}
|
||||
|
||||
inline at::Tensor arange(const at::Scalar& start,
|
||||
const at::Scalar& end,
|
||||
at::TensorOptions options = {}) {
|
||||
return arange(start, end, /*step=*/1, options);
|
||||
}
|
||||
|
||||
inline at::Tensor arange(const at::Scalar& start,
|
||||
const at::Scalar& end,
|
||||
::std::optional<at::ScalarType> dtype,
|
||||
::std::optional<at::Layout> layout,
|
||||
::std::optional<at::Device> device,
|
||||
::std::optional<bool> pin_memory) {
|
||||
auto options = at::TensorOptions()
|
||||
.dtype(dtype)
|
||||
.layout(layout)
|
||||
.device(device)
|
||||
.pinned_memory(pin_memory);
|
||||
return arange(start, end, /*step=*/1, options);
|
||||
}
|
||||
|
||||
inline at::Tensor arange(const at::Scalar& start,
|
||||
const at::Scalar& end,
|
||||
const at::Scalar& step,
|
||||
::std::optional<at::ScalarType> dtype,
|
||||
::std::optional<at::Layout> layout,
|
||||
::std::optional<at::Device> device,
|
||||
::std::optional<bool> pin_memory) {
|
||||
auto options = at::TensorOptions()
|
||||
.dtype(dtype)
|
||||
.layout(layout)
|
||||
.device(device)
|
||||
.pinned_memory(pin_memory);
|
||||
return arange(start, end, step, options);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,113 @@
|
||||
// Copyright (c) 2026 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 <ATen/core/Tensor.h>
|
||||
#include <c10/util/ArrayRef.h>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include "paddle/common/ddim.h"
|
||||
#include "paddle/phi/core/dense_tensor.h"
|
||||
|
||||
namespace at {
|
||||
|
||||
// as_strided: Create a tensor view with custom size, stride, and storage_offset
|
||||
inline at::Tensor Tensor::as_strided(
|
||||
at::IntArrayRef size,
|
||||
at::IntArrayRef stride,
|
||||
::std::optional<int64_t> storage_offset) const {
|
||||
// Materialize the compat StorageHolderView before creating the view so
|
||||
// aliasing tensors share one StorageImpl and observe later resize_ growth.
|
||||
(void)this->storage();
|
||||
auto src_impl = tensor_.impl();
|
||||
auto* src_tensor =
|
||||
std::dynamic_pointer_cast<phi::DenseTensor>(src_impl).get();
|
||||
if (!src_tensor) {
|
||||
PD_THROW("as_strided: tensor must be a DenseTensor");
|
||||
}
|
||||
// Create new meta with desired shape and strides first
|
||||
std::vector<int64_t> size_vec(size.begin(), size.end());
|
||||
std::vector<int64_t> stride_vec(stride.begin(), stride.end());
|
||||
|
||||
// Create new DenseTensor with correct meta, then share data
|
||||
// We need to create a temporary DenseTensor with the right meta
|
||||
// because ShareDataWith copies the source meta which we don't want
|
||||
auto new_tensor = std::make_shared<phi::DenseTensor>();
|
||||
|
||||
// First, set up the holder by sharing data (this copies src meta, we'll
|
||||
// override)
|
||||
new_tensor->ShareDataWith(*src_tensor);
|
||||
|
||||
// Now create the correct meta with new shape/strides
|
||||
phi::DenseTensorMeta meta(src_tensor->dtype(),
|
||||
common::make_ddim(size_vec),
|
||||
common::make_ddim(stride_vec));
|
||||
// Calculate offset in bytes
|
||||
int64_t offset = storage_offset.has_value() ? storage_offset.value() : 0;
|
||||
meta.offset = src_tensor->meta().offset +
|
||||
static_cast<size_t>(offset) * phi::SizeOf(src_tensor->dtype());
|
||||
new_tensor->set_meta(meta);
|
||||
PaddleTensor result;
|
||||
result.set_impl(new_tensor);
|
||||
return Tensor(result);
|
||||
}
|
||||
|
||||
// as_strided_: Inplace version
|
||||
inline const at::Tensor& Tensor::as_strided_(
|
||||
at::IntArrayRef size,
|
||||
at::IntArrayRef stride,
|
||||
::std::optional<int64_t> storage_offset) const {
|
||||
// Keep inplace metadata-only view rewrites attached to the same compat
|
||||
// storage as the original tensor.
|
||||
(void)this->storage();
|
||||
auto src_impl = tensor_.impl();
|
||||
auto* src_tensor =
|
||||
std::dynamic_pointer_cast<phi::DenseTensor>(src_impl).get();
|
||||
if (!src_tensor) {
|
||||
PD_THROW("as_strided_: tensor must be a DenseTensor");
|
||||
}
|
||||
std::vector<int64_t> size_vec(size.begin(), size.end());
|
||||
std::vector<int64_t> stride_vec(stride.begin(), stride.end());
|
||||
// Use set_meta instead of Resize + set_strides to avoid contiguous check
|
||||
phi::DenseTensorMeta meta(src_tensor->dtype(),
|
||||
common::make_ddim(size_vec),
|
||||
common::make_ddim(stride_vec));
|
||||
meta.layout = src_tensor->layout();
|
||||
int64_t offset = storage_offset.has_value() ? storage_offset.value() : 0;
|
||||
meta.offset = src_tensor->meta().offset +
|
||||
static_cast<size_t>(offset) * phi::SizeOf(src_tensor->dtype());
|
||||
src_tensor->set_meta(meta);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// as_strided_scatter: Scatter src into a strided view
|
||||
// Returns a new tensor (copy of self) with the strided window filled by src.
|
||||
// The original tensor is NOT modified.
|
||||
inline at::Tensor Tensor::as_strided_scatter(
|
||||
const at::Tensor& src,
|
||||
at::IntArrayRef size,
|
||||
at::IntArrayRef stride,
|
||||
::std::optional<int64_t> storage_offset) const {
|
||||
// Clone self to an independent copy so the original tensor is left unchanged
|
||||
PaddleTensor self_copy = tensor_.copy_to(tensor_.place(), /*blocking=*/true);
|
||||
at::Tensor copy_tensor(self_copy);
|
||||
at::Tensor strided_view =
|
||||
copy_tensor.as_strided(size, stride, storage_offset);
|
||||
strided_view.copy_(src);
|
||||
return copy_tensor;
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2025 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 <ATen/core/Tensor.h>
|
||||
#include <c10/core/TensorOptions.h>
|
||||
#include <optional>
|
||||
|
||||
#include "paddle/phi/api/include/api.h"
|
||||
|
||||
namespace at {
|
||||
|
||||
inline at::Tensor cat(const at::ITensorListRef& tensors, int64_t dim = 0) {
|
||||
std::vector<paddle::Tensor> pd_tensors;
|
||||
pd_tensors.reserve(tensors.size());
|
||||
for (const auto& t : tensors) {
|
||||
pd_tensors.push_back(t._PD_GetInner());
|
||||
}
|
||||
return paddle::experimental::concat(pd_tensors, dim);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,98 @@
|
||||
// Copyright (c) 2026 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 <ATen/core/Tensor.h>
|
||||
#include <vector>
|
||||
|
||||
#include "paddle/phi/api/include/api.h"
|
||||
|
||||
namespace at {
|
||||
|
||||
// chunk - splits tensor into chunks
|
||||
inline std::vector<Tensor> chunk(const Tensor& self,
|
||||
int64_t chunks,
|
||||
int64_t dim = 0) {
|
||||
if (chunks <= 0) {
|
||||
PD_THROW("chunk expects chunks to be greater than 0, got ", chunks);
|
||||
}
|
||||
|
||||
std::vector<Tensor> result;
|
||||
paddle::Tensor pd_tensor = self._PD_GetInner();
|
||||
|
||||
int64_t rank = static_cast<int64_t>(pd_tensor.dims().size());
|
||||
if (rank == 0) {
|
||||
PD_THROW("chunk expects at least a 1-dimensional tensor");
|
||||
}
|
||||
|
||||
int64_t original_dim = dim;
|
||||
if (dim < 0) {
|
||||
dim += rank;
|
||||
}
|
||||
if (dim < 0 || dim >= rank) {
|
||||
PD_THROW("Dimension out of range (expected to be in range of [",
|
||||
-rank,
|
||||
", ",
|
||||
rank - 1,
|
||||
"], but got ",
|
||||
original_dim,
|
||||
")");
|
||||
}
|
||||
|
||||
int64_t dim_size = pd_tensor.dims()[dim];
|
||||
|
||||
if (dim_size == 0) {
|
||||
for (int64_t i = 0; i < chunks; ++i) {
|
||||
auto chunk_tensor =
|
||||
paddle::experimental::slice(pd_tensor, {dim}, {0}, {0}, {1}, {});
|
||||
result.push_back(Tensor(chunk_tensor));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// PyTorch returns at most 'dim_size' non-empty chunks when chunks > dim_size
|
||||
if (chunks > dim_size) {
|
||||
chunks = dim_size;
|
||||
}
|
||||
|
||||
int64_t chunk_size = (dim_size + chunks - 1) / chunks;
|
||||
int64_t remaining = dim_size;
|
||||
|
||||
for (int64_t i = 0; i < chunks && remaining > 0; ++i) {
|
||||
int64_t current_chunk_size = std::min(chunk_size, remaining);
|
||||
auto chunk_tensor =
|
||||
paddle::experimental::slice(pd_tensor,
|
||||
{dim},
|
||||
{i * chunk_size},
|
||||
{i * chunk_size + current_chunk_size},
|
||||
{1},
|
||||
{});
|
||||
result.push_back(Tensor(chunk_tensor));
|
||||
remaining -= current_chunk_size;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
|
||||
namespace at {
|
||||
|
||||
// Member function: Tensor::chunk
|
||||
inline std::vector<Tensor> Tensor::chunk(int64_t chunks, int64_t dim) const {
|
||||
return at::chunk(*this, chunks, dim);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,212 @@
|
||||
// Copyright (c) 2026 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 <ATen/core/Tensor.h>
|
||||
#include <ATen/core/TensorBase.h>
|
||||
#include <ATen/ops/full.h>
|
||||
#include <c10/core/Scalar.h>
|
||||
#include <c10/core/ScalarType.h>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include "paddle/phi/api/include/tensor.h"
|
||||
|
||||
namespace at {
|
||||
|
||||
// Helper function implementations
|
||||
namespace detail {
|
||||
inline at::Scalar get_default_min_value(c10::ScalarType dtype) {
|
||||
switch (dtype) {
|
||||
case c10::ScalarType::Byte:
|
||||
return at::Scalar(static_cast<uint8_t>(0));
|
||||
case c10::ScalarType::Char:
|
||||
return at::Scalar(std::numeric_limits<int8_t>::lowest());
|
||||
case c10::ScalarType::Short:
|
||||
return at::Scalar(std::numeric_limits<int16_t>::lowest());
|
||||
case c10::ScalarType::Int:
|
||||
return at::Scalar(std::numeric_limits<int32_t>::lowest());
|
||||
case c10::ScalarType::Long:
|
||||
return at::Scalar(std::numeric_limits<int64_t>::lowest());
|
||||
case c10::ScalarType::UInt16:
|
||||
return at::Scalar(static_cast<uint16_t>(0));
|
||||
case c10::ScalarType::UInt32:
|
||||
return at::Scalar(static_cast<uint32_t>(0));
|
||||
case c10::ScalarType::UInt64:
|
||||
return at::Scalar(static_cast<uint64_t>(0));
|
||||
case c10::ScalarType::Half:
|
||||
return at::Scalar(-std::numeric_limits<float>::infinity());
|
||||
case c10::ScalarType::Float:
|
||||
return at::Scalar(-std::numeric_limits<float>::infinity());
|
||||
case c10::ScalarType::Double:
|
||||
return at::Scalar(-std::numeric_limits<double>::infinity());
|
||||
case c10::ScalarType::BFloat16:
|
||||
return at::Scalar(-std::numeric_limits<float>::infinity());
|
||||
case c10::ScalarType::Bool:
|
||||
return at::Scalar(false);
|
||||
default:
|
||||
return at::Scalar(-std::numeric_limits<double>::infinity());
|
||||
}
|
||||
}
|
||||
|
||||
inline at::Scalar get_default_max_value(c10::ScalarType dtype) {
|
||||
switch (dtype) {
|
||||
case c10::ScalarType::Byte:
|
||||
return at::Scalar(std::numeric_limits<uint8_t>::max());
|
||||
case c10::ScalarType::Char:
|
||||
return at::Scalar(std::numeric_limits<int8_t>::max());
|
||||
case c10::ScalarType::Short:
|
||||
return at::Scalar(std::numeric_limits<int16_t>::max());
|
||||
case c10::ScalarType::Int:
|
||||
return at::Scalar(std::numeric_limits<int32_t>::max());
|
||||
case c10::ScalarType::Long:
|
||||
return at::Scalar(std::numeric_limits<int64_t>::max());
|
||||
case c10::ScalarType::UInt16:
|
||||
return at::Scalar(std::numeric_limits<uint16_t>::max());
|
||||
case c10::ScalarType::UInt32:
|
||||
return at::Scalar(std::numeric_limits<uint32_t>::max());
|
||||
case c10::ScalarType::UInt64:
|
||||
return at::Scalar(std::numeric_limits<uint64_t>::max());
|
||||
case c10::ScalarType::Half:
|
||||
return at::Scalar(std::numeric_limits<float>::infinity());
|
||||
case c10::ScalarType::Float:
|
||||
return at::Scalar(std::numeric_limits<float>::infinity());
|
||||
case c10::ScalarType::Double:
|
||||
return at::Scalar(std::numeric_limits<double>::infinity());
|
||||
case c10::ScalarType::BFloat16:
|
||||
return at::Scalar(std::numeric_limits<float>::infinity());
|
||||
case c10::ScalarType::Bool:
|
||||
return at::Scalar(true);
|
||||
default:
|
||||
return at::Scalar(std::numeric_limits<double>::infinity());
|
||||
}
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
} // namespace at
|
||||
|
||||
namespace at {
|
||||
|
||||
inline at::Tensor Tensor::clamp(const ::std::optional<at::Scalar>& min,
|
||||
const ::std::optional<at::Scalar>& max) const {
|
||||
// Handle cases where min or max is nullopt - don't apply that bound
|
||||
if (min.has_value() && !max.has_value()) {
|
||||
// Only min is specified - use clamp_min
|
||||
return clamp_min(min.value());
|
||||
} else if (!min.has_value() && max.has_value()) {
|
||||
// Only max is specified - use clamp_max
|
||||
return clamp_max(max.value());
|
||||
} else if (!min.has_value() && !max.has_value()) {
|
||||
// Neither specified - return copy of tensor
|
||||
return *this;
|
||||
}
|
||||
// Both specified - apply full clamp
|
||||
return Tensor(paddle::experimental::clip(tensor_, min.value(), max.value()));
|
||||
}
|
||||
|
||||
inline at::Tensor Tensor::clamp(const ::std::optional<at::Tensor>& min,
|
||||
const ::std::optional<at::Tensor>& max) const {
|
||||
PaddleTensor result = tensor_;
|
||||
if (min.has_value()) {
|
||||
result = paddle::experimental::maximum(result, min.value()._PD_GetInner());
|
||||
}
|
||||
if (max.has_value()) {
|
||||
result = paddle::experimental::minimum(result, max.value()._PD_GetInner());
|
||||
}
|
||||
return Tensor(result);
|
||||
}
|
||||
|
||||
inline at::Tensor& Tensor::clamp_(
|
||||
const ::std::optional<at::Scalar>& min,
|
||||
const ::std::optional<at::Scalar>& max) const {
|
||||
// Handle cases where min or max is nullopt - don't apply that bound
|
||||
if (min.has_value() && !max.has_value()) {
|
||||
// Only min is specified - use clamp_min_
|
||||
return clamp_min_(min.value());
|
||||
} else if (!min.has_value() && max.has_value()) {
|
||||
// Only max is specified - use clamp_max_
|
||||
return clamp_max_(max.value());
|
||||
} else if (!min.has_value() && !max.has_value()) {
|
||||
// Neither specified - nothing to do
|
||||
return const_cast<at::Tensor&>(*this);
|
||||
}
|
||||
// Both specified - apply full clamp
|
||||
paddle::experimental::clip_(
|
||||
const_cast<PaddleTensor&>(tensor_), min.value(), max.value());
|
||||
return const_cast<at::Tensor&>(*this);
|
||||
}
|
||||
|
||||
inline at::Tensor& Tensor::clamp_(
|
||||
const ::std::optional<at::Tensor>& min,
|
||||
const ::std::optional<at::Tensor>& max) const {
|
||||
if (min.has_value()) {
|
||||
PaddleTensor temp =
|
||||
paddle::experimental::maximum(tensor_, min.value()._PD_GetInner());
|
||||
const_cast<PaddleTensor&>(tensor_) = temp;
|
||||
}
|
||||
if (max.has_value()) {
|
||||
PaddleTensor temp =
|
||||
paddle::experimental::minimum(tensor_, max.value()._PD_GetInner());
|
||||
const_cast<PaddleTensor&>(tensor_) = temp;
|
||||
}
|
||||
return const_cast<at::Tensor&>(*this);
|
||||
}
|
||||
|
||||
inline at::Tensor Tensor::clamp_max(const at::Scalar& max) const {
|
||||
// Create a tensor with the same shape filled with the max value
|
||||
at::Tensor max_tensor = at::full(tensor_.shape(), max, {});
|
||||
return clamp_max(max_tensor);
|
||||
}
|
||||
|
||||
inline at::Tensor Tensor::clamp_max(const at::Tensor& max) const {
|
||||
return Tensor(paddle::experimental::minimum(tensor_, max._PD_GetInner()));
|
||||
}
|
||||
|
||||
inline at::Tensor& Tensor::clamp_max_(const at::Scalar& max) const {
|
||||
// Create a tensor with the same shape filled with the max value
|
||||
at::Tensor max_tensor = at::full(tensor_.shape(), max, {});
|
||||
return clamp_max_(max_tensor);
|
||||
}
|
||||
|
||||
inline at::Tensor& Tensor::clamp_max_(const at::Tensor& max) const {
|
||||
PaddleTensor temp =
|
||||
paddle::experimental::minimum(tensor_, max._PD_GetInner());
|
||||
const_cast<PaddleTensor&>(tensor_) = temp;
|
||||
return const_cast<at::Tensor&>(*this);
|
||||
}
|
||||
|
||||
inline at::Tensor Tensor::clamp_min(const at::Scalar& min) const {
|
||||
// Create a tensor with the same shape filled with the min value
|
||||
at::Tensor min_tensor = at::full(tensor_.shape(), min, {});
|
||||
return clamp_min(min_tensor);
|
||||
}
|
||||
|
||||
inline at::Tensor Tensor::clamp_min(const at::Tensor& min) const {
|
||||
return Tensor(paddle::experimental::maximum(tensor_, min._PD_GetInner()));
|
||||
}
|
||||
|
||||
inline at::Tensor& Tensor::clamp_min_(const at::Scalar& min) const {
|
||||
// Create a tensor with the same shape filled with the min value
|
||||
at::Tensor min_tensor = at::full(tensor_.shape(), min, {});
|
||||
return clamp_min_(min_tensor);
|
||||
}
|
||||
|
||||
inline at::Tensor& Tensor::clamp_min_(const at::Tensor& min) const {
|
||||
PaddleTensor temp =
|
||||
paddle::experimental::maximum(tensor_, min._PD_GetInner());
|
||||
const_cast<PaddleTensor&>(tensor_) = temp;
|
||||
return const_cast<at::Tensor&>(*this);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2026 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 <ATen/core/Tensor.h>
|
||||
#include "paddle/phi/api/include/sparse_api.h"
|
||||
|
||||
namespace at {} // namespace at
|
||||
|
||||
namespace at {
|
||||
|
||||
inline at::Tensor Tensor::coalesce() const {
|
||||
PD_CHECK(layout() == kSparse,
|
||||
"coalesce expected sparse coordinate tensor layout but got ",
|
||||
layout());
|
||||
if (is_coalesced()) {
|
||||
return *this;
|
||||
}
|
||||
return paddle::experimental::sparse::coalesce(tensor_);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) 2026 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 <ATen/core/Tensor.h>
|
||||
|
||||
namespace at {
|
||||
|
||||
inline at::Tensor detach(const at::Tensor& self) {
|
||||
// Create a new Tensor that shares data but has no autograd history
|
||||
auto inner = self._PD_GetInner();
|
||||
PaddleTensor detached_tensor(inner.impl());
|
||||
detached_tensor.set_name(inner.name());
|
||||
detached_tensor.set_autograd_meta(nullptr);
|
||||
return Tensor(detached_tensor);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
|
||||
namespace at {
|
||||
|
||||
inline at::Tensor Tensor::detach() const { return at::detach(*this); }
|
||||
|
||||
inline at::Tensor& Tensor::detach_() const {
|
||||
// In-place version: clear autograd meta of current tensor
|
||||
PaddleTensor& inner = const_cast<PaddleTensor&>(tensor_);
|
||||
inner.set_autograd_meta(nullptr);
|
||||
return const_cast<at::Tensor&>(*this);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2026 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 <ATen/core/Tensor.h>
|
||||
#include <ATen/ops/tensor_split.h>
|
||||
|
||||
namespace at {
|
||||
|
||||
inline std::vector<at::Tensor> dsplit(const at::Tensor& self,
|
||||
int64_t sections) {
|
||||
return tensor_split(self, sections, 2);
|
||||
}
|
||||
|
||||
inline std::vector<at::Tensor> dsplit(const at::Tensor& self,
|
||||
at::IntArrayRef indices) {
|
||||
return tensor_split(self, indices, 2);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
|
||||
namespace at {
|
||||
|
||||
inline std::vector<at::Tensor> Tensor::dsplit(int64_t sections) const {
|
||||
return at::dsplit(*this, sections);
|
||||
}
|
||||
|
||||
inline std::vector<at::Tensor> Tensor::dsplit(at::IntArrayRef indices) const {
|
||||
return at::dsplit(*this, indices);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,75 @@
|
||||
// Copyright (c) 2025 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 <ATen/core/Tensor.h>
|
||||
#include <c10/core/TensorOptions.h>
|
||||
#include <utils/dense_sparse_conversion.h>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
#include "paddle/phi/api/include/api.h"
|
||||
#include "paddle/phi/common/place.h"
|
||||
|
||||
namespace at {
|
||||
|
||||
inline at::Tensor empty(
|
||||
at::IntArrayRef size,
|
||||
at::TensorOptions options = {},
|
||||
::std::optional<at::MemoryFormat> memory_format = ::std::nullopt) {
|
||||
PD_CHECK(!(memory_format.has_value() &&
|
||||
memory_format.value() != c10::MemoryFormat::Contiguous),
|
||||
"`MemoryFormat` other than Contiguous is not supported now.");
|
||||
if (options.pinned_memory()) {
|
||||
// Pinning memory is only supported for CPU tensors
|
||||
if (options.has_device() && !options.device().is_cpu()) {
|
||||
PD_THROW(
|
||||
"pin_memory=true requires device to be CPU, but got non-CPU device");
|
||||
}
|
||||
auto dense = paddle::experimental::empty(
|
||||
size._PD_ToPaddleIntArray(),
|
||||
compat::_PD_AtenScalarTypeToPhiDataType(options.dtype()),
|
||||
phi::CPUPlace());
|
||||
dense = dense.copy_to(phi::GPUPinnedPlace(), /*blocking=*/true);
|
||||
return compat::_PD_ConvertToSparseIfNeeded(dense, options.layout());
|
||||
}
|
||||
auto dense = paddle::experimental::empty(
|
||||
size._PD_ToPaddleIntArray(),
|
||||
compat::_PD_AtenScalarTypeToPhiDataType(options.dtype()),
|
||||
options._PD_GetPlace());
|
||||
return compat::_PD_ConvertToSparseIfNeeded(dense, options.layout());
|
||||
}
|
||||
|
||||
inline at::Tensor empty(at::IntArrayRef size,
|
||||
::std::optional<at::ScalarType> dtype,
|
||||
::std::optional<at::Layout> layout,
|
||||
::std::optional<at::Device> device,
|
||||
::std::optional<bool> pin_memory,
|
||||
::std::optional<at::MemoryFormat> memory_format) {
|
||||
PD_CHECK(!(memory_format.has_value() &&
|
||||
memory_format.value() != c10::MemoryFormat::Contiguous),
|
||||
"`MemoryFormat` other than Contiguous is not supported now.");
|
||||
auto options =
|
||||
at::TensorOptions()
|
||||
.dtype(dtype.value_or(c10::get_default_dtype_as_scalartype()))
|
||||
.layout(layout)
|
||||
.device(device.value_or(at::kCPU))
|
||||
.pinned_memory(pin_memory);
|
||||
return empty(size, options, memory_format);
|
||||
}
|
||||
|
||||
#define empty_symint empty // SymIntArrayRef is same as IntArrayRef
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,81 @@
|
||||
// Copyright (c) 2025 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 <ATen/core/Tensor.h>
|
||||
#include <c10/core/TensorOptions.h>
|
||||
#include <utils/dense_sparse_conversion.h>
|
||||
#include <utils/pinned_place.h>
|
||||
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
#include "paddle/phi/api/include/api.h"
|
||||
#include "paddle/phi/common/place.h"
|
||||
|
||||
namespace at {
|
||||
|
||||
inline at::Tensor empty_like(
|
||||
const at::Tensor& self,
|
||||
at::TensorOptions options = {},
|
||||
::std::optional<at::MemoryFormat> memory_format = ::std::nullopt) {
|
||||
PD_CHECK(!(memory_format.has_value() &&
|
||||
memory_format.value() != c10::MemoryFormat::Contiguous),
|
||||
"`MemoryFormat` other than Contiguous is not supported now.");
|
||||
|
||||
auto dtype = options.dtype_opt().value_or(self.dtype());
|
||||
paddle::Tensor dense;
|
||||
if (options.pinned_memory()) {
|
||||
// Pinning memory is only supported for CPU tensors
|
||||
if (options.has_device() && !options.device().is_cpu()) {
|
||||
PD_THROW(
|
||||
"pin_memory=true requires device to be CPU, but got non-CPU device");
|
||||
}
|
||||
auto dense_cpu = paddle::experimental::empty_like(
|
||||
self._PD_GetInner(),
|
||||
compat::_PD_AtenScalarTypeToPhiDataType(dtype),
|
||||
phi::CPUPlace());
|
||||
phi::Place base_place = options._PD_GetPlace();
|
||||
phi::Place pinned_place = compat::_PD_GetCreatePinnedPlace(base_place);
|
||||
dense = dense_cpu.copy_to(pinned_place, /*blocking=*/true);
|
||||
} else {
|
||||
auto place = options.device_opt().value_or(self.device());
|
||||
dense = paddle::experimental::empty_like(
|
||||
self._PD_GetInner(),
|
||||
compat::_PD_AtenScalarTypeToPhiDataType(dtype),
|
||||
place._PD_GetInner());
|
||||
}
|
||||
return compat::_PD_ConvertToSparseIfNeeded(dense, options.layout());
|
||||
}
|
||||
|
||||
inline at::Tensor empty_like(const at::Tensor& self,
|
||||
::std::optional<at::ScalarType> dtype,
|
||||
::std::optional<at::Layout> layout,
|
||||
::std::optional<at::Device> device,
|
||||
::std::optional<bool> pin_memory,
|
||||
::std::optional<at::MemoryFormat> memory_format) {
|
||||
PD_CHECK(!(memory_format.has_value() &&
|
||||
memory_format.value() != c10::MemoryFormat::Contiguous),
|
||||
"`MemoryFormat` other than Contiguous is not supported now.");
|
||||
|
||||
auto options = at::TensorOptions()
|
||||
.dtype(dtype)
|
||||
.layout(layout)
|
||||
.device(device)
|
||||
.pinned_memory(pin_memory);
|
||||
return empty_like(self, options, memory_format);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2025 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 <ATen/core/Tensor.h>
|
||||
#include <c10/core/TensorOptions.h>
|
||||
#include <c10/util/ArrayRef.h>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
#include "paddle/phi/api/include/api.h"
|
||||
|
||||
namespace at {
|
||||
|
||||
inline at::Tensor empty_strided(at::IntArrayRef size,
|
||||
at::IntArrayRef stride,
|
||||
at::TensorOptions options = {}) {
|
||||
auto empty_tensor = paddle::experimental::empty(
|
||||
size._PD_ToPaddleIntArray(),
|
||||
compat::_PD_AtenScalarTypeToPhiDataType(options.dtype()),
|
||||
options._PD_GetPlace());
|
||||
|
||||
return paddle::experimental::as_strided(
|
||||
empty_tensor,
|
||||
std::vector<int64_t>(size.begin(), size.end()),
|
||||
std::vector<int64_t>(stride.begin(), stride.end()));
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright (c) 2026 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 <ATen/core/Tensor.h>
|
||||
#include <ATen/ops/item.h>
|
||||
|
||||
#include "paddle/phi/api/include/api.h"
|
||||
|
||||
namespace at {
|
||||
|
||||
inline bool equal(const at::Tensor& self, const at::Tensor& other) {
|
||||
PD_CHECK(self.defined(),
|
||||
"Expected a proper Tensor but got None (or an undefined Tensor in "
|
||||
"C++)");
|
||||
PD_CHECK(other.defined(),
|
||||
"Expected a proper Tensor but got None (or an undefined Tensor in "
|
||||
"C++)");
|
||||
PD_CHECK(self.device() == other.device(),
|
||||
"Cannot compare two tensors on "
|
||||
"different devices. Got: ",
|
||||
self.device(),
|
||||
" and ",
|
||||
other.device());
|
||||
if (self.sizes() != other.sizes()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto lhs = self._PD_GetInner();
|
||||
auto rhs = other._PD_GetInner();
|
||||
if (self.scalar_type() != other.scalar_type()) {
|
||||
rhs = paddle::experimental::cast(
|
||||
rhs, compat::_PD_AtenScalarTypeToPhiDataType(self.scalar_type()));
|
||||
}
|
||||
|
||||
auto result = paddle::experimental::equal_all(lhs, rhs);
|
||||
return at::Tensor(std::move(result)).item<bool>();
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
|
||||
namespace at {
|
||||
|
||||
inline bool Tensor::equal(const at::Tensor& other) const {
|
||||
return at::equal(*this, other);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,137 @@
|
||||
// Copyright (c) 2026 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 <ATen/core/Tensor.h>
|
||||
|
||||
#include "paddle/phi/api/include/api.h"
|
||||
|
||||
namespace at {
|
||||
|
||||
// expand - expands tensor to new size
|
||||
// PyTorch's expand works by right-aligning dimensions and broadcasting
|
||||
// dimensions with size 1 to the target size
|
||||
// Unlike Paddle's expand_v2, PyTorch allows non-singleton dimensions to be
|
||||
// preserved when they match the corresponding target dimension
|
||||
inline Tensor expand(const Tensor& self,
|
||||
at::IntArrayRef size,
|
||||
bool implicit = false) {
|
||||
// implicit parameter is used by PyTorch's vmap for internal optimization.
|
||||
// It doesn't affect the actual expand operation, so we can safely ignore it.
|
||||
|
||||
paddle::Tensor pd_tensor = self._PD_GetInner();
|
||||
|
||||
// Target sizes - convert to vector
|
||||
std::vector<int64_t> target_size_vec(size.begin(), size.end());
|
||||
auto target_rank = target_size_vec.size();
|
||||
auto input_dims = pd_tensor.dims();
|
||||
auto input_rank = static_cast<size_t>(input_dims.size());
|
||||
|
||||
// PyTorch's expand uses right-alignment semantics:
|
||||
// - For 1D tensor expand to 2D: {3}.expand({3,4}) treats input as {3,1},
|
||||
// expands to {3,4}
|
||||
// - Non-singleton dimensions are preserved, singleton dimensions (1) can
|
||||
// expand
|
||||
//
|
||||
// For example:
|
||||
// {3}.expand({3, 4}) -> input {3} becomes {3, 1} implicitly
|
||||
// then expand: dim 0: 3 stays 3, dim 1: 1 -> 4 -> result {3, 4}
|
||||
|
||||
if (input_rank < target_rank) {
|
||||
// Add leading 1s to right-align with target shape (PyTorch behavior)
|
||||
// Input {1, 2}, target {2, 3, 2} -> reshape to {1, 1, 2}
|
||||
std::vector<int64_t> reshape_vec(target_rank, 1);
|
||||
for (size_t i = 0; i < input_rank; ++i) {
|
||||
reshape_vec[target_rank - input_rank + i] = input_dims[i];
|
||||
}
|
||||
|
||||
// Check if Paddle's expand can handle this right-aligned shape
|
||||
// Paddle allows: input[i] == 1 (can expand), or input[i] == target[i]
|
||||
// (match)
|
||||
bool can_use_paddle_expand = true;
|
||||
size_t fail_dim = 0;
|
||||
for (size_t i = 0; i < target_rank; ++i) {
|
||||
bool dim_can_expand = (reshape_vec[i] == 1);
|
||||
bool dim_is_matching = (reshape_vec[i] == target_size_vec[i]);
|
||||
if (!dim_can_expand && !dim_is_matching) {
|
||||
can_use_paddle_expand = false;
|
||||
fail_dim = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (can_use_paddle_expand) {
|
||||
// Reshape to right-aligned shape, then expand
|
||||
paddle::Tensor reshaped =
|
||||
paddle::experimental::reshape(pd_tensor, phi::IntArray(reshape_vec));
|
||||
paddle::Tensor result = paddle::experimental::expand(
|
||||
reshaped, phi::IntArray(target_size_vec));
|
||||
return Tensor(result);
|
||||
}
|
||||
|
||||
PD_THROW("expand(): the expanded size of the tensor (",
|
||||
target_size_vec[fail_dim],
|
||||
") must match the existing size (",
|
||||
reshape_vec[fail_dim],
|
||||
") at non-singleton dimension ",
|
||||
fail_dim,
|
||||
".");
|
||||
} else if (input_rank == target_rank) {
|
||||
// Same rank - check if we can use expand directly
|
||||
bool can_use_paddle_expand = true;
|
||||
size_t fail_dim = 0;
|
||||
for (size_t i = 0; i < target_rank; ++i) {
|
||||
auto in_size = input_dims[i];
|
||||
auto target_size = target_size_vec[i];
|
||||
if (in_size != 1 && in_size != target_size) {
|
||||
can_use_paddle_expand = false;
|
||||
fail_dim = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (can_use_paddle_expand) {
|
||||
paddle::Tensor result = paddle::experimental::expand(
|
||||
pd_tensor, phi::IntArray(target_size_vec));
|
||||
return Tensor(result);
|
||||
}
|
||||
|
||||
PD_THROW("expand(): the expanded size of the tensor (",
|
||||
target_size_vec[fail_dim],
|
||||
") must match the existing size (",
|
||||
input_dims[fail_dim],
|
||||
") at non-singleton dimension ",
|
||||
fail_dim,
|
||||
".");
|
||||
} else {
|
||||
PD_THROW("expand(): the number of sizes provided (",
|
||||
target_rank,
|
||||
") must be greater or equal to the number of dimensions in the "
|
||||
"tensor (",
|
||||
input_rank,
|
||||
").");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
|
||||
namespace at {
|
||||
|
||||
// Member function: Tensor::expand
|
||||
inline Tensor Tensor::expand(at::IntArrayRef size, bool implicit) const {
|
||||
return at::expand(*this, size, implicit);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) 2026 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 <ATen/core/Tensor.h>
|
||||
#include <ATen/ops/expand.h>
|
||||
|
||||
namespace at {
|
||||
|
||||
inline Tensor Tensor::expand_as(const Tensor& other) const {
|
||||
return at::expand(*this, other.sizes());
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,106 @@
|
||||
// Copyright (c) 2026 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 <ATen/core/Tensor.h>
|
||||
#include <c10/core/TensorOptions.h>
|
||||
#include <utils/pinned_place.h>
|
||||
#include <optional>
|
||||
|
||||
#include "paddle/phi/api/include/api.h"
|
||||
#include "paddle/phi/common/place.h"
|
||||
|
||||
namespace at {
|
||||
|
||||
// eye(n) — n×n identity matrix
|
||||
inline at::Tensor eye(int64_t n, at::TensorOptions options = {}) {
|
||||
if (options.pinned_memory()) {
|
||||
// Pinning memory is only supported for CPU tensors
|
||||
if (options.has_device() && !options.device().is_cpu()) {
|
||||
PD_THROW(
|
||||
"pin_memory=true requires device to be CPU, but got non-CPU device");
|
||||
}
|
||||
phi::Place base_place = options._PD_GetPlace();
|
||||
phi::Place pinned_place = compat::_PD_GetCreatePinnedPlace(base_place);
|
||||
auto dense = paddle::experimental::eye(
|
||||
n,
|
||||
/*num_columns=*/-1,
|
||||
compat::_PD_AtenScalarTypeToPhiDataType(options.dtype()),
|
||||
phi::CPUPlace());
|
||||
return dense.copy_to(pinned_place, /*blocking=*/true);
|
||||
}
|
||||
return paddle::experimental::eye(
|
||||
n,
|
||||
/*num_columns=*/-1,
|
||||
compat::_PD_AtenScalarTypeToPhiDataType(options.dtype()),
|
||||
options._PD_GetPlace());
|
||||
}
|
||||
|
||||
// eye(n, m) — n×m identity-like matrix
|
||||
inline at::Tensor eye(int64_t n, int64_t m, at::TensorOptions options = {}) {
|
||||
if (options.pinned_memory()) {
|
||||
// Pinning memory is only supported for CPU tensors
|
||||
if (options.has_device() && !options.device().is_cpu()) {
|
||||
PD_THROW(
|
||||
"pin_memory=true requires device to be CPU, but got non-CPU device");
|
||||
}
|
||||
phi::Place base_place = options._PD_GetPlace();
|
||||
phi::Place pinned_place = compat::_PD_GetCreatePinnedPlace(base_place);
|
||||
auto dense = paddle::experimental::eye(
|
||||
n,
|
||||
m,
|
||||
compat::_PD_AtenScalarTypeToPhiDataType(options.dtype()),
|
||||
phi::CPUPlace());
|
||||
return dense.copy_to(pinned_place, /*blocking=*/true);
|
||||
}
|
||||
return paddle::experimental::eye(
|
||||
n,
|
||||
m,
|
||||
compat::_PD_AtenScalarTypeToPhiDataType(options.dtype()),
|
||||
options._PD_GetPlace());
|
||||
}
|
||||
|
||||
// eye(n, dtype, layout, device, pin_memory)
|
||||
inline at::Tensor eye(int64_t n,
|
||||
::std::optional<at::ScalarType> dtype,
|
||||
::std::optional<at::Layout> layout,
|
||||
::std::optional<at::Device> device,
|
||||
::std::optional<bool> pin_memory) {
|
||||
PD_CHECK(!layout.has_value(), "`layout` is not supported now.");
|
||||
auto options =
|
||||
at::TensorOptions()
|
||||
.dtype(dtype.value_or(c10::get_default_dtype_as_scalartype()))
|
||||
.device(device.value_or(at::kCPU))
|
||||
.pinned_memory(pin_memory);
|
||||
return eye(n, options);
|
||||
}
|
||||
|
||||
// eye(n, m, dtype, layout, device, pin_memory)
|
||||
inline at::Tensor eye(int64_t n,
|
||||
int64_t m,
|
||||
::std::optional<at::ScalarType> dtype,
|
||||
::std::optional<at::Layout> layout,
|
||||
::std::optional<at::Device> device,
|
||||
::std::optional<bool> pin_memory) {
|
||||
PD_CHECK(!layout.has_value(), "`layout` is not supported now.");
|
||||
auto options =
|
||||
at::TensorOptions()
|
||||
.dtype(dtype.value_or(c10::get_default_dtype_as_scalartype()))
|
||||
.device(device.value_or(at::kCPU))
|
||||
.pinned_memory(pin_memory);
|
||||
return eye(n, m, options);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) 2026 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 <ATen/core/Tensor.h>
|
||||
|
||||
namespace at {
|
||||
|
||||
inline at::Tensor flatten(const at::Tensor& self,
|
||||
int64_t start_dim = 0,
|
||||
int64_t end_dim = -1) {
|
||||
return Tensor(paddle::experimental::flatten(self._PD_GetInner(),
|
||||
static_cast<int>(start_dim),
|
||||
static_cast<int>(end_dim)));
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
|
||||
namespace at {
|
||||
|
||||
inline at::Tensor Tensor::flatten(int64_t start_dim, int64_t end_dim) const {
|
||||
return at::flatten(*this, start_dim, end_dim);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,219 @@
|
||||
// Copyright (c) 2025 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 <ATen/core/Tensor.h>
|
||||
|
||||
#include "paddle/phi/api/include/tensor_utils.h"
|
||||
namespace at {
|
||||
|
||||
namespace detail {
|
||||
|
||||
inline void noopDelete(void* /*unused*/) {}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
class TensorMaker {
|
||||
friend TensorMaker for_blob(void* data, IntArrayRef sizes) noexcept;
|
||||
|
||||
public:
|
||||
using ContextDeleter = DeleterFnPtr;
|
||||
|
||||
TensorMaker& strides(OptionalIntArrayRef value) noexcept {
|
||||
strides_ = value;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
TensorMaker& storage_offset(std::optional<int64_t> value) noexcept {
|
||||
storage_offset_ = value;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
TensorMaker& deleter(std::function<void(void*)> value) noexcept {
|
||||
deleter_ = std::move(value);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
TensorMaker& context(void* value, ContextDeleter deleter = nullptr) noexcept {
|
||||
ctx_ = std::unique_ptr<void, ContextDeleter>{
|
||||
value, deleter != nullptr ? deleter : detail::noopDelete};
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
TensorMaker& target_device(std::optional<Device> value) noexcept {
|
||||
device_ = value;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
TensorMaker& options(TensorOptions value) noexcept {
|
||||
opts_ = value;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
TensorMaker& resizeable_storage() noexcept {
|
||||
resizeable_ = true;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Tensor make_tensor() {
|
||||
PD_CHECK(!deleter_ || !ctx_,
|
||||
"The deleter and context arguments are mutually exclusive.");
|
||||
|
||||
PD_CHECK(!storage_offset_.has_value() || storage_offset_.value() == 0,
|
||||
"storage_offset` should be zero.");
|
||||
|
||||
if (device_.has_value() && opts_.has_device() &&
|
||||
opts_.device().has_index()) {
|
||||
PD_CHECK(opts_.device() == *device_,
|
||||
"Specified device ",
|
||||
opts_.device(),
|
||||
" does not match device of data ",
|
||||
*device_);
|
||||
}
|
||||
|
||||
phi::Place pd_place;
|
||||
if (device_.has_value()) {
|
||||
pd_place = device_->_PD_GetInner();
|
||||
} else if (opts_.has_device() && opts_.device().has_index()) {
|
||||
pd_place = opts_.device()._PD_GetInner();
|
||||
} else {
|
||||
pd_place = phi::Place(); // UNDEFINED → auto-detect inside from_blob
|
||||
}
|
||||
|
||||
// Build paddle deleter: prefer explicit deleter_, then wrap ctx_ so its
|
||||
// lifetime is tied to the tensor allocation.
|
||||
paddle::Deleter pd_deleter = nullptr;
|
||||
if (deleter_) {
|
||||
pd_deleter = deleter_;
|
||||
} else if (ctx_) {
|
||||
// shared_ptr takes ownership of the context and calls its deleter when
|
||||
// the last copy (held in the lambda) is destroyed.
|
||||
auto shared_ctx =
|
||||
std::shared_ptr<void>(ctx_.release(), ctx_.get_deleter());
|
||||
pd_deleter = [shared_ctx](void* /*data*/) {};
|
||||
}
|
||||
|
||||
if (strides_.has_value()) {
|
||||
return paddle::from_blob(
|
||||
data_,
|
||||
sizes_._PD_ToPaddleIntArray(),
|
||||
strides_.value()._PD_ToPaddleIntArray(),
|
||||
compat::_PD_AtenScalarTypeToPhiDataType(opts_.dtype()),
|
||||
phi::DataLayout::NCHW,
|
||||
pd_place,
|
||||
pd_deleter);
|
||||
} else {
|
||||
return paddle::from_blob(
|
||||
data_,
|
||||
sizes_._PD_ToPaddleIntArray(),
|
||||
compat::_PD_AtenScalarTypeToPhiDataType(opts_.dtype()),
|
||||
phi::DataLayout::NCHW,
|
||||
pd_place,
|
||||
pd_deleter);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
explicit TensorMaker(void* data, IntArrayRef sizes) noexcept
|
||||
: data_{data}, sizes_{sizes} {}
|
||||
|
||||
std::size_t computeStorageSize() const noexcept;
|
||||
|
||||
DataPtr makeDataPtrFromDeleter() noexcept;
|
||||
|
||||
DataPtr makeDataPtrFromContext() noexcept;
|
||||
|
||||
IntArrayRef makeTempSizes() const noexcept;
|
||||
|
||||
void* data_;
|
||||
IntArrayRef sizes_;
|
||||
OptionalIntArrayRef strides_;
|
||||
std::optional<int64_t> storage_offset_;
|
||||
std::function<void(void*)> deleter_;
|
||||
std::unique_ptr<void, ContextDeleter> ctx_{nullptr, detail::noopDelete};
|
||||
std::optional<Device> device_;
|
||||
TensorOptions opts_;
|
||||
bool resizeable_{};
|
||||
};
|
||||
|
||||
inline TensorMaker for_blob(void* data, IntArrayRef sizes) noexcept {
|
||||
return TensorMaker{data, sizes};
|
||||
}
|
||||
|
||||
inline Tensor from_blob(
|
||||
void* data,
|
||||
IntArrayRef sizes,
|
||||
IntArrayRef strides,
|
||||
const std::function<void(void*)>& deleter,
|
||||
const TensorOptions& options = {},
|
||||
const std::optional<Device> target_device = std::nullopt) {
|
||||
return for_blob(data, sizes)
|
||||
.strides(strides)
|
||||
.deleter(deleter)
|
||||
.options(options)
|
||||
.target_device(target_device)
|
||||
.make_tensor();
|
||||
}
|
||||
|
||||
inline Tensor from_blob(
|
||||
void* data,
|
||||
IntArrayRef sizes,
|
||||
IntArrayRef strides,
|
||||
int64_t storage_offset,
|
||||
const std::function<void(void*)>& deleter,
|
||||
const TensorOptions& options = {},
|
||||
const std::optional<Device> target_device = std::nullopt) {
|
||||
return for_blob(data, sizes)
|
||||
.strides(strides)
|
||||
.storage_offset(storage_offset)
|
||||
.deleter(deleter)
|
||||
.options(options)
|
||||
.target_device(target_device)
|
||||
.make_tensor();
|
||||
}
|
||||
|
||||
inline Tensor from_blob(
|
||||
void* data,
|
||||
IntArrayRef sizes,
|
||||
std::function<void(void*)> deleter,
|
||||
const TensorOptions& options = {},
|
||||
const std::optional<Device> target_device = std::nullopt) {
|
||||
return for_blob(data, sizes)
|
||||
.deleter(std::move(deleter))
|
||||
.options(options)
|
||||
.target_device(target_device)
|
||||
.make_tensor();
|
||||
}
|
||||
|
||||
inline Tensor from_blob(void* data,
|
||||
IntArrayRef sizes,
|
||||
IntArrayRef strides,
|
||||
const TensorOptions& options = {}) {
|
||||
return for_blob(data, sizes).strides(strides).options(options).make_tensor();
|
||||
}
|
||||
|
||||
inline Tensor from_blob(void* data,
|
||||
IntArrayRef sizes,
|
||||
const TensorOptions& options = {}) {
|
||||
return for_blob(data, sizes).options(options).make_tensor();
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,104 @@
|
||||
// Copyright (c) 2025 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 <ATen/core/Tensor.h>
|
||||
#include <c10/core/SymIntArrayRef.h>
|
||||
#include <c10/core/TensorOptions.h>
|
||||
#include <utils/pinned_place.h>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
#include "paddle/phi/api/include/api.h"
|
||||
#include "paddle/phi/common/place.h"
|
||||
|
||||
namespace at {
|
||||
|
||||
inline at::Tensor full(at::IntArrayRef size,
|
||||
const at::Scalar& fill_value,
|
||||
at::TensorOptions options = {}) {
|
||||
if (options.pinned_memory()) {
|
||||
// Pinning memory is only supported for CPU tensors
|
||||
if (options.has_device() && !options.device().is_cpu()) {
|
||||
PD_THROW(
|
||||
"pin_memory=true requires device to be CPU, but got non-CPU device");
|
||||
}
|
||||
phi::Place base_place = options._PD_GetPlace();
|
||||
phi::Place pinned_place = compat::_PD_GetCreatePinnedPlace(base_place);
|
||||
auto dense = paddle::experimental::full(
|
||||
size._PD_ToPaddleIntArray(),
|
||||
fill_value,
|
||||
compat::_PD_AtenScalarTypeToPhiDataType(options.dtype()),
|
||||
phi::CPUPlace());
|
||||
return dense.copy_to(pinned_place, /*blocking=*/true);
|
||||
}
|
||||
return paddle::experimental::full(
|
||||
size._PD_ToPaddleIntArray(),
|
||||
fill_value,
|
||||
compat::_PD_AtenScalarTypeToPhiDataType(options.dtype()),
|
||||
options._PD_GetPlace());
|
||||
}
|
||||
|
||||
inline at::Tensor full(at::IntArrayRef size,
|
||||
const at::Scalar& fill_value,
|
||||
::std::optional<at::ScalarType> dtype,
|
||||
::std::optional<at::Layout> layout,
|
||||
::std::optional<at::Device> device,
|
||||
::std::optional<bool> pin_memory) {
|
||||
PD_CHECK(!layout.has_value(), "`layout` is not supported now.");
|
||||
auto options =
|
||||
at::TensorOptions()
|
||||
.dtype(dtype.value_or(c10::get_default_dtype_as_scalartype()))
|
||||
.device(device.value_or(at::kCPU))
|
||||
.pinned_memory(pin_memory);
|
||||
return full(size, fill_value, options);
|
||||
}
|
||||
|
||||
inline at::Tensor full_symint(c10::SymIntArrayRef size,
|
||||
const at::Scalar& fill_value,
|
||||
at::TensorOptions options = {}) {
|
||||
if (options.pinned_memory()) {
|
||||
phi::Place base_place = options._PD_GetPlace();
|
||||
phi::Place pinned_place = compat::_PD_GetCreatePinnedPlace(base_place);
|
||||
auto dense = paddle::experimental::full(
|
||||
size._PD_ToPaddleIntArray(),
|
||||
fill_value,
|
||||
compat::_PD_AtenScalarTypeToPhiDataType(options.dtype()),
|
||||
phi::CPUPlace());
|
||||
return dense.copy_to(pinned_place, /*blocking=*/true);
|
||||
}
|
||||
return paddle::experimental::full(
|
||||
size._PD_ToPaddleIntArray(),
|
||||
fill_value,
|
||||
compat::_PD_AtenScalarTypeToPhiDataType(options.dtype()),
|
||||
options._PD_GetPlace());
|
||||
}
|
||||
|
||||
inline at::Tensor full_symint(c10::SymIntArrayRef size,
|
||||
const at::Scalar& fill_value,
|
||||
::std::optional<at::ScalarType> dtype,
|
||||
::std::optional<at::Layout> layout,
|
||||
::std::optional<at::Device> device,
|
||||
::std::optional<bool> pin_memory) {
|
||||
PD_CHECK(!layout.has_value(), "`layout` is not supported now.");
|
||||
auto options =
|
||||
at::TensorOptions()
|
||||
.dtype(dtype.value_or(c10::get_default_dtype_as_scalartype()))
|
||||
.device(device.value_or(at::kCPU))
|
||||
.pinned_memory(pin_memory);
|
||||
return full_symint(size, fill_value, options);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright (c) 2026 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 <ATen/core/Tensor.h>
|
||||
#include <ATen/ops/tensor_split.h>
|
||||
|
||||
namespace at {
|
||||
|
||||
inline std::vector<at::Tensor> hsplit(const at::Tensor& self,
|
||||
int64_t sections) {
|
||||
// For 1D tensors, split along dim 0; otherwise split along dim 1
|
||||
int64_t dim = self._PD_GetInner().dims().size() == 1 ? 0 : 1;
|
||||
return at::tensor_split(self, sections, dim);
|
||||
}
|
||||
|
||||
inline std::vector<at::Tensor> hsplit(const at::Tensor& self,
|
||||
at::IntArrayRef indices) {
|
||||
int64_t dim = self._PD_GetInner().dims().size() == 1 ? 0 : 1;
|
||||
return at::tensor_split(self, indices, dim);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
|
||||
namespace at {
|
||||
|
||||
inline std::vector<at::Tensor> Tensor::hsplit(int64_t sections) const {
|
||||
return at::hsplit(*this, sections);
|
||||
}
|
||||
|
||||
inline std::vector<at::Tensor> Tensor::hsplit(at::IntArrayRef indices) const {
|
||||
return at::hsplit(*this, indices);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,203 @@
|
||||
// Copyright (c) 2026 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 <ATen/TensorIndexing.h>
|
||||
#include <ATen/core/Tensor.h>
|
||||
#include <c10/core/List.h>
|
||||
|
||||
namespace at::indexing {
|
||||
|
||||
inline TensorIndex::TensorIndex(const at::Tensor& tensor)
|
||||
: tensor_(std::make_shared<at::Tensor>(tensor)),
|
||||
type_(TensorIndexType::Tensor) {}
|
||||
|
||||
inline const at::Tensor& TensorIndex::tensor() const { return *tensor_; }
|
||||
|
||||
} // namespace at::indexing
|
||||
|
||||
namespace at::detail {
|
||||
|
||||
inline bool _PD_is_full_slice(const at::indexing::Slice& slice) {
|
||||
return static_cast<int64_t>(slice.start()) == 0 &&
|
||||
static_cast<int64_t>(slice.stop()) == at::indexing::INDEX_MAX &&
|
||||
static_cast<int64_t>(slice.step()) == 1;
|
||||
}
|
||||
|
||||
inline at::Tensor _PD_apply_tensor_index(
|
||||
const at::Tensor& self, ArrayRef<at::indexing::TensorIndex> indices) {
|
||||
int64_t output_dim = 0;
|
||||
int tensor_index_count = 0;
|
||||
at::Tensor result = self;
|
||||
|
||||
for (const auto& index : indices) {
|
||||
if (index.is_tensor()) {
|
||||
++tensor_index_count;
|
||||
PD_CHECK(tensor_index_count == 1,
|
||||
"Multiple tensor indices mixed with None/Slice are not "
|
||||
"supported yet.");
|
||||
result = paddle::experimental::index_select(
|
||||
result._PD_GetInner(), index.tensor()._PD_GetInner(), output_dim);
|
||||
++output_dim;
|
||||
} else if (index.is_none()) {
|
||||
result =
|
||||
paddle::experimental::unsqueeze(result._PD_GetInner(), {output_dim});
|
||||
++output_dim;
|
||||
} else if (index.is_slice()) {
|
||||
const auto& slice = index.slice();
|
||||
PD_CHECK(_PD_is_full_slice(slice),
|
||||
"Only full Slice() is supported when mixed with tensor/None "
|
||||
"indices.");
|
||||
++output_dim;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline at::Tensor _PD_index_tensor_indices(
|
||||
const at::Tensor& self, ArrayRef<at::indexing::TensorIndex> indices) {
|
||||
if (indices.size() == 0) {
|
||||
PD_THROW("index() cannot be called with an empty index list");
|
||||
}
|
||||
|
||||
bool has_slice = false;
|
||||
bool has_tensor_like = false;
|
||||
for (const auto& index : indices) {
|
||||
has_slice = has_slice || index.is_slice();
|
||||
has_tensor_like = has_tensor_like || index.is_tensor() || index.is_none();
|
||||
PD_CHECK(!index.is_ellipsis(), "Ellipsis index is not supported yet.");
|
||||
PD_CHECK(!index.is_integer(), "Integer index is not supported yet.");
|
||||
PD_CHECK(!index.is_boolean(), "Boolean index is not supported yet.");
|
||||
}
|
||||
|
||||
if (has_slice && !has_tensor_like) {
|
||||
std::vector<int64_t> axes;
|
||||
std::vector<int64_t> starts;
|
||||
std::vector<int64_t> ends;
|
||||
std::vector<int64_t> strides;
|
||||
axes.reserve(indices.size());
|
||||
starts.reserve(indices.size());
|
||||
ends.reserve(indices.size());
|
||||
strides.reserve(indices.size());
|
||||
|
||||
int64_t dim = 0;
|
||||
for (const auto& index : indices) {
|
||||
const auto& slice = index.slice();
|
||||
axes.push_back(dim++);
|
||||
starts.push_back(static_cast<int64_t>(slice.start()));
|
||||
ends.push_back(static_cast<int64_t>(slice.stop()));
|
||||
strides.push_back(static_cast<int64_t>(slice.step()));
|
||||
}
|
||||
|
||||
return paddle::experimental::slice(
|
||||
self._PD_GetInner(), axes, starts, ends, strides, {});
|
||||
}
|
||||
|
||||
if (has_slice) {
|
||||
return _PD_apply_tensor_index(self, indices);
|
||||
}
|
||||
|
||||
c10::List<::std::optional<at::Tensor>> tensor_indices;
|
||||
for (const auto& index : indices) {
|
||||
if (index.is_none()) {
|
||||
tensor_indices.push_back(::std::nullopt);
|
||||
} else if (index.is_tensor()) {
|
||||
tensor_indices.push_back(index.tensor());
|
||||
}
|
||||
}
|
||||
return self.index(tensor_indices);
|
||||
}
|
||||
|
||||
} // namespace at::detail
|
||||
|
||||
namespace at {
|
||||
|
||||
inline at::Tensor index(const at::Tensor& self,
|
||||
const c10::List<::std::optional<at::Tensor>>& indices) {
|
||||
if (indices.empty()) {
|
||||
return self;
|
||||
}
|
||||
|
||||
bool all_none = true;
|
||||
for (const auto& idx : indices) {
|
||||
if (idx.has_value()) {
|
||||
all_none = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (all_none) {
|
||||
return self;
|
||||
}
|
||||
|
||||
std::vector<paddle::Tensor> pd_indices;
|
||||
std::vector<bool> has_index(indices.size(), false);
|
||||
pd_indices.reserve(indices.size());
|
||||
|
||||
for (size_t i = 0; i < indices.size(); ++i) {
|
||||
if (indices[i].has_value()) {
|
||||
pd_indices.push_back(indices[i].value()._PD_GetInner());
|
||||
has_index[i] = true;
|
||||
} else {
|
||||
pd_indices.push_back(paddle::Tensor());
|
||||
}
|
||||
}
|
||||
|
||||
int non_none_count = 0;
|
||||
size_t first_non_none = 0;
|
||||
for (size_t i = 0; i < has_index.size(); ++i) {
|
||||
if (has_index[i]) {
|
||||
non_none_count++;
|
||||
first_non_none = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (non_none_count == 1 && first_non_none == 0) {
|
||||
return paddle::experimental::index_select(
|
||||
self._PD_GetInner(), pd_indices[0], 0);
|
||||
}
|
||||
|
||||
if (non_none_count == static_cast<int>(indices.size())) {
|
||||
auto stacked_indices = paddle::experimental::stack(pd_indices, -1);
|
||||
return paddle::experimental::gather_nd(self._PD_GetInner(),
|
||||
stacked_indices);
|
||||
}
|
||||
|
||||
auto self_dims = self._PD_GetInner().dims();
|
||||
int self_rank = self_dims.size();
|
||||
at::Tensor result = self;
|
||||
|
||||
for (size_t i = 0; i < indices.size() && i < static_cast<size_t>(self_rank);
|
||||
++i) {
|
||||
if (has_index[i]) {
|
||||
paddle::Tensor pd_result = result._PD_GetInner();
|
||||
result = paddle::experimental::index_select(
|
||||
pd_result, pd_indices[i], static_cast<int>(i));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
|
||||
namespace at {
|
||||
|
||||
inline at::Tensor Tensor::index(
|
||||
ArrayRef<at::indexing::TensorIndex> indices) const {
|
||||
return at::detail::_PD_index_tensor_indices(*this, indices);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,205 @@
|
||||
// Copyright (c) 2026 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 <ATen/core/Tensor.h>
|
||||
#include <ATen/ops/index.h>
|
||||
#include <c10/core/List.h>
|
||||
#include <c10/core/Scalar.h>
|
||||
#include <vector>
|
||||
|
||||
#include "paddle/phi/api/include/api.h"
|
||||
|
||||
namespace at::detail {
|
||||
|
||||
inline std::vector<at::Tensor> _PD_convert_indices_list(
|
||||
const c10::List<::std::optional<at::Tensor>>& indices) {
|
||||
std::vector<at::Tensor> result;
|
||||
result.reserve(indices.size());
|
||||
for (const auto& idx : indices) {
|
||||
if (idx.has_value()) {
|
||||
result.push_back(idx.value());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
inline c10::List<::std::optional<at::Tensor>> _PD_convert_tensor_index_list(
|
||||
ArrayRef<at::indexing::TensorIndex> indices) {
|
||||
c10::List<::std::optional<at::Tensor>> result;
|
||||
for (const auto& index : indices) {
|
||||
PD_CHECK(!index.is_ellipsis(), "Ellipsis index is not supported yet.");
|
||||
PD_CHECK(!index.is_integer(), "Integer index is not supported yet.");
|
||||
PD_CHECK(!index.is_boolean(), "Boolean index is not supported yet.");
|
||||
if (index.is_slice()) {
|
||||
PD_CHECK(_PD_is_full_slice(index.slice()),
|
||||
"Only full Slice() is supported in index_put_ TensorIndex "
|
||||
"paths.");
|
||||
} else if (index.is_tensor()) {
|
||||
result.push_back(index.tensor());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
inline at::Tensor _PD_squeeze_newaxis_value(
|
||||
const at::Tensor& values, ArrayRef<at::indexing::TensorIndex> indices) {
|
||||
std::vector<int64_t> value_shape(values.sizes().begin(),
|
||||
values.sizes().end());
|
||||
size_t value_dim = 0;
|
||||
bool changed = false;
|
||||
|
||||
for (const auto& index : indices) {
|
||||
if (index.is_none()) {
|
||||
if (!value_shape.empty()) {
|
||||
PD_CHECK(value_dim < value_shape.size(),
|
||||
"index_put_ value rank is too small for None index.");
|
||||
PD_CHECK(value_shape[value_dim] == 1,
|
||||
"index_put_ expected value dimension inserted by None to "
|
||||
"have size 1, but got ",
|
||||
value_shape[value_dim],
|
||||
".");
|
||||
value_shape.erase(value_shape.begin() + value_dim);
|
||||
changed = true;
|
||||
}
|
||||
} else if (index.is_tensor()) {
|
||||
if (!value_shape.empty()) {
|
||||
++value_dim;
|
||||
}
|
||||
} else if (index.is_slice()) {
|
||||
PD_CHECK(_PD_is_full_slice(index.slice()),
|
||||
"Only full Slice() is supported in index_put_ TensorIndex "
|
||||
"paths.");
|
||||
if (!value_shape.empty()) {
|
||||
++value_dim;
|
||||
}
|
||||
} else {
|
||||
PD_CHECK(!index.is_ellipsis(), "Ellipsis index is not supported yet.");
|
||||
PD_CHECK(!index.is_integer(), "Integer index is not supported yet.");
|
||||
PD_CHECK(!index.is_boolean(), "Boolean index is not supported yet.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!changed) {
|
||||
return values;
|
||||
}
|
||||
return paddle::experimental::reshape(values._PD_GetInner(),
|
||||
phi::IntArray(value_shape));
|
||||
}
|
||||
|
||||
} // namespace at::detail
|
||||
|
||||
namespace at {
|
||||
|
||||
// index_put_: Set values at specified indices (in-place)
|
||||
inline at::Tensor& index_put_(
|
||||
at::Tensor& self, // NOLINT(runtime/references)
|
||||
const c10::List<::std::optional<at::Tensor>>& indices,
|
||||
const at::Tensor& values,
|
||||
bool accumulate = false) {
|
||||
std::vector<paddle::Tensor> pd_indices;
|
||||
pd_indices.reserve(indices.size());
|
||||
for (const auto& idx : indices) {
|
||||
if (idx.has_value()) {
|
||||
pd_indices.push_back(idx.value()._PD_GetInner());
|
||||
}
|
||||
}
|
||||
|
||||
paddle::experimental::index_put_(
|
||||
self._PD_GetInner(), pd_indices, values._PD_GetInner(), accumulate);
|
||||
return self;
|
||||
}
|
||||
|
||||
// index_put: Non-inplace version
|
||||
inline at::Tensor index_put(
|
||||
const at::Tensor& self,
|
||||
const c10::List<::std::optional<at::Tensor>>& indices,
|
||||
const at::Tensor& values,
|
||||
bool accumulate = false) {
|
||||
std::vector<paddle::Tensor> pd_indices;
|
||||
pd_indices.reserve(indices.size());
|
||||
for (const auto& idx : indices) {
|
||||
if (idx.has_value()) {
|
||||
pd_indices.push_back(idx.value()._PD_GetInner());
|
||||
}
|
||||
}
|
||||
|
||||
return paddle::experimental::index_put(
|
||||
self._PD_GetInner(), pd_indices, values._PD_GetInner(), accumulate);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
|
||||
namespace at {
|
||||
|
||||
inline at::Tensor Tensor::index(
|
||||
const c10::List<::std::optional<at::Tensor>>& indices) const {
|
||||
return at::index(*this, indices);
|
||||
}
|
||||
|
||||
inline at::Tensor& Tensor::index_put_(
|
||||
const c10::List<::std::optional<at::Tensor>>& indices,
|
||||
const at::Tensor& values,
|
||||
bool accumulate) const {
|
||||
return at::index_put_(
|
||||
const_cast<at::Tensor&>(*this), indices, values, accumulate);
|
||||
}
|
||||
|
||||
inline at::Tensor& Tensor::index_put_(
|
||||
ArrayRef<at::indexing::TensorIndex> indices, Tensor const& rhs) {
|
||||
auto tensor_indices = detail::_PD_convert_tensor_index_list(indices);
|
||||
at::Tensor values = detail::_PD_squeeze_newaxis_value(rhs, indices);
|
||||
if (tensor_indices.empty()) {
|
||||
return copy_(values);
|
||||
}
|
||||
return index_put_(tensor_indices, values);
|
||||
}
|
||||
|
||||
inline at::Tensor& Tensor::index_put_(
|
||||
ArrayRef<at::indexing::TensorIndex> indices, const Scalar& v) {
|
||||
auto tensor_indices = detail::_PD_convert_tensor_index_list(indices);
|
||||
if (tensor_indices.empty()) {
|
||||
std::vector<int64_t> value_shape(this->sizes().begin(),
|
||||
this->sizes().end());
|
||||
auto scalar_tensor =
|
||||
at::Tensor(paddle::experimental::full(phi::IntArray(value_shape),
|
||||
phi::Scalar(v.to<double>()),
|
||||
this->_PD_GetInner().dtype()));
|
||||
return copy_(scalar_tensor);
|
||||
}
|
||||
auto scalar_tensor = at::Tensor(paddle::experimental::full(
|
||||
{}, phi::Scalar(v.to<double>()), this->_PD_GetInner().dtype()));
|
||||
return index_put_(indices, scalar_tensor);
|
||||
}
|
||||
|
||||
inline at::Tensor& Tensor::index_put_(
|
||||
std::initializer_list<at::indexing::TensorIndex> indices,
|
||||
Tensor const& rhs) {
|
||||
return index_put_(ArrayRef<at::indexing::TensorIndex>(indices), rhs);
|
||||
}
|
||||
|
||||
inline at::Tensor& Tensor::index_put_(
|
||||
std::initializer_list<at::indexing::TensorIndex> indices, const Scalar& v) {
|
||||
return index_put_(ArrayRef<at::indexing::TensorIndex>(indices), v);
|
||||
}
|
||||
|
||||
inline at::Tensor Tensor::index_put(
|
||||
const c10::List<::std::optional<at::Tensor>>& indices,
|
||||
const at::Tensor& values,
|
||||
bool accumulate) const {
|
||||
return at::index_put(*this, indices, values, accumulate);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) 2026 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 <ATen/core/Tensor.h>
|
||||
#include "paddle/phi/core/sparse_coo_tensor.h"
|
||||
|
||||
namespace at {} // namespace at
|
||||
|
||||
namespace at {
|
||||
|
||||
inline bool Tensor::is_coalesced() const {
|
||||
PD_CHECK(tensor_.layout() == common::DataLayout::SPARSE_COO,
|
||||
"is_coalesced expected sparse coordinate tensor layout but got ",
|
||||
layout());
|
||||
auto sparse_coo_tensor =
|
||||
std::dynamic_pointer_cast<phi::SparseCooTensor>(tensor_.impl());
|
||||
return sparse_coo_tensor->coalesced();
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2026 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 <ATen/core/Tensor.h>
|
||||
#include <ATen/ops/_local_scalar_dense.h>
|
||||
|
||||
namespace at {} // namespace at
|
||||
|
||||
namespace at {
|
||||
|
||||
inline at::Scalar Tensor::item() const {
|
||||
auto numel = this->sym_numel();
|
||||
PD_CHECK(numel == 1,
|
||||
"a Tensor with ",
|
||||
numel,
|
||||
" elements cannot be converted to Scalar");
|
||||
if (this->is_sparse()) {
|
||||
if (this->_nnz() == 0) return Scalar(0);
|
||||
if (this->is_coalesced()) return at::_local_scalar_dense(this->_values());
|
||||
return at::_local_scalar_dense(this->_values().sum());
|
||||
} else {
|
||||
return _local_scalar_dense(*this);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T Tensor::item() const {
|
||||
return item().to<T>();
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) 2026 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 <ATen/core/Tensor.h>
|
||||
|
||||
namespace at {
|
||||
|
||||
inline at::Tensor masked_select(const at::Tensor& self,
|
||||
const at::Tensor& mask) {
|
||||
return Tensor(paddle::experimental::masked_select(self._PD_GetInner(),
|
||||
mask._PD_GetInner()));
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
|
||||
namespace at {
|
||||
|
||||
inline at::Tensor Tensor::masked_select(const at::Tensor& mask) const {
|
||||
return at::masked_select(*this, mask);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,115 @@
|
||||
// Copyright (c) 2026 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 <ATen/core/Tensor.h>
|
||||
|
||||
namespace at {
|
||||
|
||||
inline at::Tensor narrow(const at::Tensor& self,
|
||||
int64_t dim,
|
||||
int64_t start,
|
||||
int64_t length) {
|
||||
// Bounds checks matching PyTorch behavior
|
||||
PD_CHECK(self.dim() > 0, "narrow() cannot be applied to a 0-dim tensor.");
|
||||
PD_CHECK(length >= 0, "narrow(): length must be non-negative.");
|
||||
|
||||
// Normalize negative dim
|
||||
int64_t ndim = self.dim();
|
||||
if (dim < 0) dim += ndim;
|
||||
PD_CHECK(dim >= 0 && dim < ndim,
|
||||
"start out of range (expected to be in range of [",
|
||||
-ndim,
|
||||
", ",
|
||||
ndim - 1,
|
||||
"], but got ",
|
||||
dim,
|
||||
")");
|
||||
|
||||
int64_t cur_size = self.sizes()[dim];
|
||||
|
||||
// Wrap negative start (matching PyTorch: only wrap when start != cur_size)
|
||||
if (start < 0) {
|
||||
start = start + cur_size;
|
||||
}
|
||||
PD_CHECK(start <= cur_size - length,
|
||||
"start (",
|
||||
start,
|
||||
") + length (",
|
||||
length,
|
||||
") exceeds dimension size (",
|
||||
cur_size,
|
||||
").");
|
||||
|
||||
// Use slice to implement narrow: narrow(dim, start, length) is equivalent
|
||||
// to slice(dim, start, start + length)
|
||||
return Tensor(paddle::experimental::slice(
|
||||
self._PD_GetInner(), {dim}, {start}, {start + length}, {1}, {}));
|
||||
}
|
||||
|
||||
inline at::Tensor narrow_symint(const at::Tensor& self,
|
||||
int64_t dim,
|
||||
c10::SymInt start,
|
||||
c10::SymInt length) {
|
||||
return narrow(self, dim, start, length);
|
||||
}
|
||||
|
||||
inline at::Tensor narrow(const at::Tensor& self,
|
||||
int64_t dim,
|
||||
const at::Tensor& start,
|
||||
int64_t length) {
|
||||
// Extract scalar value from start tensor
|
||||
PD_CHECK(start.numel() == 1,
|
||||
"start must be a 0-dim tensor or 1-element tensor");
|
||||
int64_t start_val = start.item<int64_t>();
|
||||
return narrow(self, dim, start_val, length);
|
||||
}
|
||||
|
||||
inline at::Tensor narrow_symint(const at::Tensor& self,
|
||||
int64_t dim,
|
||||
const at::Tensor& start,
|
||||
c10::SymInt length) {
|
||||
return narrow(self, dim, start, length);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
|
||||
namespace at {
|
||||
|
||||
inline at::Tensor Tensor::narrow(int64_t dim,
|
||||
int64_t start,
|
||||
int64_t length) const {
|
||||
return at::narrow(*this, dim, start, length);
|
||||
}
|
||||
|
||||
inline at::Tensor Tensor::narrow_symint(int64_t dim,
|
||||
c10::SymInt start,
|
||||
c10::SymInt length) const {
|
||||
return at::narrow_symint(*this, dim, start, length);
|
||||
}
|
||||
|
||||
inline at::Tensor Tensor::narrow(int64_t dim,
|
||||
const at::Tensor& start,
|
||||
int64_t length) const {
|
||||
return at::narrow(*this, dim, start, length);
|
||||
}
|
||||
|
||||
inline at::Tensor Tensor::narrow_symint(int64_t dim,
|
||||
const at::Tensor& start,
|
||||
c10::SymInt length) const {
|
||||
return at::narrow_symint(*this, dim, start, length);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright (c) 2026 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 <ATen/core/Tensor.h>
|
||||
#include <ATen/ops/narrow.h>
|
||||
|
||||
namespace at {
|
||||
|
||||
inline at::Tensor narrow_copy(const at::Tensor& self,
|
||||
int64_t dim,
|
||||
int64_t start,
|
||||
int64_t length) {
|
||||
// narrow_copy returns a copy of the narrowed tensor
|
||||
return narrow(self, dim, start, length).clone();
|
||||
}
|
||||
|
||||
inline at::Tensor narrow_copy_symint(const at::Tensor& self,
|
||||
int64_t dim,
|
||||
c10::SymInt start,
|
||||
c10::SymInt length) {
|
||||
return narrow_copy(self, dim, start, length);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
|
||||
namespace at {
|
||||
|
||||
inline at::Tensor Tensor::narrow_copy(int64_t dim,
|
||||
int64_t start,
|
||||
int64_t length) const {
|
||||
return at::narrow_copy(*this, dim, start, length);
|
||||
}
|
||||
|
||||
inline at::Tensor Tensor::narrow_copy_symint(int64_t dim,
|
||||
c10::SymInt start,
|
||||
c10::SymInt length) const {
|
||||
return at::narrow_copy_symint(*this, dim, start, length);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) 2025 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 <ATen/core/Tensor.h>
|
||||
#include <c10/core/TensorOptions.h>
|
||||
#include <utils/pinned_place.h>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
#include "paddle/phi/api/include/api.h"
|
||||
#include "paddle/phi/common/place.h"
|
||||
|
||||
namespace at {
|
||||
|
||||
// Member function: Tensor::new_empty
|
||||
inline Tensor Tensor::new_empty(at::IntArrayRef size,
|
||||
at::TensorOptions options) const {
|
||||
caffe2::TypeMeta actual_dtype = options.dtype_opt().value_or(dtype());
|
||||
auto actual_device = options.device_opt().value_or(device());
|
||||
auto actual_pin_memory = options.pinned_memory();
|
||||
|
||||
auto pd_dtype = compat::_PD_AtenScalarTypeToPhiDataType(actual_dtype);
|
||||
auto pd_place = actual_device._PD_GetInner();
|
||||
|
||||
paddle::Tensor result;
|
||||
if (actual_pin_memory) {
|
||||
// Pinning memory is only supported for CPU tensors
|
||||
if (options.has_device() && !actual_device.is_cpu()) {
|
||||
PD_THROW(
|
||||
"pin_memory=true requires device to be CPU, but got non-CPU device");
|
||||
}
|
||||
phi::Place pinned_place = compat::_PD_GetCreatePinnedPlace(pd_place);
|
||||
auto dense_cpu = paddle::experimental::empty(
|
||||
size._PD_ToPaddleIntArray(), pd_dtype, phi::CPUPlace());
|
||||
result = dense_cpu.copy_to(pinned_place, /*blocking=*/true);
|
||||
} else {
|
||||
result = paddle::experimental::empty(
|
||||
size._PD_ToPaddleIntArray(), pd_dtype, pd_place);
|
||||
}
|
||||
return Tensor(result);
|
||||
}
|
||||
|
||||
inline Tensor Tensor::new_empty(at::IntArrayRef size,
|
||||
::std::optional<at::ScalarType> dtype,
|
||||
::std::optional<at::Layout>,
|
||||
::std::optional<at::Device> device,
|
||||
::std::optional<bool> pin_memory) const {
|
||||
auto options = at::TensorOptions()
|
||||
.dtype(dtype.value_or(this->scalar_type()))
|
||||
.device(device.value_or(this->device()))
|
||||
.pinned_memory(pin_memory);
|
||||
return new_empty(size, options);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,70 @@
|
||||
// Copyright (c) 2026 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 <ATen/core/Tensor.h>
|
||||
#include <c10/core/TensorOptions.h>
|
||||
#include <utils/pinned_place.h>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
#include "paddle/phi/api/include/api.h"
|
||||
#include "paddle/phi/common/place.h"
|
||||
|
||||
namespace at {
|
||||
|
||||
// Member function: Tensor::new_full
|
||||
inline Tensor Tensor::new_full(at::IntArrayRef size,
|
||||
const at::Scalar& fill_value,
|
||||
at::TensorOptions options) const {
|
||||
caffe2::TypeMeta actual_dtype = options.dtype_opt().value_or(dtype());
|
||||
auto actual_device = options.device_opt().value_or(device());
|
||||
auto actual_pin_memory = options.pinned_memory();
|
||||
|
||||
auto pd_dtype = compat::_PD_AtenScalarTypeToPhiDataType(actual_dtype);
|
||||
auto pd_place = actual_device._PD_GetInner();
|
||||
|
||||
paddle::Tensor result;
|
||||
if (actual_pin_memory) {
|
||||
// Pinning memory is only supported for CPU tensors
|
||||
if (options.has_device() && !actual_device.is_cpu()) {
|
||||
PD_THROW(
|
||||
"pin_memory=true requires device to be CPU, but got non-CPU device");
|
||||
}
|
||||
phi::Place pinned_place = compat::_PD_GetCreatePinnedPlace(pd_place);
|
||||
auto dense_cpu = paddle::experimental::full(
|
||||
size._PD_ToPaddleIntArray(), fill_value, pd_dtype, phi::CPUPlace());
|
||||
result = dense_cpu.copy_to(pinned_place, /*blocking=*/true);
|
||||
} else {
|
||||
result = paddle::experimental::full(
|
||||
size._PD_ToPaddleIntArray(), fill_value, pd_dtype, pd_place);
|
||||
}
|
||||
return Tensor(result);
|
||||
}
|
||||
|
||||
inline Tensor Tensor::new_full(at::IntArrayRef size,
|
||||
const at::Scalar& fill_value,
|
||||
::std::optional<at::ScalarType> dtype,
|
||||
::std::optional<at::Layout>,
|
||||
::std::optional<at::Device> device,
|
||||
::std::optional<bool> pin_memory) const {
|
||||
auto options = at::TensorOptions()
|
||||
.dtype(dtype.value_or(this->scalar_type()))
|
||||
.device(device.value_or(this->device()))
|
||||
.pinned_memory(pin_memory);
|
||||
return new_full(size, fill_value, options);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) 2025 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 <ATen/core/Tensor.h>
|
||||
#include <c10/core/TensorOptions.h>
|
||||
#include <utils/pinned_place.h>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
#include "paddle/phi/api/include/api.h"
|
||||
#include "paddle/phi/common/place.h"
|
||||
|
||||
namespace at {
|
||||
|
||||
// Member function: Tensor::new_ones
|
||||
inline Tensor Tensor::new_ones(at::IntArrayRef size,
|
||||
at::TensorOptions options) const {
|
||||
caffe2::TypeMeta actual_dtype = options.dtype_opt().value_or(dtype());
|
||||
auto actual_device = options.device_opt().value_or(device());
|
||||
auto actual_pin_memory = options.pinned_memory();
|
||||
|
||||
auto pd_dtype = compat::_PD_AtenScalarTypeToPhiDataType(actual_dtype);
|
||||
auto pd_place = actual_device._PD_GetInner();
|
||||
|
||||
paddle::Tensor result;
|
||||
if (actual_pin_memory) {
|
||||
// Pinning memory is only supported for CPU tensors
|
||||
if (options.has_device() && !actual_device.is_cpu()) {
|
||||
PD_THROW(
|
||||
"pin_memory=true requires device to be CPU, but got non-CPU device");
|
||||
}
|
||||
phi::Place pinned_place = compat::_PD_GetCreatePinnedPlace(pd_place);
|
||||
auto dense_cpu = paddle::experimental::ones(
|
||||
size._PD_ToPaddleIntArray(), pd_dtype, phi::CPUPlace());
|
||||
result = dense_cpu.copy_to(pinned_place, /*blocking=*/true);
|
||||
} else {
|
||||
result = paddle::experimental::ones(
|
||||
size._PD_ToPaddleIntArray(), pd_dtype, pd_place);
|
||||
}
|
||||
return Tensor(result);
|
||||
}
|
||||
|
||||
inline Tensor Tensor::new_ones(at::IntArrayRef size,
|
||||
::std::optional<at::ScalarType> dtype,
|
||||
::std::optional<at::Layout>,
|
||||
::std::optional<at::Device> device,
|
||||
::std::optional<bool> pin_memory) const {
|
||||
auto options = at::TensorOptions()
|
||||
.dtype(dtype.value_or(this->scalar_type()))
|
||||
.device(device.value_or(this->device()))
|
||||
.pinned_memory(pin_memory);
|
||||
return new_ones(size, options);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright (c) 2026 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 <ATen/core/Tensor.h>
|
||||
#include <c10/core/SymIntArrayRef.h>
|
||||
#include <c10/core/TensorOptions.h>
|
||||
#include <utils/pinned_place.h>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
#include "paddle/phi/api/include/api.h"
|
||||
#include "paddle/phi/common/place.h"
|
||||
|
||||
namespace at {
|
||||
|
||||
// Member function: Tensor::new_zeros
|
||||
inline Tensor Tensor::new_zeros(at::IntArrayRef size,
|
||||
at::TensorOptions options) const {
|
||||
caffe2::TypeMeta actual_dtype = options.dtype_opt().value_or(dtype());
|
||||
auto actual_device = options.device_opt().value_or(device());
|
||||
auto actual_pin_memory = options.pinned_memory();
|
||||
|
||||
auto pd_dtype = compat::_PD_AtenScalarTypeToPhiDataType(actual_dtype);
|
||||
auto pd_place = actual_device._PD_GetInner();
|
||||
|
||||
paddle::Tensor result;
|
||||
if (actual_pin_memory) {
|
||||
// Pinning memory is only supported for CPU tensors
|
||||
if (options.has_device() && !actual_device.is_cpu()) {
|
||||
PD_THROW(
|
||||
"pin_memory=true requires device to be CPU, but got non-CPU device");
|
||||
}
|
||||
phi::Place pinned_place = compat::_PD_GetCreatePinnedPlace(pd_place);
|
||||
auto dense_cpu = paddle::experimental::zeros(
|
||||
size._PD_ToPaddleIntArray(), pd_dtype, phi::CPUPlace());
|
||||
result = dense_cpu.copy_to(pinned_place, /*blocking=*/true);
|
||||
} else {
|
||||
result = paddle::experimental::zeros(
|
||||
size._PD_ToPaddleIntArray(), pd_dtype, pd_place);
|
||||
}
|
||||
return Tensor(result);
|
||||
}
|
||||
|
||||
inline Tensor Tensor::new_zeros(at::IntArrayRef size,
|
||||
::std::optional<at::ScalarType> dtype,
|
||||
::std::optional<at::Layout>,
|
||||
::std::optional<at::Device> device,
|
||||
::std::optional<bool> pin_memory) const {
|
||||
auto options = at::TensorOptions()
|
||||
.dtype(dtype.value_or(this->scalar_type()))
|
||||
.device(device.value_or(this->device()))
|
||||
.pinned_memory(pin_memory);
|
||||
return new_zeros(size, options);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,99 @@
|
||||
// Copyright (c) 2025 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 <ATen/core/Tensor.h>
|
||||
#include <c10/core/TensorOptions.h>
|
||||
#include <utils/pinned_place.h>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
#include "paddle/phi/api/include/api.h"
|
||||
#include "paddle/phi/common/place.h"
|
||||
|
||||
namespace at {
|
||||
|
||||
inline at::Tensor ones(at::IntArrayRef size, at::TensorOptions options = {}) {
|
||||
if (options.pinned_memory()) {
|
||||
// Pinning memory is only supported for CPU tensors
|
||||
if (options.has_device() && !options.device().is_cpu()) {
|
||||
PD_THROW(
|
||||
"pin_memory=true requires device to be CPU, but got non-CPU device");
|
||||
}
|
||||
phi::Place base_place = options._PD_GetPlace();
|
||||
phi::Place pinned_place = compat::_PD_GetCreatePinnedPlace(base_place);
|
||||
auto dense = paddle::experimental::ones(
|
||||
size._PD_ToPaddleIntArray(),
|
||||
compat::_PD_AtenScalarTypeToPhiDataType(options.dtype()),
|
||||
phi::CPUPlace());
|
||||
return dense.copy_to(pinned_place, /*blocking=*/true);
|
||||
}
|
||||
return paddle::experimental::ones(
|
||||
size._PD_ToPaddleIntArray(),
|
||||
compat::_PD_AtenScalarTypeToPhiDataType(options.dtype()),
|
||||
options._PD_GetPlace());
|
||||
}
|
||||
|
||||
inline at::Tensor ones(at::IntArrayRef size,
|
||||
::std::optional<at::ScalarType> dtype,
|
||||
::std::optional<at::Layout> layout,
|
||||
::std::optional<at::Device> device,
|
||||
::std::optional<bool> pin_memory) {
|
||||
PD_CHECK(!layout.has_value(), "`layout` is not supported now.");
|
||||
auto options =
|
||||
at::TensorOptions()
|
||||
.dtype(dtype.value_or(c10::get_default_dtype_as_scalartype()))
|
||||
.device(device.value_or(at::kCPU))
|
||||
.pinned_memory(pin_memory);
|
||||
return ones(size, options);
|
||||
}
|
||||
|
||||
inline at::Tensor ones_symint(c10::SymIntArrayRef size,
|
||||
at::TensorOptions options = {}) {
|
||||
if (options.pinned_memory()) {
|
||||
// Pinning memory is only supported for CPU tensors
|
||||
if (options.has_device() && !options.device().is_cpu()) {
|
||||
PD_THROW(
|
||||
"pin_memory=true requires device to be CPU, but got non-CPU device");
|
||||
}
|
||||
phi::Place base_place = options._PD_GetPlace();
|
||||
phi::Place pinned_place = compat::_PD_GetCreatePinnedPlace(base_place);
|
||||
auto dense = paddle::experimental::ones(
|
||||
size._PD_ToPaddleIntArray(),
|
||||
compat::_PD_AtenScalarTypeToPhiDataType(options.dtype()),
|
||||
phi::CPUPlace());
|
||||
return dense.copy_to(pinned_place, /*blocking=*/true);
|
||||
}
|
||||
return paddle::experimental::ones(
|
||||
size._PD_ToPaddleIntArray(),
|
||||
compat::_PD_AtenScalarTypeToPhiDataType(options.dtype()),
|
||||
options._PD_GetPlace());
|
||||
}
|
||||
|
||||
inline at::Tensor ones_symint(c10::SymIntArrayRef size,
|
||||
::std::optional<at::ScalarType> dtype,
|
||||
::std::optional<at::Layout> layout,
|
||||
::std::optional<at::Device> device,
|
||||
::std::optional<bool> pin_memory) {
|
||||
PD_CHECK(!layout.has_value(), "`layout` is not supported now.");
|
||||
auto options =
|
||||
at::TensorOptions()
|
||||
.dtype(dtype.value_or(c10::get_default_dtype_as_scalartype()))
|
||||
.device(device.value_or(at::kCPU))
|
||||
.pinned_memory(pin_memory);
|
||||
return ones_symint(size, options);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) 2026 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 <ATen/core/Tensor.h>
|
||||
|
||||
namespace at {
|
||||
|
||||
inline at::Tensor permute(const at::Tensor& self, at::IntArrayRef dims) {
|
||||
std::vector<int> perm(dims.size());
|
||||
for (size_t i = 0; i < dims.size(); i++) {
|
||||
perm[i] = static_cast<int>(dims[i]);
|
||||
}
|
||||
return paddle::experimental::transpose(self._PD_GetInner(), perm);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
|
||||
namespace at {
|
||||
|
||||
inline at::Tensor Tensor::permute(at::IntArrayRef dims) const {
|
||||
return at::permute(*this, dims);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) 2026 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 <ATen/core/Tensor.h>
|
||||
|
||||
namespace at {
|
||||
|
||||
inline at::Tensor reciprocal(const at::Tensor& self) {
|
||||
return Tensor(paddle::experimental::reciprocal(self._PD_GetInner()));
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
|
||||
namespace at {
|
||||
|
||||
inline at::Tensor Tensor::reciprocal() const { return at::reciprocal(*this); }
|
||||
|
||||
inline at::Tensor& Tensor::reciprocal_() const {
|
||||
PaddleTensor& inner = const_cast<PaddleTensor&>(tensor_);
|
||||
paddle::experimental::reciprocal_(inner);
|
||||
return const_cast<at::Tensor&>(*this);
|
||||
}
|
||||
|
||||
} // namespace at
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user