Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
ecde5bb
[WS2][CUDA][MiniMax-H3] sp_norm_adaln: sequence-parallel norm/AdaLN/g…
fusheng-ji Oct 7, 2026
1bfeb6d
Merge tp_adaln_3mod evidence into PR #494
fusheng-ji Oct 7, 2026
63b8caa
[WS2][MiniMax-H3] sp_norm_adaln: 8 x B200 NCCL evidence report and fi…
fusheng-ji Oct 7, 2026
ba00517
Merge inherited RFC 420 review fixes into PR #494
fusheng-ji Oct 8, 2026
24ddebd
Merge the H3 prior-art runner into PR #494
fusheng-ji Oct 8, 2026
3d440be
Merge the H3 prior-art runner fix into PR #494
fusheng-ji Oct 8, 2026
0ca4ced
Merge the H3 prior-art figure fix into PR #494
fusheng-ji Oct 8, 2026
85a3403
Merge the H3 prior-art figure fix into PR #494
fusheng-ji Oct 8, 2026
1968e13
Merge the H3 prior-art evidence into PR #494
fusheng-ji Oct 8, 2026
407fa0f
Merge the H3 prior-art figure layout fix into PR #494
fusheng-ji Oct 8, 2026
d960006
test(h3): consolidate native validation tests for review file limit
fusheng-ji Oct 9, 2026
86c168f
fix(h3): address automated review bounds, device, and benchmark findings
fusheng-ji Oct 9, 2026
b74aa08
Merge test-h3 refactor and migrate H3 SP kernels, tools, and tests
fusheng-ji Oct 10, 2026
91faa19
Merge migrated H3 TP dependency and retain SP review fixes
fusheng-ji Oct 10, 2026
93710b9
Sync migrated H3 stack ancestry
fusheng-ji Oct 10, 2026
3a56817
Merge migrated validation documentation from PR #493
fusheng-ji Oct 10, 2026
e1db135
Merge feat/h3-tp-adaln-3mod into feat/h3-sp-norm-adaln
fusheng-ji Oct 13, 2026
919ba8a
Merge feat/h3-tp-adaln-3mod into feat/h3-sp-norm-adaln
fusheng-ji Oct 13, 2026
00ddc50
Merge feat/h3-tp-adaln-3mod into feat/h3-sp-norm-adaln
fusheng-ji Oct 13, 2026
732d314
Merge feat/h3-tp-adaln-3mod into feat/h3-sp-norm-adaln
fusheng-ji Oct 13, 2026
4705a44
Merge feat/h3-tp-adaln-3mod into feat/h3-sp-norm-adaln
fusheng-ji Oct 13, 2026
299592c
Merge feat/h3-tp-adaln-3mod into feat/h3-sp-norm-adaln
fusheng-ji Oct 13, 2026
0d9d102
Merge feat/h3-tp-adaln-3mod into feat/h3-sp-norm-adaln
fusheng-ji Oct 13, 2026
83e8133
Merge feat/h3-tp-adaln-3mod into feat/h3-sp-norm-adaln
fusheng-ji Oct 13, 2026
613d123
Merge feat/h3-tp-adaln-3mod into feat/h3-sp-norm-adaln
fusheng-ji Oct 13, 2026
0033b3a
docs: move the measurement outputs out of the pull request
fusheng-ji Oct 19, 2026
4ef511c
docs: point commit hashes at the rewritten history
fusheng-ji Oct 23, 2026
66129bf
Merge test-h3 into feat/h3-sp-norm-adaln
fusheng-ji Oct 25, 2026
c1081fa
Merge feat/h3-tp-adaln-3mod into feat/h3-sp-norm-adaln
fusheng-ji Oct 26, 2026
69ae865
Merge feat/h3-tp-adaln-3mod into feat/h3-sp-norm-adaln
fusheng-ji Oct 26, 2026
11a85a8
Merge parent (#493) into #494
fusheng-ji Oct 26, 2026
84170d3
Merge parent (#493) into #494
fusheng-ji Oct 26, 2026
3bb3585
Merge feat/h3-tp-adaln-3mod into feat/h3-sp-norm-adaln
fusheng-ji Oct 26, 2026
705027a
Merge feat/h3-tp-adaln-3mod into feat/h3-sp-norm-adaln
fusheng-ji Oct 26, 2026
7497e0e
Merge feat/h3-tp-adaln-3mod into feat/h3-sp-norm-adaln
fusheng-ji Oct 26, 2026
5d50d34
fix(h3): check the lists of the sequence-parallel native entry points…
fusheng-ji Oct 26, 2026
ae76f12
Merge feat/h3-tp-adaln-3mod into feat/h3-sp-norm-adaln
fusheng-ji Oct 26, 2026
7984eb6
Merge feat/h3-tp-adaln-3mod into feat/h3-sp-norm-adaln
fusheng-ji Oct 27, 2026
002fb39
fix(h3): check the modulation index in the sequence-parallel partials…
fusheng-ji Oct 27, 2026
68fc83c
Merge #493 round-5 fixes into #494
fusheng-ji Oct 27, 2026
4d2614b
Merge feat/h3 round-6 upstream sync (#493) into #494
fusheng-ji Oct 29, 2026
2768c0f
test(h3): restore the rmsnorm native validation tests dropped from th…
fusheng-ji Oct 29, 2026
3223b8a
Merge #493 round-7 fix into #494
fusheng-ji Oct 29, 2026
2b211f0
Merge #493 review fixes into #494
fusheng-ji Oct 30, 2026
69235c4
Merge branch 'test-h3' into feat/h3-sp-norm-adaln
maxiaosong1124 Oct 30, 2026
f7230b8
fix(h3): plan an inference-mode row index on every call, and move the…
fusheng-ji Oct 30, 2026
946390f
Merge remote-tracking branch 'origin/feat/h3-sp-norm-adaln' into loca…
fusheng-ji Oct 30, 2026
47620c7
fix(h3): validate rstd as contiguous 1-D in the SP backward entry poi…
fusheng-ji Oct 30, 2026
bb2c365
Merge branch 'local/h3fix/493' into local/h3fix/494
fusheng-ji Oct 30, 2026
769b0bc
fix(h3): one rstd error message for all backward entry points, and re…
fusheng-ji Oct 30, 2026
562841e
Merge branch 'test-h3' into feat/h3-sp-norm-adaln
maxiaosong1124 Oct 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions csrc/bindings/ops.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,25 @@ std::vector<torch::Tensor> h3_gate_residual_backward(torch::Tensor grad, torch::
torch::Tensor tile_begin,
torch::Tensor tile_end,
torch::Tensor seg_first_tile);
std::vector<torch::Tensor> h3_rmsnorm_backward_partials(
torch::Tensor grad, torch::Tensor x, torch::Tensor weight, torch::Tensor rstd,
c10::optional<torch::Tensor> shift, c10::optional<torch::Tensor> scale,
c10::optional<torch::Tensor> index, torch::Tensor dw_rows, torch::Tensor dw_begin,
torch::Tensor dw_end, c10::optional<torch::Tensor> seg_rows,
c10::optional<torch::Tensor> seg_begin, c10::optional<torch::Tensor> seg_end);
std::vector<torch::Tensor> h3_rmsnorm_fold_partials(torch::Tensor dw_partial, torch::Tensor weight,
c10::optional<torch::Tensor> seg_partial,
c10::optional<torch::Tensor> seg_first_tile);
torch::Tensor h3_gate_grad_partials(torch::Tensor grad, torch::Tensor y, torch::Tensor rows,
torch::Tensor tile_begin, torch::Tensor tile_end);
torch::Tensor h3_gate_grad_fold(torch::Tensor partial, torch::Tensor seg_first_tile,
c10::ScalarType dtype);
torch::Tensor h3_rmsnorm_backward_dx(torch::Tensor grad, torch::Tensor x, torch::Tensor weight,
torch::Tensor rstd, c10::optional<torch::Tensor> shift,
c10::optional<torch::Tensor> scale,
c10::optional<torch::Tensor> index);
torch::Tensor h3_gate_residual_backward_dy(torch::Tensor grad, torch::Tensor gate,
torch::Tensor index);
#endif

// PyBind11 Module Registration
Expand Down Expand Up @@ -848,5 +867,28 @@ PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
"Gated-residual backward: exact dy, deterministic sorted segment sum for dgate",
py::arg("grad"), py::arg("y"), py::arg("gate"), py::arg("index"), py::arg("sorted_pos"),
py::arg("tile_begin"), py::arg("tile_end"), py::arg("seg_first_tile"));
m.def("h3_rmsnorm_backward_partials", &h3_rmsnorm_backward_partials,
"WS1 RMSNorm dweight / table-gradient tile partials over explicit row lists (SP)",
py::arg("grad"), py::arg("x"), py::arg("weight"), py::arg("rstd"), py::arg("shift"),
py::arg("scale"), py::arg("index"), py::arg("dw_rows"), py::arg("dw_begin"),
py::arg("dw_end"), py::arg("seg_rows") = py::none(), py::arg("seg_begin") = py::none(),
py::arg("seg_end") = py::none());
m.def("h3_rmsnorm_fold_partials", &h3_rmsnorm_fold_partials,
"WS1 ascending folds of RMSNorm dweight and per-segment table-gradient partials",
py::arg("dw_partial"), py::arg("weight"), py::arg("seg_partial") = py::none(),
py::arg("seg_first_tile") = py::none());
m.def("h3_gate_grad_partials", &h3_gate_grad_partials,
"WS1 d_gate tile partials over explicit row lists (SP)", py::arg("grad"), py::arg("y"),
py::arg("rows"), py::arg("tile_begin"), py::arg("tile_end"));
m.def("h3_gate_grad_fold", &h3_gate_grad_fold,
"WS1 per-segment ascending fold of d_gate partials, cast once", py::arg("partial"),
py::arg("seg_first_tile"), py::arg("dtype"));
m.def("h3_rmsnorm_backward_dx", &h3_rmsnorm_backward_dx,
"Row-local dx of the WS1 RMSNorm(+modulation) backward", py::arg("grad"), py::arg("x"),
py::arg("weight"), py::arg("rstd"), py::arg("shift") = py::none(),
py::arg("scale") = py::none(), py::arg("index") = py::none());
m.def("h3_gate_residual_backward_dy", &h3_gate_residual_backward_dy,
"Row-local d_y = grad * gate[index] of the WS1 gated-residual backward",
py::arg("grad"), py::arg("gate"), py::arg("index"));
#endif
}
8 changes: 5 additions & 3 deletions csrc/cuda/h3/adaln_row_gather.cu
Original file line number Diff line number Diff line change
Expand Up @@ -212,9 +212,10 @@ torch::Tensor h3_adaln_row_gather_backward(torch::Tensor grad, torch::Tensor sor
TORCH_CHECK(grad.is_cuda() && grad.dim() == 3 && grad.is_contiguous(),
"grad must be a contiguous (C, S, H) CUDA tensor");
for (const auto* t : {&sorted_pos, &tile_begin, &tile_end, &seg_first_tile}) {
TORCH_CHECK(t->is_cuda() && t->device() == grad.device() && t->dim() == 1 && t->is_contiguous() &&
TORCH_CHECK(t->is_cuda() && t->device() == grad.device() &&
t->dim() == 1 && t->is_contiguous() &&
t->scalar_type() == at::kLong,
"tile metadata must be contiguous int64 tensors on ", grad.device());
"tile metadata must be contiguous 1-D int64 tensors on grad device");
}
TORCH_CHECK(seg_first_tile.numel() >= 1, "seg_first_tile must hold R + 1 entries");
TORCH_CHECK(tile_begin.numel() == tile_end.numel(), "tile_begin/tile_end length mismatch");
Expand All @@ -225,9 +226,10 @@ torch::Tensor h3_adaln_row_gather_backward(torch::Tensor grad, torch::Tensor sor
const int64_t num_rows = seg_first_tile.numel() - 1;
const int64_t tiles = tile_begin.numel();
TORCH_CHECK(sorted_pos.numel() == seq, "sorted_pos must have S entries");
TORCH_CHECK(chunks > 0 && seq > 0 && hidden > 0 && num_rows > 0,
"gradient dimensions and table row count must be positive");
const c10::cuda::CUDAGuard device_guard(grad.device());
auto out = torch::empty({num_rows, width}, grad.options().dtype(out_dtype));
if (num_rows == 0) return out; // a zero-sized grid is not a valid launch
auto partial = torch::empty({std::max<int64_t>(tiles, 1), width},
grad.options().dtype(at::kFloat));
auto stream = at::cuda::getCurrentCUDAStream();
Expand Down
95 changes: 95 additions & 0 deletions csrc/cuda/h3/gate_residual.cu
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
// 16-bit values is exact in FP32, so this equals the eager VJP bitwise);
// d_gate[i] = sum over positions mapped to i of grad * y, an FP32 segmented
// sum over positions sorted stably by row, in fixed tiles folded in order.
// The tile partials and the fold are also bound separately for sequence
// parallel callers (each tile computed where its rows live, folded in order).

#include <torch/extension.h>
#include <ATen/cuda/CUDAContext.h>
Expand Down Expand Up @@ -262,3 +264,96 @@ std::vector<torch::Tensor> h3_gate_residual_backward(torch::Tensor grad, torch::
C10_CUDA_KERNEL_LAUNCH_CHECK();
return {dy, dgate};
}

// Sequence parallel: the WS1 d_gate tile partials over explicit row lists of
// (grad, y) rows -> (tiles, N) FP32.
torch::Tensor h3_gate_grad_partials(torch::Tensor grad, torch::Tensor y, torch::Tensor rows,
torch::Tensor tile_begin, torch::Tensor tile_end) {
TORCH_CHECK(grad.is_cuda() && grad.dim() == 2 && grad.is_contiguous(),
"grad must be a contiguous (M, N) CUDA tensor");
check_act(y, "y", grad);
for (const auto* t : {&rows, &tile_begin, &tile_end}) {
TORCH_CHECK(t->is_cuda() && t->device() == grad.device() && t->scalar_type() == at::kLong &&
t->dim() == 1 && t->is_contiguous(),
"tile metadata must be contiguous int64 CUDA tensors on grad's device");
}
TORCH_CHECK(tile_begin.numel() == tile_end.numel(), "tile_begin and tile_end differ in length");
const int64_t n = grad.size(1);
const int64_t tiles = tile_begin.numel();
const c10::cuda::CUDAGuard guard(grad.device());
auto partial = torch::empty({tiles, n}, grad.options().dtype(at::kFloat));
if (tiles == 0) return partial;
const int threads = 256;
const int64_t col_blocks64 = (n + threads - 1) / threads;
TORCH_CHECK(col_blocks64 <= 65535, "N=", n, " needs ", col_blocks64,
" column blocks, more than the grid.y limit of 65535");
const unsigned col_blocks = static_cast<unsigned>(col_blocks64);
// The row and tile lists are caller-built: every row must be a row of grad and every tile must
// lie inside the list before the kernel reads through them.
h3_tiles::check_lists(at::cuda::getCurrentCUDAStream(), rows.data_ptr<int64_t>(), rows.numel(),
grad.size(0), tile_begin.data_ptr<int64_t>(), tile_end.data_ptr<int64_t>(),
tiles, nullptr, 0, 0);
H3_DISPATCH(grad.scalar_type(), "h3_gate_grad_partials", [&] {
gate_grad_partial_kernel<T><<<dim3(static_cast<unsigned>(tiles), col_blocks), threads, 0,
at::cuda::getCurrentCUDAStream()>>>(
reinterpret_cast<const T*>(grad.data_ptr()), reinterpret_cast<const T*>(y.data_ptr()),
rows.data_ptr<int64_t>(), tile_begin.data_ptr<int64_t>(), tile_end.data_ptr<int64_t>(),
partial.data_ptr<float>(), n);
});
C10_CUDA_KERNEL_LAUNCH_CHECK();
return partial;
}

// The WS1 d_gate fold: each segment's tiles in ascending order, cast once to dtype.
torch::Tensor h3_gate_grad_fold(torch::Tensor partial, torch::Tensor seg_first_tile,
c10::ScalarType dtype) {
TORCH_CHECK(partial.is_cuda() && partial.dim() == 2 && partial.is_contiguous() &&
partial.scalar_type() == at::kFloat,
"partial must be a contiguous float32 (tiles, N) CUDA tensor");
TORCH_CHECK(seg_first_tile.is_cuda() && seg_first_tile.device() == partial.device() &&
seg_first_tile.scalar_type() == at::kLong && seg_first_tile.dim() == 1 &&
seg_first_tile.is_contiguous(),
"seg_first_tile must be a contiguous int64 CUDA tensor on partial's device");
TORCH_CHECK(seg_first_tile.numel() >= 1, "seg_first_tile must hold R + 1 entries");
const int64_t n = partial.size(1);
const int64_t segments = seg_first_tile.numel() - 1;
const c10::cuda::CUDAGuard guard(partial.device());
auto out = torch::empty({segments, n}, partial.options().dtype(dtype));
if (segments == 0) return out; // a zero-sized grid is not a valid launch
const int threads = 256;
const int64_t col_blocks64 = (n + threads - 1) / threads;
TORCH_CHECK(col_blocks64 <= 65535, "N=", n, " needs ", col_blocks64,
" column blocks, more than the grid.y limit of 65535");
const unsigned col_blocks = static_cast<unsigned>(col_blocks64);
h3_tiles::check_lists(at::cuda::getCurrentCUDAStream(), nullptr, 0, 0, nullptr, nullptr, 0,
seg_first_tile.data_ptr<int64_t>(), segments, partial.size(0));
H3_DISPATCH(dtype, "h3_gate_grad_fold", [&] {
gate_grad_fold_kernel<T><<<dim3(static_cast<unsigned>(segments), col_blocks), threads, 0,
at::cuda::getCurrentCUDAStream()>>>(
partial.data_ptr<float>(), seg_first_tile.data_ptr<int64_t>(),
reinterpret_cast<T*>(out.data_ptr()), n);
});
C10_CUDA_KERNEL_LAUNCH_CHECK();
return out;
}

// Row-local part of the WS1 backward only: d_y = cast(grad * gate[index]).
torch::Tensor h3_gate_residual_backward_dy(torch::Tensor grad, torch::Tensor gate,
torch::Tensor index) {
TORCH_CHECK(grad.is_cuda() && grad.dim() == 2 && grad.is_contiguous(),
"grad must be a contiguous (M, N) CUDA tensor");
const Gate g = make_gate(gate, index, grad);
const int64_t rows = grad.size(0);
const int64_t n = grad.size(1);
const c10::cuda::CUDAGuard guard(grad.device());
auto dy = torch::empty_like(grad);
H3_DISPATCH(grad.scalar_type(), "h3_gate_residual_backward_dy", [&] {
const bool vec = can_vectorize(gate, n, {&grad, &dy});
gate_residual_rows_kernel<T, true><<<row_blocks(rows), 256, 0,
at::cuda::getCurrentCUDAStream()>>>(
reinterpret_cast<const T*>(grad.data_ptr()), nullptr, reinterpret_cast<T*>(dy.data_ptr()),
rows, n, g, vec);
});
C10_CUDA_KERNEL_LAUNCH_CHECK();
return dy;
}
Loading
Loading