[QDP] Complete Tensor Core IQP path remediation#1387
Conversation
5b0c215 to
a3cb616
Compare
…tests - Expose encode_batch_tc through Rust core, PyO3, and Python backend - Fix IQP TC kernel: correct batch stride for ZZ params and raise FWT_SHARED_MEM_THRESHOLD to 12 for fused shared-memory path at N<=12 - Align ImplicitHadamardOzaki.cu with PR6 ldmatrix/alignment fixes - Add benchmark_pr5.py with --path fwt|tc|both (GPU-vs-GPU, no PyTorch) - Add test_iqp_tc_path.py smoke and normalization tests
…C GEMM on non-Hadamard logic (PR6). Tests: wsl cargo test passed (0 failures). PR6 comments added.
…r fusion and Kronecker decomposition This commit introduces the foundational architecture for Tensor Core FWT, including: - Fused shared-memory kernel for N <= 12 (1.68x speedup). - Matrix-Free Kronecker decomposition for N > 12 using Adaptive Ozaki GEMM. - Fixed critical ldmatrix alignment bugs in CUDA kernels. - Exposed encode_batch_tc to Python API. - Comprehensive benchmark script and performance report.
…eline fixes - iqp_tc.cu: 2-step Kronecker path with fused transpose; CHECK_CUDA_RETURN; static buffers - ImplicitHadamardOzaki: fused-batch MMA + naive FP64 fallback (replace persistent MMA) - iqp.rs: propagate cu_stream() to launch_iqp_encode_tc - encode_batch_tc(encoding_method) for iqp / iqp-z - benchmark_e2e.py: Mahout-Arrow (FWT) vs Mahout-TC with correctness verification
…rify
- naive_hadamard: implement transpose_batch for Kronecker legs (n>=256)
- Ozaki dispatch: INT8 MMA fused TC for n in {64,128}; naive FP64 for n>=256
- iqp_tc: zero temp/out buffers and stream-sync to avoid WDDM flakes
- benchmark_e2e: clone DLPack tensors so FWT/TC states are not aliased
- test_iqp_tc_path: add N=16/17/18 agreement tests (12 cases total)
Verified WSL2 RTX 4060 2026-06-11: pytest 12/12; N=14-18 max_err ~1e-17;
E2E iqp-z N=16 SUCCESS (1.5e-8, ~14.5x vs FWT).
c2d9f08 to
0d12958
Compare
242e2be to
a82426c
Compare
2306712 to
0d12958
Compare
0d12958 to
6521852
Compare
| rtol=1e-10, | ||
| atol=1e-10, |
There was a problem hiding this comment.
with different precision can have different tolerances(f32,f64)
| method = "iqp-z" | ||
|
|
||
| # Generate random parameters (batched) | ||
| data = torch.randn(batch_size, n_params, dtype=torch.float64, device="cuda") |
There was a problem hiding this comment.
Let's not use truely random numbers because it introduces flackiness.
| @pytest.mark.parametrize("n_qubits", [2, 4, 6]) | ||
| @pytest.mark.parametrize("batch_size", [1, 16, 64]) |
There was a problem hiding this comment.
Add weird n_qubits and batch_size for robustness.
|
|
||
| // In future PRs, Kronecker Transpose and FWT will happen here. | ||
|
|
||
| recombine_complex_kernel<<<blocks, DEFAULT_BLOCK_SIZE, 0, stream>>>( |
There was a problem hiding this comment.
This TC launcher currently writes the pre-FWT phase vector rather than an IQP-encoded state. iqp_phase_split_kernel computes exp(i theta(x)), and this recombine writes it directly to the output; the FWT + normalization is still marked as future work. For all-zero params, this returns all ones instead of |0...0>. Can we either keep this as private scaffolding for now, or complete the transform before exposing it through encode_batch_tc?
| cudaMalloc(&d_state_real, total_elements * sizeof(double)); | ||
| cudaMalloc(&d_state_imag, total_elements * sizeof(double)); | ||
|
|
||
| unsigned int data_len = num_qubits; |
There was a problem hiding this comment.
For full ZZ batches, this hardcodes data_len = num_qubits, but Rust validates sample_size as num_qubits + num_qubits * (num_qubits - 1) / 2. That means sample 1 starts inside sample 0's ZZ parameters when enable_zz=true. Can we pass the actual sample_size/data length into launch_iqp_encode_tc, matching the existing batch launcher?
| ) | ||
|
|
||
| # 2. QDP Engine (Testing the Batch throughput C++ / Rust API path) | ||
| actual_state_dlpack = engine.encode(data, n_qubits, encoding_method=method) |
There was a problem hiding this comment.
This test currently exercises engine.encode(...), which routes through the existing standard IQP batch path, not the new encode_batch_tc path. So it can pass even if the TC scaffold is incorrect. Can we add a direct CUDA/Rust test for IqpEncoder::encode_batch_tc, or expose an explicit opt-in Python API and test that path?
| size_t total_elements = num_samples * state_len; | ||
|
|
||
| double *d_state_real, *d_state_imag; | ||
| cudaMalloc(&d_state_real, total_elements * sizeof(double)); |
There was a problem hiding this comment.
This launcher ignores both cudaMalloc return values and then returns cudaSuccess unconditionally. On OOM or launch failure, Rust will report success while the output may be invalid; if the second allocation fails, the first allocation can also leak. Can we return CUDA errors consistently with the existing IQP launchers and free any partial allocations on failure?
|
Btw, @aloha1357 Thanks for your patch! |
6521852 to
44f5aca
Compare
|
@ryankert01 Thanks for the detailed review — these are exactly the right things to flag on this PR. Context: intentional scaffolding #1387 (PR2) is scoped as TC-path scaffolding only: Where your points are addressed
One-Line Summary per PR
Note: PR3 (#1388) is a separate line (shared-mem FWT in
Question for you If the PR2 scaffolding looks acceptable in principle, would you prefer to:
Option 2 avoids repeatedly growing the same PR and keeps the review surface stable. |
|
Im object to merge any scaffolding pr into main. We can do a dev branch dedicate for this or a big pr. |
44f5aca to
ac78832
Compare
|
Thanks, that makes sense. I agree that merging a I reworked this branch following the “big PR” approach you What changed:
|
This reverts commit 6b4d853.
Tensor Core IQP path remediation
This PR updates the Tensor Core IQP path from the earlier scaffold into a merge-ready implementation addressing the review feedback on #1387.
The main goals are:
AdaptiveOzakiEnginecodeencode_batch_tcdirectly with deterministic correctness testsWhat changed
Dead code removal
The previous branch carried an unused
AdaptiveOzakiEngineimplementation and an unusedlaunch_adaptive_ozaki_gemmFFI surface. That code was not part of the IQP Tensor Core path and made the PR broader than necessary.This PR removes that path entirely:
qdp/qdp-kernels/src/AdaptiveOzaki.cuqdp/qdp-kernels/src/AdaptiveOzaki.hAdaptiveOzaki.cufrombuild.rslaunch_adaptive_ozaki_gemmFFI declaration and non-CUDA stub fromlib.rsozaki_config.hCUDA error handling
The
N <= 12Tensor Core path now checks CUDA failures instead of silently returning success:cudaFuncSetAttributecudaStreamSynchronize(stream)cudaGetLastError()Test coverage
The test suite now exercises
encode_batch_tcdirectly instead of only the defaultencodepath.42and137torch_iqp_encode_refCPU formula referenceN = 6, 8, 10, 12,max_err < 1e-9N = 14, 16,max_err < 1e-5Reviewer feedback addressed
encode_batch_tcdirectlycudaFuncSetAttributeis checked; stream is synchronized before final error check1e-9/1e-5tolerancesmainPath separation
This PR does not introduce the later Native/Fused FP32 Hadamard work. That will be proposed separately as a new explicit opt-in path.
encodepathencode_batch_tcpathValidation
Validated locally in the repository WSL environment:
cargo build -p qdp-kernelscargo test -p qdp-kernels -qcargo clippy -p qdp-kernels -- -D warningspytest testing/qdp/test_iqp_tc_path.py -vpytest testing/qdp/test_iqp_tc_path.py -qpytest testing/qdp/test_bindings.py -qpytest testing/qdp/ -qruff check qdp/ty check qdp/Notes on large N
The implementation is intended to support larger Tensor Core IQP workloads, but the automated CPU-oracle correctness tests intentionally stop at
N = 16.For
N = 27, a full state has2^27 = 134,217,728amplitudes, so a full CPU-oracle max-error test is not appropriate for this PR gate. Larger-N coverage should use smoke/invariant tests and benchmark-specific validation rather than materializing a full CPU reference in CI.