refactor(linalg): typed GPU dispatch for Outer + retire Kron GPU promotion trait (#1003)#1101
Conversation
…#1003) Replace the legacy generated 12x12 cuOuter_ii dtype-pair function table (144 wrappers + a 2D lookup) with a single std::variant-based cuOuter_dispatch, following the elementwise cu*_dispatch design (#1003; #650/#938 direction). Outer.cpp already promotes both operands to the output dtype before dispatch, so out/Lin/Rin share out->dtype(); cuOuter_dispatch visits that single type via as_storage_variant and routes exactly as the old diagonal table entries did: floating/complex -> cuBLAS GER (A = 1 * x * y^T), integer/bool -> the custom cuOuter_kernel. Behaviour and numerics are unchanged; this only changes how the kernel is selected. Removed: the 144 cuOuter_internal_* wrappers (cuOuter_internal.cu/.hpp), the cuOuter_ii member (linalg_internal_interface.hpp) and its ~144-line population (linalg_internal_interface.cpp). Net -1600 lines. The CPU Outer_ii table is untouched (out of #1003's CUDA scope). Testing: GPU Kron/Outer suite 9/9 on an RTX 4070 Ti SUPER (CUDA 13.0), incl. the mixed ComplexFloat x Double -> ComplexDouble promotion discriminator and GPU-vs-CPU cross-checks over all dtypes; CPU Outer tests still pass; both GPU and CPU debug builds clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…put dispatch (#1003) Kron.cpp was the last user of the bespoke GPU promotion trait hierarchy (type_promote_gpu_t / type_promote_from_gpu_pointer / _t) in Type.hpp. Its GPU branch double-visited the input pointers and derived the output type from that trait. Cast both operands to the promoted output dtype up front (a no-op ref when the dtype already matches) and dispatch on the single output type via out.gpu_ptr(). This is numerically identical to the kernel's per-element TO(Lin)*TO(Rin) cast, drops the kernel instantiations from 11x11 to 11, and lets the trait hierarchy be deleted from Type.hpp. Follows the same uniform-output-type shape now used by Outer. Testing: GPU Kron suite 5/5 (incl. mixed ComplexFloat x Double -> ComplexDouble promotion + GPU-vs-CPU cross-check over all dtypes) on an RTX 4070 Ti SUPER; CPU Kron tests still pass; GPU and CPU debug builds clean. No remaining users of the removed traits (verified by grep; only historical comments mention them). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request refactors the GPU implementation of the Outer and Kron operations by replacing the legacy 12x12 function table with a typed std::visit dispatch on the output dtype. This change significantly reduces boilerplate code and retires the bespoke type_promote_from_gpu_pointer_t trait. The review feedback recommends improving the signature of the newly introduced cuOuter_dispatch function to adhere to the repository style guide. Specifically, it suggests passing input storage pointers by const reference, passing scalar size parameters by value, and replacing the legacy cytnx_uint64 typedef with standard C++ types.
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.
| void cuOuter_dispatch(boost::intrusive_ptr<Storage_base> &out, | ||
| boost::intrusive_ptr<Storage_base> &Lin, | ||
| boost::intrusive_ptr<Storage_base> &Rin, const cytnx_uint64 &j1, | ||
| const cytnx_uint64 &j2); |
There was a problem hiding this comment.
For the newly introduced cuOuter_dispatch function, we should improve const-correctness and adhere to the repository style guide by:
- Passing the read-only input storage pointers
LinandRinasconst boost::intrusive_ptr<Storage_base>&instead of non-const references. - Passing the small scalar size parameters
j1andj2by value instead ofconst&(per Repository Style Guide, line 30). - Avoiding the legacy
cytnx_uint64typedef for size parameters in new code, preferring standard C++ types likeuint64_t(per Repository Style Guide, line 34).
| void cuOuter_dispatch(boost::intrusive_ptr<Storage_base> &out, | |
| boost::intrusive_ptr<Storage_base> &Lin, | |
| boost::intrusive_ptr<Storage_base> &Rin, const cytnx_uint64 &j1, | |
| const cytnx_uint64 &j2); | |
| void cuOuter_dispatch(boost::intrusive_ptr<Storage_base> &out, | |
| const boost::intrusive_ptr<Storage_base> &Lin, | |
| const boost::intrusive_ptr<Storage_base> &Rin, | |
| uint64_t j1, uint64_t j2); |
| void cuOuter_dispatch(boost::intrusive_ptr<Storage_base> &out, | ||
| boost::intrusive_ptr<Storage_base> &Lin, | ||
| boost::intrusive_ptr<Storage_base> &Rin, const cytnx_uint64 &j1, | ||
| const cytnx_uint64 &j2) { |
There was a problem hiding this comment.
Update the definition of cuOuter_dispatch to match the improved signature with const-correct input storage pointers, by-value scalar parameters, and standard C++ types instead of legacy typedefs.
void cuOuter_dispatch(boost::intrusive_ptr<Storage_base> &out,
const boost::intrusive_ptr<Storage_base> &Lin,
const boost::intrusive_ptr<Storage_base> &Rin,
uint64_t j1, uint64_t j2) {
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5a0325e058
ℹ️ 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".
| @@ -499,29 +499,6 @@ namespace cytnx { | |||
| template <typename TL, typename TR> | |||
| using type_promote_from_pointer_t = typename type_promote_from_pointer<TL, TR>::type; | |||
There was a problem hiding this comment.
Update the CUDA type-promotion tests
Removing Type_class::type_promote_gpu_t here leaves the #ifdef UNI_GPU static assertions in tests/Type_test.cpp still referencing that alias, so any CUDA test build with RUN_TESTS=ON fails to compile before the GPU tests can run. Please update or remove those assertions as part of retiring the GPU promotion trait.
Useful? React with 👍 / 👎.
ianmccul
left a comment
There was a problem hiding this comment.
Tensor Outer(const Tensor &lhs, const Tensor &rhs) {
cytnx_error_msg(lhs.is_void(), "[ERROR] lhs must be initialized.%s", "\n");
cytnx_error_msg(rhs.is_void(), "[ERROR] rhs must be initialized.%s", "\n");
cytnx_error_msg(lhs.device() != rhs.device(),
"[ERROR] the two tensors must be on the same device.%s", "\n");
cytnx_error_msg(lhs.rank() != 1, "[ERROR] tensor #1 should have rank-1.%s", "\n");
cytnx_error_msg(rhs.rank() != 1, "[ERROR] tensor #2 should have rank-1.%s", "\n");
return Kron(lhs, rhs).reshape(std::vector<cytnx_uint64>{lhs.shape()[0], rhs.shape()[0]});
}|
Addressing @ianmccul's review comment (replace the Outer dispatch with |
|
To use Codex here, create an environment for this repo. |
|
The second goal of this PR — retiring |
|
To use Codex here, create an environment for this repo. |
|
Closing as superseded by #1105 and #1106. This PR's refactor — route On current The GPU test coverage from the sibling PR #1100 is being salvaged into |
Problem
The last of #1003:
OuterandKronwere the two remaining GPU ops still on legacy dispatch.cuOuter_iidtype-pair table (144 wrapper functions + a 2D lookup) — exactly the kind of "old generated dtype table" Merge CUDA unary and binary elementwise operations through one typed kernel framework #1003 wants gone.type_promote_gpu_t/type_promote_from_gpu_pointer/_t) inType.hpp.Fix
Two commits:
Outer → typed dispatch. Replace
cuOuter_iiwith a singlestd::variant-basedcuOuter_dispatch(the elementwisecu*_dispatchdesign).Outer.cppalready promotes both operands to the output dtype, soout/Lin/Rinshareout->dtype(); the dispatch visits that one type viaas_storage_variantand routes exactly as the old diagonal table entries did — floating/complex → cuBLAS GER (A = 1·x·yᵀ), integer/bool → the customcuOuter_kernel. Deletes the 144 wrappers + the table + its population. Net ≈ −1600 lines.Kron → retire the trait. Cast both operands to the promoted output dtype up front (a no-op ref when it already matches) and dispatch on the single output type via
out.gpu_ptr(). This is numerically identical to the kernel's per-elementTO(Lin)*TO(Rin)cast, cuts kernel instantiations from 11×11 → 11, and lets the trait hierarchy be deleted fromType.hpp(no remaining users — only historical comments mention it).Both changes are GPU-only and behaviour-preserving — they change how a kernel is selected, not the math. The CPU
Outer_ii/type_promote_from_pointer_tpaths are untouched (out of #1003's CUDA scope).Testing
On an RTX 4070 Ti SUPER (CUDA 13.0,
sm_89):ComplexFloat × Double → ComplexDoublepromotion discriminator (output dtype and value) and GPU-vs-CPU cross-checks over every dtype (from test(linalg): GPU correctness coverage for Kron and Outer (#1003, confirms #999) #1100).BlockUniTensor::Tracebug, a large-shapeMulOOM) — those tests run byte-identical code on this branch and its base, so the migration does not affect them.Notes
masteras the stack merges.