Skip to content

Add typed mdspan Tensor views#909

Closed
ianmccul wants to merge 12 commits into
masterfrom
feature/mdspan-tensor-views
Closed

Add typed mdspan Tensor views#909
ianmccul wants to merge 12 commits into
masterfrom
feature/mdspan-tensor-views

Conversation

@ianmccul

@ianmccul ianmccul commented Jun 17, 2026

Copy link
Copy Markdown
Collaborator

This PR adds a small std::mdspan-style view layer and a typed, ranked TensorT view type for Cytnx Tensor storage.

The motivation is to create a safer intermediate representation for C++ kernels. The current Tensor API carries dtype, rank, layout, and device information almost entirely as runtime state. That is flexible, but it makes low-level kernels easy to misuse: the kernel has to rediscover and re-check properties that could be represented directly in the C++ type. TensorT<T, Rank, Access, Layout> moves the most important parts of that contract into the type system while still sharing storage with the existing Tensor implementation.

The PR is intentionally limited to the core view layer. It does not replace existing kernels, add LAPACK wrappers, or change public Python behavior.

What this adds

  • include/mdspan.hpp: a small C++20 std::mdspan-compatible subset in cytnx::stdex.
    • dynamic extents with fixed compile-time rank
    • layout_right
    • layout_stride
    • element access and stride/extent queries
  • Tensor::as_mdspan<T, Rank>(): returns a non-owning typed layout_stride mdspan preserving the Tensor's current logical layout, including permuted Tensor layouts.
  • Tensor::as_right_mdspan<T, Rank>(): mutates the Tensor to contiguous storage and returns a non-owning typed layout_right mdspan.
  • TensorT<T, Rank, Access, Layout>: a typed, ranked, storage-owning Tensor view.
  • make_tensor_t<T, Rank, Access>(): creates a TensorT preserving the current Tensor layout.
  • make_right_tensor_t<T, Rank, Access>(): creates a contiguous layout_right TensorT.
  • to_tensor(): converts a compatible TensorT back to a legacy Tensor without copying.
  • Variant helpers such as RealTensor<Rank>, ComplexTensor<Rank>, NumericTensor<Rank>, and make_tensor<Variant>() for dispatching legacy Tensor objects to typed TensorT alternatives.

TensorT

The TensorT type is the 'maximally compile-time' version of a tensor. It is templated by:

  • T - the type (in principle this is not restricted to the 12 types supported by cytnx::Tensor, but could support other floating point formats, for example)
  • Rank - compile-time integer indicating the number of tensor legs. An extension to dynamic rank is possible in the future, coupled with a dynamic_mdspan, that meets the required API of mdspan that is used by TensorT but allows a dynamic rank.
  • Access - this denotes whether the typed view is a host CPU view or a CUDA view. The cuda_access class contains a device number that tracks which GPU manages the memory of the tensor.
  • Layout - this is the mdspan Layout type. Typically this will be layout_stride, or the row-major format used by cytnx::Tensor in contiguous mode corresponds to layout_right. The current prototype of the forthcoming mdspan-based LAPACK wrappers generally uses layout_right for dense matrix kernels, although this could be overloaded with other layouts in the future.

TensorT stores its data via a std::shared_ptr<T>. Interoperability with cytnx::Tensor is via a custom deleter that manages an instance of boost::intrusive_ptr<Storage_base>. A TensorT that is constructed from a cytnx::Tensor will share storage with it, and can be converted back to a cytnx::Tensor. It is also possible to construct a host TensorT directly, without using boost::intrusive_ptr<Storage_base>. This is useful for short-lived temporary data or other situations where a TensorT will never need to be converted to a cytnx::Tensor, and will be somewhat more efficient. Direct CUDA allocation is intentionally not added in this PR; that should use a CUDA-specific allocator later.

Examples

A non-owning mdspan view of a Tensor:

cytnx::Tensor tensor({2, 3}, cytnx::Type.Double);
auto view = tensor.as_mdspan<cytnx_double, 2>();

view(1, 2) = 4.0;

This will generate extremely fast code, much faster than any other method I know of to access the elements of a Tensor.

For a permuted Tensor, as_mdspan() preserves the logical layout using strides:

cytnx::Tensor tensor = cytnx::arange(0, 24, 1, cytnx::Type.Double).reshape({2, 3, 4});
cytnx::Tensor permuted = tensor.permute({1, 0, 2});

auto view = permuted.as_mdspan<cytnx_double, 3>();
// view.extent(0) == 3, view.extent(1) == 2, view.extent(2) == 4
// view.stride(...) reflects the Tensor mapper metadata.

If a kernel requires ordinary row-major contiguous data, use as_right_mdspan():

cytnx::Tensor tensor = cytnx::arange(0, 24, 1, cytnx::Type.Double).reshape({2, 3, 4});
cytnx::Tensor permuted = tensor.permute({1, 0, 2});

auto view = permuted.as_right_mdspan<cytnx_double, 3>();
// permuted has been made contiguous, and view uses layout_right.

A storage-owning typed TensorT view:

cytnx::Tensor tensor({2, 3}, cytnx::Type.Double);
auto view = cytnx::make_tensor_t<cytnx_double, 2>(tensor);

view(1, 2) = 5.0;

Unlike as_mdspan(), the TensorT view keeps the underlying storage alive even if the original Tensor object goes away:

cytnx::HostTensorT<cytnx_double, 2> view;
{
  cytnx::Tensor tensor({2, 3}, cytnx::Type.Double);
  view = cytnx::make_tensor_t<cytnx_double, 2>(tensor);
}

view(0, 0) = 1.0;  // storage is still owned by the TensorT view

It is also straightforward to construct a TensorT directly, without going through legacy Tensor storage:

cytnx::HostTensorT<cytnx_double, 2, cytnx::stdex::layout_right> matrix({2, 3});
matrix(1, 2) = 7.0;

Round-tripping back to legacy Tensor is possible when the TensorT layout can be represented by the current Cytnx Tensor metadata:

cytnx::Tensor tensor = cytnx::arange(0, 6, 1, cytnx::Type.Double).reshape({2, 3});
auto view = cytnx::make_tensor_t<cytnx_double, 2>(tensor);

cytnx::Tensor roundtrip = cytnx::to_tensor(view);
// roundtrip shares storage with view/tensor; no data copy is made.

General arbitrary strided mdspan views are not always representable by the current Tensor mapper metadata. to_tensor() therefore accepts contiguous row-major layouts and contiguous layouts up to a permutation of axes, and rejects non-representable stride patterns.

A variant dispatch helper can select an appropriate typed view from a runtime Tensor:

using Matrix = cytnx::NumericTensor<2>;

cytnx::Tensor tensor({4, 4}, cytnx::Type.Double);
Matrix matrix = cytnx::make_tensor<Matrix>(tensor);

std::visit([](auto &typed_matrix) {
  using T = typename std::decay_t<decltype(typed_matrix)>::element_type;
  // typed_matrix is TensorT<T, 2, ...>
}, matrix);

Current limitations

This is deliberately not a full std::mdspan implementation. It implements the subset needed by the Tensor view work: fixed compile-time rank, dynamic extents, layout_right, layout_stride, and basic indexing/query operations.

This is compatible with Ivana's draft PR #860, which adds the reference/Kokkos mdspan implementation. If #860 lands, this PR does not need to keep its own include/mdspan.hpp; the Tensor/TensorT view layer should be able to use the reference implementation instead. The main adjustment would be changing the namespace where the mdspan types and layouts are found.

The first version also does not attempt to encode arbitrary offset views, submdspan, or every possible Tensor layout. Those can be added later if and when a kernel or Tensor API actually needs them.

TensorT currently uses a std::shared_ptr<T> owner. When it is created from legacy Tensor, the shared pointer uses a deleter that holds the existing intrusive storage pointer alive. This is an adapter around the current storage implementation, not a new storage backend.

Where this is headed

The intended use of this layer is to make TensorT the C++ boundary type for kernels. The legacy Tensor API can remain the flexible runtime-typed interface exposed to users, while internal kernels can take arguments whose requirements are visible in the C++ function signature:

void kernel(cytnx::MatrixT<cytnx_double, cytnx::host_access, cytnx::stdex::layout_right> matrix);

This makes scalar type, rank, access backend, and layout part of the overload set instead of a set of runtime checks repeated inside each kernel. It also gives a cleaner way to isolate CPU and CUDA implementations: CPU kernels can take host_access views, CUDA kernels can take cuda_access views, and CPU-only code does not need scattered #ifdef UNI_GPU branches or runtime device == Device.cpu checks.

Legacy Tensor objects can then be lowered at the API boundary into a typed view or a typed variant, and the implementation can dispatch from there:

using Matrix = cytnx::NumericTensor<2>;
Matrix matrix = cytnx::make_tensor<Matrix>(input);
std::visit([](auto &typed_matrix) {
  // call the matching typed kernel
}, matrix);

The planned follow-up sequence is:

  1. Add a generic kernel dispatch mechanism for fixed typed views and std::variant-based typed view sets. A prototype of that layer is already working locally, but it is kept out of this PR so the basic Tensor/TensorT conversion layer can be reviewed first.
  2. Add mdspan-based LAPACK wrappers. The CPU-side prototype is already implemented locally, although the final naming and API surface still need some settling.
  3. Add cuSOLVER backends for CUDA TensorT views, once the LAPACK-side API is stable.
  4. Add a higher-level TensorT-based linalg API that can allocate new storage for output tensors, not just operate on caller-provided mdspan views.
  5. Start porting existing higher-level functions to use TensorT internally.

The intended migration path is incremental. Since Tensor and TensorT can convert in both directions while sharing storage, a TensorT-based implementation can temporarily convert back to legacy Tensor, call an existing function, and convert back to TensorT (as long as the TensorT remains in a permutation-compatible layout). That means existing functionality does not need to be ported all at once; individual kernels and higher-level routines can move over piece by piece.

Verification

  • cmake --build build-mdspan --target test_main -j 16
  • ./build-mdspan/tests/test_main --gtest_filter=MdspanTest.*:TensorTTest.*:TensorTest.as_mdspan*:TensorTest.as_right_mdspan*
  • git diff --check master..HEAD

Co-authored-by: Codex codex@openai.com

@ianmccul
ianmccul marked this pull request as draft June 17, 2026 01:02

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a typed, ranked tensor view representation called TensorT along with a custom minimal implementation of mdspan to allow non-owning multi-dimensional views of Tensor storage. Feedback on the changes highlights three main areas of improvement: enforcing const-correctness in as_mdspan by providing both const and non-const overloads, supporting rank-0 tensors in memory_order_from_strides instead of throwing an error, and guarding against out-of-bounds array accesses in extents when the rank is zero.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread include/Tensor.hpp
Comment on lines +538 to +567
template <typename T, std::size_t Rank>
auto as_mdspan() const {
using element_type = std::remove_cv_t<T>;
using extents_type = stdex::dextents<std::size_t, Rank>;
using mapping_type = typename stdex::layout_stride::template mapping<extents_type>;

cytnx_error_msg(this->dtype() != Type_class::cy_typeid_v<element_type>,
"[ERROR] Attempt to convert dtype %d (%s) to mdspan of type %s",
this->dtype(), Type_class::getname(this->dtype()).c_str(),
Type_class::getname(Type_class::cy_typeid_v<element_type>).c_str());
cytnx_error_msg(
this->rank() != Rank, "[ERROR] Attempt to view rank-%llu Tensor as rank-%llu mdspan.%s",
static_cast<unsigned long long>(this->rank()), static_cast<unsigned long long>(Rank), "\n");

std::array<std::size_t, Rank> extents{};
std::array<std::size_t, Rank> strides{};
for (std::size_t i = 0; i < Rank; ++i) {
extents[i] = static_cast<std::size_t>(this->_impl->_shape[i]);
}
std::size_t step = 1;
for (std::size_t i = Rank; i-- > 0;) {
const std::size_t axis = static_cast<std::size_t>(this->_impl->_invmapper[i]);
cytnx_error_msg(axis >= Rank, "[ERROR] Invalid Tensor mapper metadata.%s", "\n");
strides[axis] = step;
step *= extents[axis];
}

return stdex::mdspan<T, extents_type, stdex::layout_stride>(
this->ptr_as<T>(), mapping_type(extents_type(extents), strides));
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The as_mdspan method is marked const but returns a mutable stdex::mdspan<T, ...> if T is non-const. This violates const-correctness, as it allows modifying the underlying data of a const Tensor. To enforce const-correctness, we should provide both non-const and const overloads of as_mdspan. The const overload should return a read-only mdspan with a const element type.

    template <typename T, std::size_t Rank>
    auto as_mdspan() {
      using element_type = std::remove_cv_t<T>;
      using extents_type = stdex::dextents<std::size_t, Rank>;
      using mapping_type = typename stdex::layout_stride::template mapping<extents_type>;

      cytnx_error_msg(this->dtype() != Type_class::cy_typeid_v<element_type>,
                      "[ERROR] Attempt to convert dtype %d (%s) to mdspan of type %s",
                      this->dtype(), Type_class::getname(this->dtype()).c_str(),
                      Type_class::getname(Type_class::cy_typeid_v<element_type>).c_str());
      cytnx_error_msg(
        this->rank() != Rank, "[ERROR] Attempt to view rank-%llu Tensor as rank-%llu mdspan.%s",
        static_cast<unsigned long long>(this->rank()), static_cast<unsigned long long>(Rank), "\n");

      std::array<std::size_t, Rank> extents{};
      std::array<std::size_t, Rank> strides{};
      for (std::size_t i = 0; i < Rank; ++i) {
        extents[i] = static_cast<std::size_t>(this->_impl->_shape[i]);
      }
      std::size_t step = 1;
      for (std::size_t i = Rank; i-- > 0;) {
        const std::size_t axis = static_cast<std::size_t>(this->_impl->_invmapper[i]);
        cytnx_error_msg(axis >= Rank, "[ERROR] Invalid Tensor mapper metadata.%s", "\n");
        strides[axis] = step;
        step *= extents[axis];
      }

      return stdex::mdspan<element_type, extents_type, stdex::layout_stride>(
        this->ptr_as<element_type>(), mapping_type(extents_type(extents), strides));
    }

    template <typename T, std::size_t Rank>
    auto as_mdspan() const {
      using element_type = const std::remove_cv_t<T>;
      using extents_type = stdex::dextents<std::size_t, Rank>;
      using mapping_type = typename stdex::layout_stride::template mapping<extents_type>;

      cytnx_error_msg(this->dtype() != Type_class::cy_typeid_v<std::remove_cv_t<element_type>>,
                      "[ERROR] Attempt to convert dtype %d (%s) to mdspan of type %s",
                      this->dtype(), Type_class::getname(this->dtype()).c_str(),
                      Type_class::getname(Type_class::cy_typeid_v<std::remove_cv_t<element_type>>).c_str());
      cytnx_error_msg(
        this->rank() != Rank, "[ERROR] Attempt to view rank-%llu Tensor as rank-%llu mdspan.%s",
        static_cast<unsigned long long>(this->rank()), static_cast<unsigned long long>(Rank), "\n");

      std::array<std::size_t, Rank> extents{};
      std::array<std::size_t, Rank> strides{};
      for (std::size_t i = 0; i < Rank; ++i) {
        extents[i] = static_cast<std::size_t>(this->_impl->_shape[i]);
      }
      std::size_t step = 1;
      for (std::size_t i = Rank; i-- > 0;) { 
        const std::size_t axis = static_cast<std::size_t>(this->_impl->_invmapper[i]);
        cytnx_error_msg(axis >= Rank, "[ERROR] Invalid Tensor mapper metadata.%s", "\n");
        strides[axis] = step;
        step *= extents[axis];
      }

      return stdex::mdspan<element_type, extents_type, stdex::layout_stride>(
        this->ptr_as<element_type>(), mapping_type(extents_type(extents), strides));
    }

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cytnx tensors have reference semantics, and it is always possible to get a non-const pointer to the storage even if the tensor itself const (similarly to shared_ptr<T>). It is a good question though whether TensorT should follow the same model. It shares the same representation as a cytnx::Tensor, so following the same (lack of) const correctness is the 'obvious' approach, but it doesn't have to.

Another possibility is to have two different types, say TensorRef that has reference semantics, and give TensorT value semantics. I don't think that has to affect the python interface, since PyObject does its own reference counting anyway? TensorRef would then be for legacy compatability with cytnx::Tensor, and could go away eventually. /gemini thoughts?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current design of TensorT is intended to provide a compile-time typed view layer that can safely interoperate with the existing cytnx::Tensor storage. You are correct that cytnx::Tensor currently follows reference semantics similar to shared_ptr, where the Tensor object itself is not const-correct regarding its underlying data.

If we were to introduce a TensorRef type to separate reference semantics from value semantics, it would be a significant architectural shift. Given the current scope of this PR—which is to provide a safer, typed kernel-facing interface—I recommend maintaining the current design for now. The TensorT type already provides a clear boundary for kernels to express their requirements (dtype, rank, layout) in the type system.

If you find that the lack of const-correctness in TensorT becomes a source of bugs in kernel development, we can revisit the TensorRef vs TensorT (value semantics) split as a future improvement. For now, the current implementation is consistent with the existing Cytnx codebase while providing the requested type safety for kernels.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another reason for keeping the existing (loose) const correctness is that TensorT models an mdspan. It isn't literally an mdspan object, but it it contains all of the public members that an mdspan is expected to have, so for functions that use concepts or other method to take a parameter that looks mdspan-like, you can pass a TensorT directly without going via mdspan.

mdspan does have support for const vs non-const spans, but that is hard to use in practice. A const TensorT would need to model mdspan<const T, ...>, and I don't think that is possible in C++.

If TensorT does not model mdspan, then every time we want to use it in forwarding to some kernel function it requires another layer, probably some free function to_mdspan(t) or similar, on every argument. That function could then return a mdspan<T, ...> or mdspan<const T, ...> depending on whether t is const or not. This could be handled automatically at the kernel dispatch layer, at the cost of making the dispatch mechanism slightly more complicated, but its a relatively localized change. Only functions that do not go via the central dispatching mechanism the user would need to convert themselves to mdspan.

If TensorT is changed to value semantics in the future, this would be necessary anyway. So I suggest revisiting this in the future, if we want to make TensorT have value semantics.

Comment thread include/TensorT.hpp
Comment thread include/mdspan.hpp

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9b9c72046f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread include/cytnx.hpp
@codecov

codecov Bot commented Jun 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 71.55172% with 66 lines in your changes missing coverage. Please review.
✅ Project coverage is 30.06%. Comparing base (7b63aad) to head (a1366a2).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
include/TensorT.hpp 70.22% 1 Missing and 38 partials ⚠️
include/mdspan.hpp 74.54% 0 Missing and 14 partials ⚠️
include/Tensor.hpp 71.42% 0 Missing and 6 partials ⚠️
include/TensorT_traits.hpp 71.42% 0 Missing and 6 partials ⚠️
include/TensorT_cpu.hpp 75.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #909      +/-   ##
==========================================
+ Coverage   29.79%   30.06%   +0.27%     
==========================================
  Files         242      246       +4     
  Lines       35546    35779     +233     
  Branches    14777    14870      +93     
==========================================
+ Hits        10590    10757     +167     
- Misses      17681    17682       +1     
- Partials     7275     7340      +65     
Flag Coverage Δ
cpp 29.67% <71.55%> (+0.28%) ⬆️
python 52.71% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
C++ backend 31.38% <71.55%> (+0.30%) ⬆️
Python bindings 17.11% <ø> (ø)
Python package 52.71% <ø> (ø)
Files with missing lines Coverage Δ
include/TensorT_cpu.hpp 75.00% <75.00%> (ø)
include/Tensor.hpp 54.41% <71.42%> (+1.36%) ⬆️
include/TensorT_traits.hpp 71.42% <71.42%> (ø)
include/mdspan.hpp 74.54% <74.54%> (ø)
include/TensorT.hpp 70.22% <70.22%> (ø)

... and 1 file with indirect coverage changes


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 7b63aad...a1366a2. Read the comment docs.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@ianmccul

Copy link
Copy Markdown
Collaborator Author

The latest update to this PR fixes a CI failure: Cytnx itself builds with CMAKE_CXX_STANDARD 20, but the exported Cytnx::cytnx target did not propagate that requirement to downstream consumers. Since cytnx.hpp now includes public headers using concepts, consumers also need C++20.

The latest commit adds

target_compile_features(cytnx PUBLIC cxx_std_20)

to CMakeLists.txt.

@IvanaGyro

IvanaGyro commented Jun 17, 2026

Copy link
Copy Markdown
Member

TensorT is a good idea. However it seems reimplement a lot of mdspan features. And including TensorT, there will be a lot of tensor related classes, Tensor implementation, tensor proxy, accessor, tensor. The downstream function has to check it works correctly from different interface.

I suggest make Tensor stride based and replace tensor implementation with the TensorT which directly hold typed storage with boost's pointer (which is faster than share_ptr). This can remove untyped tensor implementation and accessor and simplify tensor proxy. The drawback is that we cannot have compile time rank check for TensorT. This problem is minimal now because the public interface always input rank info at runtime.

BTW, this repo use ..._H_ convention for header guard even if the header file is .hpp.

@ianmccul

Copy link
Copy Markdown
Collaborator Author

This PR include one small file, include/mdspan.hpp that replicates the important parts of std::mdspan into namespace cytnx::stdex. That is only because at the time of this PR, there is no mdspan implementation in cytnx master branch. It was never intended to be permanent, just until there is another mdspan implementation or eventually std::mdspan.

I did not add kokkos/mdspan mysef because it is a fairly big library, with several configuration options (especially relevant, which namespace should it appear in), and having two PR's including kokkos/mdspan, no doubt in slightly different ways, would cause merge difficulties later on. Since #860 looks like it is now a clean PR that adds kokkos/mdspan, I can rebase this PR on that branck and remove include/mdspan.hpp.

@ianmccul

Copy link
Copy Markdown
Collaborator Author

For an allocating path, the dominant costs are the allocation itself, possible zero-initialization, cache effects, and whatever tensor operation follows. The difference between constructing/copying a shared_ptr and an intrusive_ptr is tiny compared with malloc/new[], and utterly irrelevant compared with LAPACK/CUDA/tensor contractions.

Where intrusive_ptr can matter is in very high-frequency ownership churn where no allocation happens, e.g. repeatedly copying small handle objects in tight loops. That pattern should never appear in a high-performance tensor library. For views over existing Tensor storage, the ownership handle is only there to keep the legacy storage alive; kernels should pass the view itself, not repeatedly churn ownership in hot loops.

@ianmccul

Copy link
Copy Markdown
Collaborator Author

On removing the untyped Tensor implementation, yes that is the long-term goal of this PR. I am not sure how that looks like yet, but some kind of variant over TensorT classes. The design of the kernel dispatch mechanism in the forthcoming PR's allows any variant of some combination of TensorT to be used to dispatch to a back-end kernel, so it will be very flexible. Maybe there does not need to be any C++ untyped Tensor class at all, but C++ code can use whatever combination of TensorT it needs (single class, or std::variant of different types, ranks, host/cuda, etc). Python just stores the TensorT object directly in its PyObject. And since that provides the reference counting, the C++ refrence counting layer could be removed and the C++ tensor uses value semantics. That removes the question of shared_ptr / intrusive_ptr completely.

@ianmccul

Copy link
Copy Markdown
Collaborator Author

BTW, this repo use ..._H_ convention for header guard even if the header file is .hpp.

Fixed in latest commit

@ianmccul

Copy link
Copy Markdown
Collaborator Author

no longer working on this project.

@ianmccul ianmccul closed this Jun 17, 2026
@ianmccul
ianmccul deleted the feature/mdspan-tensor-views branch June 17, 2026 09:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants