Repository navigation
feat: ⚡ device SVDs and distributed CholeskyQR for large GPU sweeps - #85
Open
Panadestein wants to merge 5 commits into
Open
Panadestein wants to merge 5 commits into
Panadestein wants to merge 5 commits into
Conversation
truncated_qr and the oversampled truncation took every SVD of the sketch's R factor on the host. From n = 512 on, cuSOLVER's gesvd on an A100 beats numpy's OpenBLAS gesdd (0.63 s against 2.2 s at n = 2048, complex128), and it converges on the ill-conditioned factors where gesdd fails. thin_svd sends factors with a side of at least 512 to the device and keeps smaller ones on the host. An oversampled kicked Ising compression at chi = 1024 drops from 148 s to 96 s. Assisted-by: Claude Code:claude-opus-5-5
With several devices, every site of the right-to-left pass gathered the sketch on rank 0, ran a Householder QR there and scattered the rows of the output core, while the other devices waited: the serial part that kept 4-GPU runs at 1.5x of one GPU. From chi_out = 1024, the sketch is now factored where it lies: an all-to-all turns the devices' blocks of sketch columns into blocks of rows, a shifted CholeskyQR3 (Fukaya et al. 2020) orthonormalizes them exchanging only chi_out x chi_out Gram matrices, and a second all-to-all hands every device its rows of the output core, which each device also copies to the host. Sketches too ill-conditioned for it (numerically rank-deficient, as early in an evolution at cutoff 0) fall back to the Householder QR on rank 0; after repeated fallbacks the next sites skip the attempt. On four A100s, one compression of a 127-site complex128 kicked Ising stack takes 0.72x as long at chi = 1024, 0.68x at 2048 and 0.76x at 4096 (1.25x at 512, hence the threshold). Outputs are orthonormal to 2e-14, as with Householder, and agree with it to rounding. Assisted-by: Claude Code:claude-opus-5-5
`solve_triangular` imports `cupyx.scipy.linalg`, which the CuPy-less lint job cannot resolve; `allowed-unresolved-imports` only covered the top-level `cupyx`. Assisted-by: Claude Code:claude-opus-5-5
…ent sketches Whether rounding leaves the null space of a numerically rank-deficient sketch resolvable depends on the BLAS: with the lowest supported NumPy, CholeskyQR factored the test matrix instead of declining it (28 of 400 seeds locally too), and the result was exact (orthonormal to 7e-16, residual 4e-16). The test now accepts either outcome but checks the factorization when there is one, and a matrix with an exactly zero column covers the decline deterministically. Assisted-by: Claude Code:claude-opus-5-5
Contributor
robertodr
reviewed
Nov 2, 2026
| ``x``, ``(n, k)``, on the backend of ``a``. | ||
| """ | ||
| if isinstance(a, np.ndarray): | ||
| return np.linalg.solve(a, b) |
Member
There was a problem hiding this comment.
does it make sense to use the triangular solve in scipy? https://docs.scipy.org/doc/scipy/reference/generated/scipy.linalg.solve_triangular.html#scipy.linalg.solve_triangular
or are only the tiny linear systems solved on the CPU?
Member
Author
There was a problem hiding this comment.
Yes, I would say that for sizes where the triangular system arises, i.e. it gets triggered by the Cholesky QR, which is at BD>1024, running on CPU will be potentially very slow. And of course for small systems I don't think the difference will matter much between scipy and numpy, but I could measure. We don't have scipy as a strong dependency now.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🤖 AI text below 🤖
What
Two performance changes for large sweeps on GPUs:
Large thin SVDs on the device (cherry-picked from
bench/kicked-ising, c592cb0).thin_svdruns SVDs with a side of at least 512 on the GPU (cuSOLVERgesvd) and keeps smaller ones on the host. At n = 2048, complex128, an A100 takes 0.63 s against 2.2 s for NumPy's OpenBLASgesdd, andgesvdconverges on ill-conditioned factors wheregesddfails.Distributed CholeskyQR of the sketch on several GPUs. At every site of the right-to-left pass, the sketch used to be gathered on rank 0, factored there by a Householder QR, and its rows scattered, while the other GPUs waited. That serial step is why 4 GPUs only reached 1.5x of one at chi = 2048. From
chi_out = 1024on, the sketch is now factored in place:chi_out x chi_outGram matrices; the second plain pass is skipped once the first sees an orthonormal input;Sketches too ill-conditioned for CholeskyQR (numerically rank-deficient: early in an evolution at cutoff 0, where the bonds are padded to
chi_out) fall back to the Householder path. Rank deficiency spans runs of neighbouring sites, so after two fallbacks in a row the next 2, 4, ... (at most 16) sites skip the attempt. One GPU, andchi_out < 1024, are unchanged.New pieces:
utils.cholesky_qr,utils.solve_triangular(in_backend, since the array API has no triangular solve),DeviceGroup.alltoall. The docs (features/large-problems,contributing/testing) describe the new path.Benchmarks
Leonardo, one node with 4x A100-SXM-64GB (NVLink), complex128, IBM 127-qubit kicked Ising Heisenberg MPO (
src(U^dag, O, U)per sub-layer,theta_h = 8pi/32). Each pair below compresses the same input with the same seed.Saturated operator (random MPO with bonds
min(chi, 4^d), one compression per sub-layer):Real evolution, 20 Trotter steps of
<Z62>fromZ62, chi = 1024, cutoff 0, each QR path in its own job: 969 s Householder vs 851 s CholeskyQR (0.88x). Steps 0-6 are rank-deficient everywhere and cost 1.02-1.04x, thanks to the backoff (1.14x without it). From step 8 on, the operator saturates, CholeskyQR takes over, and steps run 0.73-0.86x.Accuracy
<0|O|0>to 1e-11-1e-13 relative, and the Frobenius distance sits at the 3e-8 floor of the inner-product measurement.<Z62>agrees to 13 digits while the compressions are exact (steps 0-6). From step 8 it drifts to about 3e-3 (0.1874 vs 0.1861 at step 20). This drift is not CholeskyQR's. The same Householder code on 2 GPUs instead of 4, where only the rounding differs, drifts from the 4-GPU run the same way: it diverges from step 6, reaches 1e-4 by step 8, and stays at 3e-4 to 5e-3 afterwards. Once truncation sets in, the 20-step chain amplifies any rounding-level change to the truncation-error scale. CholeskyQR's differences from the 4-GPU Householder run stay inside that envelope (3e-4 to 6e-3). Against a plain 1-GPU run, the difference at step 20 is 5.5e-4 for 4-GPU Householder, 8.8e-4 for 2-GPU Householder and 1.8e-3 for 4-GPU CholeskyQR; across the steps, all three lie in the same 1e-5 to 8e-3 band. For scale, chi = 1024 and 2048 differ by 4e-3 at step 20, and the 2D reference by 8e-2.cholesky_qragainsttruncated_qrfor condition numbers 1 to 1e12, with a cutoff, and on rank-deficient input (which must be declined on every rank). The multi-device equivalence tests run on both QR paths, plus two-GPU tests on hardware. All pass on Leonardo (tests/test_gpu_backend.py74/74 with the linalg and group tests).Notes for review
alltoalland the host-side writes reuse the existing event-ordered peer copies; there is no new dependency (no NCCL).🤖 Generated with Claude Code