chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:40:42 +08:00
commit e25996e7db
15472 changed files with 3536181 additions and 0 deletions
@@ -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
@@ -0,0 +1,52 @@
// 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/Device.h>
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
#include <c10/cuda/CUDAStream.h>
#endif
namespace at {
inline void Tensor::record_stream(at::Stream s) const {
auto dense_tensor =
std::dynamic_pointer_cast<phi::DenseTensor>(tensor_.impl());
PD_CHECK(dense_tensor != nullptr,
"record_stream only supports DenseTensor, but got a non-dense "
"tensor implementation.");
PD_CHECK(dense_tensor->place().GetType() != phi::AllocationType::CPU,
"record_stream is not supported for CPU tensors.");
#if (defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)) && \
!defined(PADDLE_WITH_CUSTOM_DEVICE)
paddle::memory::RecordStream(
dense_tensor->Holder(), reinterpret_cast<gpuStream_t>(s.native_handle()));
#elif defined(PADDLE_WITH_XPU)
paddle::memory::RecordStream(dense_tensor->Holder(),
reinterpret_cast<XPUStream>(s.native_handle()));
#elif defined(PADDLE_WITH_CUSTOM_DEVICE)
paddle::memory::RecordStream(
dense_tensor->Holder(),
reinterpret_cast<phi::stream::stream_t>(s.native_handle()));
#else
(void)s;
(void)dense_tensor;
PD_THROW(
"record_stream is not supported: no GPU/XPU/Custom device enabled "
"in this build.");
#endif
}
} // 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>
namespace at {
// Member function: Tensor::rename
inline Tensor Tensor::rename(::std::optional<at::DimnameList>) const {
return *this;
}
} // namespace at
@@ -0,0 +1,44 @@
// 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"
namespace at {
inline at::Tensor reshape(const at::Tensor& self, at::IntArrayRef shape) {
return paddle::experimental::reshape(self._PD_GetInner(),
shape._PD_ToPaddleIntArray());
}
inline at::Tensor reshape_symint(const at::Tensor& self,
c10::SymIntArrayRef shape) {
return paddle::experimental::reshape(self._PD_GetInner(),
shape._PD_ToPaddleIntArray());
}
} // namespace at
namespace at {
inline at::Tensor Tensor::reshape(at::IntArrayRef shape) const {
return at::reshape(*this, shape);
}
} // namespace at
@@ -0,0 +1,119 @@
// 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 <limits>
#include <optional>
#include <string_view>
#include <vector>
#include "paddle/phi/api/include/api.h"
#include "paddle/phi/common/memory_utils.h"
#include "paddle/phi/core/ddim.h"
#include "paddle/phi/core/memory/malloc.h"
namespace at {
namespace detail {
inline int64_t ResizeCheckedNumel(at::IntArrayRef size) {
int64_t numel = 1;
for (const auto dim : size) {
TORCH_CHECK(dim >= 0,
"Trying to create tensor with negative dimension ",
dim,
": ",
size);
if (dim == 0) {
numel = 0;
continue;
}
TORCH_CHECK(numel <= std::numeric_limits<int64_t>::max() / dim,
"resize_ size is too large, possible overflow for size ",
size);
numel *= dim;
}
return numel;
}
inline size_t ResizeCheckedStorageBytes(int64_t numel,
size_t itemsize,
size_t storage_offset_bytes) {
const auto numel_size = static_cast<size_t>(numel);
TORCH_CHECK(
itemsize == 0 || numel_size <= (std::numeric_limits<size_t>::max() -
storage_offset_bytes) /
itemsize,
"resize_ size is too large in bytes");
return storage_offset_bytes + numel_size * itemsize;
}
} // namespace detail
// resize_ - operate on the underlying DenseTensor directly so we preserve
// storage semantics across shrink/grow round-trips. When growth exceeds the
// current capacity, expand the shared storage itself so aliasing views keep
// their storage offset and existing storage contents stay intact.
inline const at::Tensor& Tensor::resize_(
at::IntArrayRef size,
::std::optional<at::MemoryFormat> memory_format) const {
// Keep old compat behavior for memory_format in this split PR.
// TODO(youge325): add real ChannelsLast/ChannelsLast3d restride support
// later.
(void)memory_format;
std::vector<int64_t> dims(size.begin(), size.end());
int64_t new_numel = detail::ResizeCheckedNumel(size);
auto dense_tensor =
std::dynamic_pointer_cast<phi::DenseTensor>(tensor_.impl());
TORCH_CHECK(dense_tensor != nullptr,
"resize_ only supports DenseTensor, but got a non-dense tensor");
TORCH_CHECK(tensor_.defined(),
"resize_ is not allowed on an undefined tensor");
const size_t itemsize = phi::SizeOf(dense_tensor->dtype());
const size_t new_storage_bytes = detail::ResizeCheckedStorageBytes(
new_numel, itemsize, dense_tensor->meta().offset);
const size_t current_storage_bytes =
dense_tensor->Holder() == nullptr ? 0 : dense_tensor->Holder()->size();
if (new_storage_bytes <= current_storage_bytes || new_numel == 0) {
dense_tensor->Resize(dims);
return *this;
}
// Sync through the compat Storage path first so the DenseTensor holder is a
// live StorageHolderView backed by shared StorageImpl.
auto storage = this->storage();
const auto old_holder = dense_tensor->Holder();
TORCH_CHECK(old_holder != nullptr,
"resize_ cannot grow a tensor without allocated storage");
const phi::Place place = old_holder->place();
auto new_holder = paddle::memory::AllocShared(place, new_storage_bytes);
TORCH_CHECK(new_holder != nullptr, "resize_ failed to allocate storage");
const size_t copy_bytes = std::min(old_holder->size(), new_storage_bytes);
if (copy_bytes > 0 && old_holder->ptr() != nullptr &&
old_holder->ptr() != new_holder->ptr()) {
phi::memory_utils::Copy(
place, new_holder->ptr(), place, old_holder->ptr(), copy_bytes);
}
storage.set_data_ptr_noswap(std::move(new_holder));
dense_tensor->Resize(phi::make_ddim(dims));
return *this;
}
} // namespace at
@@ -0,0 +1,80 @@
// 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 select(const at::Tensor& self, int64_t dim, int64_t index) {
// Normalize dim to positive value for error messages
int64_t orig_dim = dim;
if (dim < 0) {
dim += self.dim();
}
// Check dim is valid
if (dim < 0 || dim >= self.dim()) {
PD_CHECK(false,
"select(): index ",
orig_dim,
" out of range for tensor of size ",
self.sizes(),
" at dimension ",
orig_dim);
}
// Handle negative index
int64_t orig_index = index;
if (index < 0) {
index = self.size(dim) + index;
}
// Check index is valid
if (index < 0 || index >= self.size(dim)) {
PD_CHECK(false,
"select(): index ",
orig_index,
" out of range for tensor of size ",
self.sizes(),
" at dimension ",
orig_dim < 0 ? orig_dim + self.dim() : orig_dim);
}
return Tensor(
paddle::experimental::slice(self._PD_GetInner(),
/*axes=*/{static_cast<int>(dim)},
/*starts=*/{index},
/*ends=*/{index + 1},
/*infer_flags=*/{1},
/*decrease_axis=*/{static_cast<int>(dim)}));
}
inline at::Tensor select_symint(const at::Tensor& self,
int64_t dim,
c10::SymInt index) {
return select(self, dim, static_cast<int64_t>(index));
}
} // namespace at
namespace at {
inline at::Tensor Tensor::select(int64_t dim, int64_t index) const {
return at::select(*this, dim, index);
}
inline at::Tensor Tensor::select_symint(int64_t dim, c10::SymInt index) const {
return at::select_symint(*this, dim, index);
}
} // namespace at
@@ -0,0 +1,51 @@
// 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 slice(const at::Tensor& self,
int64_t dim = 0,
::std::optional<int64_t> start = ::std::nullopt,
::std::optional<int64_t> end = ::std::nullopt,
int64_t step = 1) {
// Materialize the compat StorageHolderView before creating the slice so the
// base tensor and its views observe the same shared storage during resize_.
(void)self.storage();
return paddle::experimental::slice(
self._PD_GetInner(),
{dim},
start.has_value() ? IntArrayRef(start.value())._PD_ToPaddleIntArray()
: IntArrayRef()._PD_ToPaddleIntArray(),
end.has_value() ? IntArrayRef(end.value())._PD_ToPaddleIntArray()
: IntArrayRef()._PD_ToPaddleIntArray(),
{1},
{});
}
} // namespace at
namespace at {
inline at::Tensor Tensor::slice(int64_t dim,
::std::optional<int64_t> start,
::std::optional<int64_t> end,
int64_t step) const {
return at::slice(*this, dim, start, end, step);
}
} // namespace at
@@ -0,0 +1,119 @@
// 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 <algorithm>
#include <memory>
#include <optional>
#include "paddle/phi/api/include/api.h"
#include "paddle/phi/api/include/sparse_api.h"
#include "paddle/phi/common/place.h"
#include "paddle/phi/core/sparse_coo_tensor.h"
namespace at::detail {
inline std::vector<int64_t> _PD_infer_sparse_coo_size(
const at::Tensor& indices) {
auto host_indices = indices.cpu().to(at::kLong);
int64_t sparse_dim = host_indices.dim() > 0 ? host_indices.size(0) : 0;
int64_t nnz = host_indices.dim() > 1 ? host_indices.size(1) : 0;
std::vector<int64_t> inferred_size(static_cast<size_t>(sparse_dim), 0);
const int64_t* data = host_indices.const_data_ptr<int64_t>();
for (int64_t dim = 0; dim < sparse_dim; ++dim) {
for (int64_t i = 0; i < nnz; ++i) {
inferred_size[static_cast<size_t>(dim)] = std::max(
inferred_size[static_cast<size_t>(dim)], data[dim * nnz + i] + 1);
}
}
return inferred_size;
}
inline void _PD_set_sparse_coo_coalesced(at::Tensor* tensor,
::std::optional<bool> is_coalesced) {
if (!is_coalesced.has_value()) {
return;
}
auto sparse_tensor = std::dynamic_pointer_cast<phi::SparseCooTensor>(
tensor->_PD_GetInner().impl());
PD_CHECK(sparse_tensor,
"Expected SparseCooTensor result from sparse_coo_tensor.");
sparse_tensor->SetCoalesced(is_coalesced.value());
}
} // namespace at::detail
namespace at {
inline at::Tensor sparse_coo_tensor(
const at::Tensor& indices,
const at::Tensor& values,
at::IntArrayRef size,
at::TensorOptions options = {},
::std::optional<bool> is_coalesced = ::std::nullopt) {
paddle::Tensor idx = indices._PD_GetInner();
paddle::Tensor vals = values._PD_GetInner();
// PyTorch ignores dtype mismatch between values and TensorOptions in
// sparse_coo_tensor; the resulting sparse tensor uses values' original dtype.
// Do not cast or throw here.
if (options.pinned_memory()) {
phi::Place base_place = options._PD_GetPlace();
phi::Place pinned_place = compat::_PD_GetCreatePinnedPlace(base_place);
idx = idx.copy_to(pinned_place, /*blocking=*/true);
vals = vals.copy_to(pinned_place, /*blocking=*/true);
}
// PyTorch: sparse_coo_tensor(indices, values, size)
// Paddle: sparse_coo_tensor(values, indices, shape)
at::Tensor result = paddle::experimental::sparse::sparse_coo_tensor(
vals, idx, std::vector<int64_t>(size.begin(), size.end()));
detail::_PD_set_sparse_coo_coalesced(&result, is_coalesced);
return result;
}
inline at::Tensor sparse_coo_tensor(const at::Tensor& indices,
const at::Tensor& values,
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<bool> is_coalesced) {
PD_CHECK(!layout.has_value() || layout.value() == c10::kSparse,
"`layout` must be Sparse for sparse_coo_tensor.");
auto options =
at::TensorOptions().dtype(dtype).device(device).pinned_memory(pin_memory);
return sparse_coo_tensor(indices, values, size, options, is_coalesced);
}
inline at::Tensor sparse_coo_tensor(
const at::Tensor& indices,
const at::Tensor& values,
at::TensorOptions options = {},
::std::optional<bool> is_coalesced = ::std::nullopt) {
return sparse_coo_tensor(indices,
values,
detail::_PD_infer_sparse_coo_size(indices),
options,
is_coalesced);
}
} // 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 <c10/core/TensorOptions.h>
#include <utils/pinned_place.h>
#include <optional>
#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"
#include "paddle/phi/core/sparse_csr_tensor.h"
namespace at {
inline at::Tensor sparse_csr_tensor(const at::Tensor& crow_indices,
const at::Tensor& col_indices,
const at::Tensor& values,
at::IntArrayRef size,
at::TensorOptions options) {
paddle::Tensor crows = crow_indices._PD_GetInner();
paddle::Tensor cols = col_indices._PD_GetInner();
paddle::Tensor vals = values._PD_GetInner();
// PyTorch ignores dtype mismatch between values and TensorOptions in
// sparse_csr_tensor; the resulting sparse tensor uses values' original dtype.
// Do not cast or throw here.
if (options.pinned_memory()) {
phi::Place base_place = options._PD_GetPlace();
phi::Place pinned_place = compat::_PD_GetCreatePinnedPlace(base_place);
crows = crows.copy_to(pinned_place, /*blocking=*/true);
cols = cols.copy_to(pinned_place, /*blocking=*/true);
vals = vals.copy_to(pinned_place, /*blocking=*/true);
}
// Get the underlying DenseTensors
auto* dense_crows = dynamic_cast<phi::DenseTensor*>(crows.impl().get());
auto* dense_cols = dynamic_cast<phi::DenseTensor*>(cols.impl().get());
auto* dense_values = dynamic_cast<phi::DenseTensor*>(vals.impl().get());
PD_CHECK(dense_crows != nullptr,
"crow_indices must be a dense tensor for sparse_csr_tensor.");
PD_CHECK(dense_cols != nullptr,
"col_indices must be a dense tensor for sparse_csr_tensor.");
PD_CHECK(dense_values != nullptr,
"values must be a dense tensor for sparse_csr_tensor.");
// Create the SparseCsrTensor
std::shared_ptr<phi::SparseCsrTensor> csr_tensor =
std::make_shared<phi::SparseCsrTensor>(
*dense_crows,
*dense_cols,
*dense_values,
common::make_ddim(std::vector<int64_t>(size.begin(), size.end())));
// Wrap in a Paddle Tensor
paddle::Tensor result;
result.set_impl(csr_tensor);
return result;
}
inline at::Tensor sparse_csr_tensor(const at::Tensor& crow_indices,
const at::Tensor& col_indices,
const at::Tensor& values,
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.value() == c10::kSparseCsr,
"`layout` must be SparseCsr for sparse_csr_tensor.");
auto options =
at::TensorOptions().dtype(dtype).device(device).pinned_memory(pin_memory);
return sparse_csr_tensor(crow_indices, col_indices, values, size, options);
}
inline at::Tensor sparse_csr_tensor(const at::Tensor& crow_indices,
const at::Tensor& col_indices,
const at::Tensor& values,
at::TensorOptions options) {
// Infer size from crow_indices and col_indices:
// nrows = crow_indices.size(0) - 1
// ncols = max(col_indices) + 1
int64_t nrows = crow_indices.size(0) - 1;
int64_t ncols = 0;
if (col_indices.numel() > 0) {
auto* dense_cols = dynamic_cast<phi::DenseTensor*>(
col_indices._PD_GetInner().impl().get());
PD_CHECK(dense_cols != nullptr,
"col_indices must be a dense tensor for sparse_csr_tensor.");
PD_CHECK(
dense_cols->place().GetType() == phi::AllocationType::CPU,
"sparse_csr_tensor without explicit size only supports CPU "
"col_indices for automatic size inference. Please provide the size "
"parameter explicitly for non-CPU tensors.");
int64_t n = dense_cols->numel();
if (dense_cols->dtype() == phi::DataType::INT64) {
const int64_t* data = dense_cols->data<int64_t>();
for (int64_t i = 0; i < n; ++i) {
if (data[i] + 1 > ncols) ncols = data[i] + 1;
}
} else if (dense_cols->dtype() == phi::DataType::INT32) {
const int32_t* data = dense_cols->data<int32_t>();
for (int64_t i = 0; i < n; ++i) {
int64_t val = static_cast<int64_t>(data[i]) + 1;
if (val > ncols) ncols = val;
}
} else {
PD_CHECK(false,
"col_indices must have dtype int32 or int64 for automatic "
"size inference in sparse_csr_tensor.");
}
}
std::vector<int64_t> size_vec = {nrows, ncols};
return sparse_csr_tensor(
crow_indices, col_indices, values, at::IntArrayRef(size_vec), options);
}
} // namespace at
@@ -0,0 +1,93 @@
// 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 std::vector<at::Tensor> split(const at::Tensor& self,
int64_t split_size,
int64_t dim = 0) {
// Calculate number of splits based on split_size
int64_t dim_size = self._PD_GetInner().dims()[dim];
std::vector<int64_t> split_sizes;
for (int64_t i = 0; i < dim_size; i += split_size) {
split_sizes.push_back(std::min(split_size, dim_size - i));
}
auto outputs =
paddle::experimental::split(self._PD_GetInner(), split_sizes, dim);
std::vector<at::Tensor> at_tensors;
at_tensors.reserve(outputs.size());
for (const auto& paddle_tensor : outputs) {
at_tensors.emplace_back(paddle_tensor);
}
return at_tensors;
}
inline std::vector<at::Tensor> split_symint(const at::Tensor& self,
c10::SymInt split_size,
int64_t dim = 0) {
return split(self, static_cast<int64_t>(split_size), dim);
}
inline std::vector<at::Tensor> split(const at::Tensor& self,
at::IntArrayRef split_sizes,
int64_t dim = 0) {
auto outputs = paddle::experimental::split(
self._PD_GetInner(), split_sizes._PD_ToPaddleIntArray(), dim);
std::vector<at::Tensor> at_tensors;
at_tensors.reserve(outputs.size());
for (const auto& paddle_tensor : outputs) {
at_tensors.emplace_back(paddle_tensor);
}
return at_tensors;
}
inline std::vector<at::Tensor> split_symint(const at::Tensor& self,
c10::SymIntArrayRef split_sizes,
int64_t dim = 0) {
return split(
self,
at::IntArrayRef(reinterpret_cast<const int64_t*>(split_sizes.data()),
split_sizes.size()),
dim);
}
} // namespace at
namespace at {
inline std::vector<at::Tensor> Tensor::split(int64_t split_size,
int64_t dim = 0) const {
return at::split(*this, split_size, dim);
}
inline std::vector<at::Tensor> Tensor::split_symint(c10::SymInt split_size,
int64_t dim = 0) const {
return at::split_symint(*this, split_size, dim);
}
inline std::vector<at::Tensor> Tensor::split(at::IntArrayRef split_sizes,
int64_t dim = 0) const {
return at::split(*this, split_sizes, dim);
}
inline std::vector<at::Tensor> Tensor::split_symint(
c10::SymIntArrayRef split_sizes, int64_t dim = 0) const {
return at::split_symint(*this, split_sizes, dim);
}
} // 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/split.h>
namespace at {
inline std::vector<at::Tensor> split_with_sizes(const at::Tensor& self,
at::IntArrayRef split_sizes,
int64_t dim = 0) {
return at::split(self, split_sizes, dim);
}
inline std::vector<at::Tensor> split_with_sizes_symint(
const at::Tensor& self, c10::SymIntArrayRef split_sizes, int64_t dim = 0) {
return at::split_symint(self, split_sizes, dim);
}
} // namespace at
namespace at {
inline std::vector<at::Tensor> Tensor::split_with_sizes(
at::IntArrayRef split_sizes, int64_t dim) const {
return at::split_with_sizes(*this, split_sizes, dim);
}
inline std::vector<at::Tensor> Tensor::split_with_sizes_symint(
c10::SymIntArrayRef split_sizes, int64_t dim) const {
return at::split_with_sizes_symint(*this, split_sizes, dim);
}
} // namespace at
@@ -0,0 +1,66 @@
// 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 squeeze(const at::Tensor& self) {
return paddle::experimental::squeeze(self._PD_GetInner(), {});
}
inline at::Tensor squeeze(const at::Tensor& self, int64_t dim) {
return paddle::experimental::squeeze(self._PD_GetInner(), {dim});
}
inline at::Tensor squeeze(const at::Tensor& self, at::IntArrayRef dim) {
return paddle::experimental::squeeze(self._PD_GetInner(),
dim._PD_ToPaddleIntArray());
}
} // namespace at
namespace at {
inline at::Tensor Tensor::squeeze() const { return at::squeeze(*this); }
inline at::Tensor Tensor::squeeze(int64_t dim) const {
return at::squeeze(*this, dim);
}
inline at::Tensor Tensor::squeeze(at::IntArrayRef dim) const {
return at::squeeze(*this, dim);
}
inline at::Tensor& Tensor::squeeze_() const {
PaddleTensor& inner = const_cast<PaddleTensor&>(tensor_);
paddle::experimental::squeeze_(inner, {});
return const_cast<at::Tensor&>(*this);
}
inline at::Tensor& Tensor::squeeze_(int64_t dim) const {
PaddleTensor& inner = const_cast<PaddleTensor&>(tensor_);
paddle::experimental::squeeze_(inner, {dim});
return const_cast<at::Tensor&>(*this);
}
inline at::Tensor& Tensor::squeeze_(at::IntArrayRef dim) const {
PaddleTensor& inner = const_cast<PaddleTensor&>(tensor_);
paddle::experimental::squeeze_(inner, dim._PD_ToPaddleIntArray());
return const_cast<at::Tensor&>(*this);
}
} // namespace at
@@ -0,0 +1,146 @@
// 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/ArrayRef.h>
#include <c10/util/OptionalArrayRef.h>
#include <optional>
#include <vector>
#include "paddle/phi/api/include/api.h"
#include "paddle/phi/common/int_array.h"
#include "paddle/phi/common/scalar.h"
namespace at::detail {
// Internal implementation for std (standard deviation = sqrt(variance))
inline Tensor _PD_std_impl(const Tensor& self,
const std::vector<int64_t>& dims_vec,
double correction_value,
bool keepdim) {
// Validate dimensions before processing
int64_t ndim = self.dim();
for (int64_t d : dims_vec) {
int64_t dim_idx = d < 0 ? d + ndim : d;
if (dim_idx < 0 || dim_idx >= ndim) {
PD_CHECK(false,
"Dimension out of range (expected to be in range of [",
-ndim,
", ",
ndim - 1,
"], but got ",
d,
")");
}
}
phi::IntArray dims_int_array(dims_vec);
paddle::Tensor tensor = self._PD_GetInner();
paddle::Tensor 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);
}
paddle::Tensor diff = paddle::experimental::subtract(tensor, mean_tensor);
paddle::Tensor diff_squared = paddle::experimental::multiply(diff, diff);
paddle::Tensor 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]);
}
paddle::Tensor correction_scalar =
paddle::experimental::full(phi::IntArray(result_shape_vec),
phi::Scalar(corrected_n),
sum_squared_diff.dtype(),
sum_squared_diff.place());
paddle::Tensor variance =
paddle::experimental::divide(sum_squared_diff, correction_scalar);
paddle::Tensor result = paddle::experimental::sqrt(variance);
return Tensor(result);
}
} // namespace at::detail
namespace at {
inline Tensor Tensor::std(bool unbiased) const {
std::vector<int64_t> empty_dims;
double correction = unbiased ? 1.0 : 0.0;
return detail::_PD_std_impl(*this, empty_dims, correction, false);
}
inline Tensor Tensor::std(at::OptionalIntArrayRef dim,
bool unbiased,
bool keepdim) const {
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 detail::_PD_std_impl(*this, dims_vec, correction, keepdim);
}
inline Tensor Tensor::std(at::OptionalIntArrayRef dim,
const ::std::optional<at::Scalar>& correction,
bool keepdim) 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 detail::_PD_std_impl(*this, dims_vec, correction_value, keepdim);
}
} // namespace at
@@ -0,0 +1,110 @@
// 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 <c10/core/TensorOptions.h>
#include <c10/util/OptionalArrayRef.h>
#include <optional>
#include <string_view>
#include "paddle/phi/api/include/api.h"
namespace at {
inline at::Tensor sum(const at::Tensor& self,
::std::optional<at::ScalarType> dtype = ::std::nullopt) {
// Match PyTorch promotion: integer inputs -> int64; others -> keep input
// dtype.
at::ScalarType resolved_dtype;
if (dtype.has_value()) {
resolved_dtype = dtype.value();
} else {
at::ScalarType input_dtype = self.scalar_type();
resolved_dtype = at::isIntegralType(input_dtype, /*includeBool=*/true)
? at::kLong
: input_dtype;
}
return paddle::experimental::sum(
self._PD_GetInner(),
{},
compat::_PD_AtenScalarTypeToPhiDataType(resolved_dtype),
/*keepdim=*/false);
}
inline at::Tensor sum(const at::Tensor& self,
at::OptionalIntArrayRef dim,
bool keepdim = false,
::std::optional<at::ScalarType> dtype = ::std::nullopt) {
// Match PyTorch promotion: integer inputs -> int64; others -> keep input
// dtype.
at::ScalarType resolved_dtype;
if (dtype.has_value()) {
resolved_dtype = dtype.value();
} else {
at::ScalarType input_dtype = self.scalar_type();
resolved_dtype = at::isIntegralType(input_dtype, /*includeBool=*/true)
? at::kLong
: input_dtype;
}
return paddle::experimental::sum(
self._PD_GetInner(),
dim.has_value() ? dim.value()._PD_ToPaddleIntArray()
: paddle::experimental::IntArray(),
compat::_PD_AtenScalarTypeToPhiDataType(resolved_dtype),
keepdim);
}
inline at::Tensor& sum_out(
at::Tensor&
out, // NOLINT: intentional non-const reference for output parameter
const at::Tensor& self,
at::OptionalIntArrayRef dim,
bool keepdim = false,
::std::optional<at::ScalarType> dtype = ::std::nullopt) {
auto res = sum(self, dim, keepdim, dtype);
paddle::experimental::assign_out_(res._PD_GetInner(), out._PD_GetInner());
return out;
}
inline at::Tensor& sum_out(
at::Tensor&
out, // NOLINT: intentional non-const reference for output parameter
const at::Tensor& self,
::std::optional<at::ScalarType> dtype = ::std::nullopt) {
auto res = sum(self, dtype);
paddle::experimental::assign_out_(res._PD_GetInner(), out._PD_GetInner());
return out;
}
} // namespace at
namespace at {
inline at::Tensor Tensor::sum(::std::optional<at::ScalarType> dtype) const {
return at::sum(*this, dtype);
}
inline at::Tensor Tensor::sum(at::OptionalIntArrayRef dim,
bool keepdim,
::std::optional<at::ScalarType> dtype) const {
return at::sum(*this, dim, keepdim, dtype);
}
} // 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 t(const Tensor& self) {
return self.transpose(0, self.dim() < 2 ? 0 : 1);
}
} // namespace at
namespace at {
inline at::Tensor Tensor::t() const { return at::t(*this); }
inline at::Tensor& Tensor::t_() const {
return transpose_(0, dim() < 2 ? 0 : 1);
}
} // namespace at
@@ -0,0 +1,47 @@
// 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 <c10/core/ScalarType.h>
#include "paddle/common/macros.h"
namespace at {
#define TENSOR(T, S) \
PADDLE_API Tensor tensor(ArrayRef<T> values, const TensorOptions& options); \
inline Tensor tensor(std::initializer_list<T> values, \
const TensorOptions& options) { \
return at::tensor(ArrayRef<T>(values), options); \
} \
inline Tensor tensor(T value, const TensorOptions& options) { \
return at::tensor(ArrayRef<T>(value), options); \
} \
inline Tensor tensor(ArrayRef<T> values) { \
return at::tensor(std::move(values), at::dtype(k##S)); \
} \
inline Tensor tensor(std::initializer_list<T> values) { \
return at::tensor(ArrayRef<T>(values)); \
} \
inline Tensor tensor(T value) { return at::tensor(ArrayRef<T>(value)); }
AT_FORALL_SCALAR_TYPES_AND3(Bool, Half, BFloat16, TENSOR)
AT_FORALL_COMPLEX_TYPES(TENSOR)
#undef TENSOR
} // namespace at
@@ -0,0 +1,207 @@
// 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>
namespace at {
inline std::vector<at::Tensor> tensor_split(const at::Tensor& self,
int64_t sections,
int64_t dim = 0) {
// Follow PyTorch's tensor_split_sections_symint implementation
PD_CHECK(self._PD_GetInner().dims().size() > 0,
"tensor_split expected at least a 1-dimensional tensor, but got a "
"tensor with ",
self._PD_GetInner().dims().size(),
" dims");
PD_CHECK(
sections > 0, "number of sections must be larger than 0, got ", sections);
int64_t dim_size = self._PD_GetInner().dims()[dim];
// Calculate split sizes: first (dim_size % sections) chunks get size
// (dim_size / sections + 1), remaining chunks get size (dim_size / sections)
auto min_split_size = dim_size / sections;
auto num_splits_one_extra = dim_size % sections;
std::vector<int64_t> split_sizes;
split_sizes.reserve(sections);
for (int64_t split_idx = 0; split_idx < sections; ++split_idx) {
auto split_size = (split_idx < num_splits_one_extra) ? (min_split_size + 1)
: min_split_size;
split_sizes.push_back(split_size);
}
// Use split with calculated sizes
auto outputs =
paddle::experimental::split(self._PD_GetInner(), split_sizes, dim);
std::vector<at::Tensor> at_tensors;
at_tensors.reserve(outputs.size());
for (const auto& paddle_tensor : outputs) {
at_tensors.emplace_back(paddle_tensor);
}
return at_tensors;
}
inline std::vector<at::Tensor> tensor_split_symint(const at::Tensor& self,
c10::SymInt sections,
int64_t dim = 0) {
return tensor_split(self, static_cast<int64_t>(sections), dim);
}
inline std::vector<at::Tensor> tensor_split(const at::Tensor& self,
at::IntArrayRef indices,
int64_t dim = 0) {
// Follow PyTorch's _tensor_split_indices implementation
// indices are split positions, not sizes
PD_CHECK(self._PD_GetInner().dims().size() > 0,
"tensor_split expected at least a 1-dimensional tensor, but got a "
"tensor with ",
self._PD_GetInner().dims().size(),
" dims");
int64_t num_indices = indices.size();
int64_t dim_size = self._PD_GetInner().dims()[dim];
// Convert indices (positions) to sizes
std::vector<int64_t> split_sizes;
split_sizes.reserve(num_indices + 1);
int64_t start_idx = 0;
for (int64_t i = 0; i < num_indices; ++i) {
int64_t end_idx = indices[i];
// Handle negative indices
if (end_idx < 0) {
end_idx += dim_size;
}
// Clamp to valid range
end_idx = std::max(start_idx, std::min(end_idx, dim_size));
split_sizes.push_back(end_idx - start_idx);
start_idx = end_idx;
}
// Add the last segment
split_sizes.push_back(dim_size - start_idx);
// Use split with calculated sizes
auto outputs =
paddle::experimental::split(self._PD_GetInner(), split_sizes, dim);
std::vector<at::Tensor> at_tensors;
at_tensors.reserve(outputs.size());
for (const auto& paddle_tensor : outputs) {
at_tensors.emplace_back(paddle_tensor);
}
return at_tensors;
}
inline std::vector<at::Tensor> tensor_split_symint(const at::Tensor& self,
c10::SymIntArrayRef indices,
int64_t dim = 0) {
return tensor_split(
self,
at::IntArrayRef(reinterpret_cast<const int64_t*>(indices.data()),
indices.size()),
dim);
}
inline std::vector<at::Tensor> tensor_split(
const at::Tensor& self,
const at::Tensor& tensor_indices_or_sections,
int64_t dim = 0) {
// Follow PyTorch's validation and implementation
PD_CHECK(self._PD_GetInner().dims().size() > 0,
"tensor_split expected at least a 1-dimensional tensor, but got a "
"tensor with ",
self._PD_GetInner().dims().size(),
" dims");
auto split_device = tensor_indices_or_sections.device();
PD_CHECK(split_device.is_cpu(),
"tensor_split expected tensor_indices_or_sections to be on cpu, but "
"it's on ",
split_device);
auto split_dtype = tensor_indices_or_sections.scalar_type();
PD_CHECK(split_dtype == at::kLong,
"tensor_split expected tensor_indices_or_sections to have dtype of "
"long, but got ",
split_dtype);
auto split_dim = tensor_indices_or_sections.dim();
PD_CHECK(split_dim == 1 || split_dim == 0,
"tensor_split expected tensor_indices_or_sections to be a "
"zero-dimensional or one-dimensional tensor, but got a tensor with ",
split_dim,
" dims");
if (split_dim == 0) {
// 0-dimensional tensor: treat as sections
int64_t sections = tensor_indices_or_sections.item<int64_t>();
return tensor_split(self, sections, dim);
} else {
// 1-dimensional tensor: treat as indices
// Need to handle non-contiguous tensors properly
const PaddleTensor& paddle_tensor =
tensor_indices_or_sections._PD_GetInner();
const int64_t* indices_data = paddle_tensor.data<int64_t>();
auto stride = tensor_indices_or_sections.stride(0);
auto numel = tensor_indices_or_sections.numel();
std::vector<int64_t> indices(numel);
for (int64_t offset = 0; offset < numel; ++offset) {
// indices tensor could be non-contiguous
indices[offset] = *(indices_data + offset * stride);
}
return tensor_split(self, at::IntArrayRef(indices), dim);
}
}
} // namespace at
namespace at {
inline std::vector<at::Tensor> Tensor::tensor_split(int64_t sections,
int64_t dim = 0) const {
return at::tensor_split(*this, sections, dim);
}
inline std::vector<at::Tensor> Tensor::tensor_split_symint(
c10::SymInt sections, int64_t dim = 0) const {
return at::tensor_split_symint(*this, sections, dim);
}
inline std::vector<at::Tensor> Tensor::tensor_split(at::IntArrayRef indices,
int64_t dim = 0) const {
return at::tensor_split(*this, indices, dim);
}
inline std::vector<at::Tensor> Tensor::tensor_split_symint(
c10::SymIntArrayRef indices, int64_t dim = 0) const {
return at::tensor_split_symint(*this, indices, dim);
}
inline std::vector<at::Tensor> Tensor::tensor_split(
const at::Tensor& tensor_indices_or_sections, int64_t dim = 0) const {
return at::tensor_split(*this, tensor_indices_or_sections, dim);
}
} // namespace at
+133
View File
@@ -0,0 +1,133 @@
// 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/Device.h>
#include <c10/core/ScalarType.h>
#include <c10/core/TensorOptions.h>
#include <utils/scalar_type_conversion.h>
#include "paddle/phi/api/include/api.h"
#include "paddle/phi/common/place.h"
namespace at {
// Overload 1: to(TensorOptions, non_blocking, copy, memory_format)
inline at::Tensor Tensor::to(
at::TensorOptions options,
bool non_blocking,
bool copy,
::std::optional<at::MemoryFormat> memory_format) const {
// Handle device transfer
PaddleTensor result = tensor_;
bool materialized_copy = false;
if (options.has_device()) {
const c10::Device& dev = options.device();
phi::Place place;
switch (dev.type()) {
case c10::DeviceType::CPU:
case c10::DeviceType::CUDA:
case c10::DeviceType::XPU:
case c10::DeviceType::IPU:
case c10::DeviceType::CUSTOM:
place = dev._PD_GetInner();
break;
default:
PD_THROW("Unsupported device type: ", dev.type());
break;
}
if (place != tensor_.place()) {
result = result.copy_to(place, /*blocking=*/!non_blocking);
materialized_copy = true;
}
}
// Handle dtype cast
if (options.has_dtype()) {
auto target_dtype =
compat::_PD_AtenScalarTypeToPhiDataType(options.dtype());
if (target_dtype != result.dtype()) {
result = paddle::experimental::cast(result, target_dtype);
materialized_copy = true;
}
}
if (copy && !materialized_copy) {
result = paddle::experimental::assign(result);
}
return at::Tensor(result);
}
// Overload 2: to(optional<ScalarType>, optional<Layout>, optional<Device>,
// optional<bool> pin_memory, non_blocking, copy, memory_format)
inline at::Tensor 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::TensorOptions options;
if (dtype.has_value()) {
options = options.dtype(dtype.value());
}
if (device.has_value()) {
options = options.device(device.value());
}
if (pin_memory.has_value() && pin_memory.value()) {
options = options.pinned_memory(true);
}
return to(options, non_blocking, copy, memory_format);
}
// Overload 3: to(Device, ScalarType, non_blocking, copy, memory_format)
inline at::Tensor Tensor::to(
at::Device device,
at::ScalarType dtype,
bool non_blocking,
bool copy,
::std::optional<at::MemoryFormat> memory_format) const {
at::TensorOptions options = at::TensorOptions().device(device).dtype(dtype);
return to(options, non_blocking, copy, memory_format);
}
// Overload 4: to(ScalarType, non_blocking, copy, memory_format)
inline at::Tensor Tensor::to(
at::ScalarType dtype,
bool non_blocking,
bool copy,
::std::optional<at::MemoryFormat> memory_format) const {
auto target_dtype = compat::_PD_AtenScalarTypeToPhiDataType(dtype);
if (!copy && target_dtype == tensor_.dtype()) {
return *this;
}
return at::Tensor(paddle::experimental::cast(tensor_, target_dtype));
}
// Overload 5: to(const Tensor& other, non_blocking, copy, memory_format)
inline at::Tensor Tensor::to(
const at::Tensor& other,
bool non_blocking,
bool copy,
::std::optional<at::MemoryFormat> memory_format) const {
at::TensorOptions options =
at::TensorOptions().device(other.device()).dtype(other.scalar_type());
return to(options, non_blocking, copy, memory_format);
}
} // namespace at
@@ -0,0 +1,88 @@
// 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 <limits>
#include <optional>
#include <string_view>
#include <vector>
#include "paddle/phi/api/include/api.h"
namespace at::detail {
inline int _PD_normalize_transpose_dim(int64_t dim,
int64_t ndim,
const char* name) {
int64_t normalized = dim;
if (normalized < 0) {
normalized += ndim;
}
PD_CHECK(normalized >= 0 && normalized < ndim, name, " out of range");
PD_CHECK(normalized <= static_cast<int64_t>(std::numeric_limits<int>::max()),
name,
" out of int range");
return static_cast<int>(normalized);
}
inline std::vector<int> _PD_make_transpose_perm(int64_t ndim, int d0, int d1) {
PD_CHECK(ndim <= static_cast<int64_t>(std::numeric_limits<int>::max()),
"tensor rank out of int range");
std::vector<int> perm(static_cast<size_t>(ndim));
for (int64_t i = 0; i < ndim; ++i) {
perm[static_cast<size_t>(i)] = static_cast<int>(i);
}
std::swap(perm[d0], perm[d1]);
return perm;
}
} // namespace at::detail
namespace at {
inline at::Tensor transpose(const at::Tensor& self,
int64_t dim0,
int64_t dim1) {
int64_t ndim = self.dim();
int d0 = at::detail::_PD_normalize_transpose_dim(dim0, ndim, "dim0");
int d1 = at::detail::_PD_normalize_transpose_dim(dim1, ndim, "dim1");
auto perm = at::detail::_PD_make_transpose_perm(ndim, d0, d1);
return paddle::experimental::transpose(self._PD_GetInner(), perm);
}
} // namespace at
namespace at {
inline at::Tensor Tensor::transpose(int64_t dim0, int64_t dim1) const {
return at::transpose(*this, dim0, dim1);
}
inline at::Tensor& Tensor::transpose_(int64_t dim0, int64_t dim1) const {
int64_t ndim = this->dim();
int d0 = at::detail::_PD_normalize_transpose_dim(dim0, ndim, "dim0");
int d1 = at::detail::_PD_normalize_transpose_dim(dim1, ndim, "dim1");
auto perm = at::detail::_PD_make_transpose_perm(ndim, d0, d1);
PaddleTensor& inner = const_cast<PaddleTensor&>(tensor_);
paddle::experimental::transpose_(inner, perm);
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>
namespace at {
inline at::Tensor unflatten(const at::Tensor& self,
int64_t dim,
at::IntArrayRef sizes) {
// Compute the new shape by replacing the dimension at 'dim' with 'sizes'
int64_t ndim = self._PD_GetInner().dims().size();
int64_t actual_dim = dim < 0 ? dim + ndim : dim;
std::vector<int64_t> new_shape;
for (int64_t i = 0; i < ndim; ++i) {
if (i == actual_dim) {
for (auto s : sizes) {
new_shape.push_back(s);
}
} else {
new_shape.push_back(self._PD_GetInner().dims()[i]);
}
}
return Tensor(paddle::experimental::reshape(self._PD_GetInner(), new_shape));
}
inline at::Tensor unflatten_symint(const at::Tensor& self,
int64_t dim,
c10::SymIntArrayRef sizes) {
return unflatten(
self,
dim,
at::IntArrayRef(reinterpret_cast<const int64_t*>(sizes.data()),
sizes.size()));
}
} // namespace at
namespace at {
inline at::Tensor Tensor::unflatten(int64_t dim, at::IntArrayRef sizes) const {
return at::unflatten(*this, dim, sizes);
}
inline at::Tensor Tensor::unflatten_symint(int64_t dim,
c10::SymIntArrayRef sizes) const {
return at::unflatten_symint(*this, dim, sizes);
}
} // namespace at
@@ -0,0 +1,48 @@
// 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/split.h>
namespace at {
inline std::vector<at::Tensor> unsafe_split(const at::Tensor& self,
int64_t split_size,
int64_t dim = 0) {
return at::split(self, split_size, dim);
}
inline std::vector<at::Tensor> unsafe_split_symint(const at::Tensor& self,
c10::SymInt split_size,
int64_t dim = 0) {
return at::split(self, static_cast<int64_t>(split_size), dim);
}
} // namespace at
namespace at {
inline std::vector<at::Tensor> Tensor::unsafe_split(int64_t split_size,
int64_t dim) const {
return at::unsafe_split(*this, split_size, dim);
}
inline std::vector<at::Tensor> Tensor::unsafe_split_symint(
c10::SymInt split_size, int64_t dim) const {
return at::unsafe_split_symint(*this, split_size, dim);
}
} // namespace at
@@ -0,0 +1,46 @@
// 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/split.h>
namespace at {
inline std::vector<at::Tensor> unsafe_split_with_sizes(
const at::Tensor& self, at::IntArrayRef split_sizes, int64_t dim = 0) {
return at::split(self, split_sizes, dim);
}
inline std::vector<at::Tensor> unsafe_split_with_sizes_symint(
const at::Tensor& self, c10::SymIntArrayRef split_sizes, int64_t dim = 0) {
return at::split_symint(self, split_sizes, dim);
}
} // namespace at
namespace at {
inline std::vector<at::Tensor> Tensor::unsafe_split_with_sizes(
at::IntArrayRef split_sizes, int64_t dim) const {
return at::unsafe_split_with_sizes(*this, split_sizes, dim);
}
inline std::vector<at::Tensor> Tensor::unsafe_split_with_sizes_symint(
c10::SymIntArrayRef split_sizes, int64_t dim) const {
return at::unsafe_split_with_sizes_symint(*this, split_sizes, dim);
}
} // namespace at
@@ -0,0 +1,39 @@
// 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 unsqueeze(const at::Tensor& self, int64_t dim) {
return paddle::experimental::unsqueeze(self._PD_GetInner(), {dim});
}
} // namespace at
namespace at {
inline at::Tensor Tensor::unsqueeze(int64_t dim) const {
return at::unsqueeze(*this, dim);
}
inline at::Tensor& Tensor::unsqueeze_(int64_t dim) const {
PaddleTensor& inner = const_cast<PaddleTensor&>(tensor_);
paddle::experimental::unsqueeze_(inner, {dim});
return const_cast<at::Tensor&>(*this);
}
} // namespace at
@@ -0,0 +1,38 @@
// 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 view(const at::Tensor& self, at::IntArrayRef size) {
return paddle::experimental::view_shape(self._PD_GetInner(), size.vec());
}
} // namespace at
namespace at {
inline at::Tensor Tensor::view(at::IntArrayRef size) const {
return at::view(*this, size);
}
inline at::Tensor Tensor::view(at::ScalarType dtype) const {
return paddle::experimental::view_dtype(
this->_PD_GetInner(), compat::_PD_AtenScalarTypeToPhiDataType(dtype));
}
} // namespace at
@@ -0,0 +1,27 @@
// 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 {}
namespace at {
inline at::Tensor Tensor::view_as(const at::Tensor& other) const {
return view(other.sizes());
}
} // 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> vsplit(const at::Tensor& self,
int64_t sections) {
return at::tensor_split(self, sections, 0);
}
inline std::vector<at::Tensor> vsplit(const at::Tensor& self,
at::IntArrayRef indices) {
return at::tensor_split(self, indices, 0);
}
} // namespace at
namespace at {
inline std::vector<at::Tensor> Tensor::vsplit(int64_t sections) const {
return at::vsplit(*this, sections);
}
inline std::vector<at::Tensor> Tensor::vsplit(at::IntArrayRef indices) const {
return at::vsplit(*this, indices);
}
} // namespace at
@@ -0,0 +1,106 @@
// 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/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 zeros(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::zeros(
size._PD_ToPaddleIntArray(),
compat::_PD_AtenScalarTypeToPhiDataType(options.dtype()),
phi::CPUPlace());
dense = dense.copy_to(pinned_place, /*blocking=*/true);
return compat::_PD_ConvertToSparseIfNeeded(dense, options.layout());
}
auto dense = paddle::experimental::zeros(
size._PD_ToPaddleIntArray(),
compat::_PD_AtenScalarTypeToPhiDataType(options.dtype()),
options._PD_GetPlace());
return compat::_PD_ConvertToSparseIfNeeded(dense, options.layout());
}
inline at::Tensor zeros(at::IntArrayRef size,
::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.value_or(c10::get_default_dtype_as_scalartype()))
.layout(layout)
.device(device.value_or(at::kCPU))
.pinned_memory(pin_memory);
return zeros(size, options);
}
inline at::Tensor zeros_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::zeros(
size._PD_ToPaddleIntArray(),
compat::_PD_AtenScalarTypeToPhiDataType(options.dtype()),
phi::CPUPlace());
dense = dense.copy_to(pinned_place, /*blocking=*/true);
return compat::_PD_ConvertToSparseIfNeeded(dense, options.layout());
}
auto dense = paddle::experimental::zeros(
size._PD_ToPaddleIntArray(),
compat::_PD_AtenScalarTypeToPhiDataType(options.dtype()),
options._PD_GetPlace());
return compat::_PD_ConvertToSparseIfNeeded(dense, options.layout());
}
inline at::Tensor zeros_symint(c10::SymIntArrayRef size,
::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.value_or(c10::get_default_dtype_as_scalartype()))
.layout(layout)
.device(device.value_or(at::kCPU))
.pinned_memory(pin_memory);
return zeros_symint(size, options);
}
} // namespace at
@@ -0,0 +1,93 @@
// 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/api/include/sparse_api.h"
#include "paddle/phi/common/place.h"
namespace at {
inline at::Tensor zeros_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 layout = options.layout();
if (layout == c10::kStrided && (self.is_sparse() || self.is_sparse_csr())) {
layout = self.layout();
}
auto dtype = options.dtype();
if (dtype == c10::ScalarType::Undefined) {
dtype = self.scalar_type();
}
paddle::Tensor base = self._PD_GetInner();
if (self.is_sparse() || self.is_sparse_csr()) {
base = paddle::experimental::sparse::to_dense(base);
}
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::zeros_like(
base, 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 {
dense = paddle::experimental::zeros_like(
base,
compat::_PD_AtenScalarTypeToPhiDataType(dtype),
options._PD_GetPlace());
}
return compat::_PD_ConvertToSparseIfNeeded(dense, layout);
}
inline at::Tensor zeros_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 zeros_like(self, options, memory_format);
}
} // namespace at