|
Uni20
Asynchronous tensor and runtime library.
|
spin_half_heisenberg_dmrg_example.cpp constructs a U(1) spin-half Néel product MPS and reduced-boundary Heisenberg MPO, then runs alternating two-site DMRG sweeps. It prints one summary per directional traversal, elapsed time, and the difference from a registered reference energy when one is available.
--execution=cpu is the default. A CUDA build with the CUDA, cuBLAS, and cuSOLVER backends also supports --execution=cuda. The MPS, environments, two-site centers, Krylov vectors, and SVD factors then remain on the selected device; the MPO coefficients and compact singular-value selection data remain on the host. CUDA DMRG currently supports real float or double, and this example uses double.
The default four-site calculation can verify the exact open-chain energy
with
The larger reference case is
--local-matvecs=N selects the fixed number of effective-Hamiltonian applications requested for each local update. It defaults to four. This is a local work budget, not a convergence tolerance; sweep-level environment and energy convergence remain the controlling DMRG criteria.
--energy-tol=VALUE overrides the sweep-level energy tolerance. Its default is zero, which selects 100 * numeric_limits<Real>::epsilon() after applying the chosen scalar precision.
--scalar=complex runs the same calculation with uni20::complex<Real> storage and arithmetic. This is useful for controlled comparisons with implementations that do not provide a real-scalar path. --precision=fp32|fp64 selects Real independently; the default is fp64. Reference checks use precision-specific tolerances; the four-site fp32 real and complex paths are registered CTests with a 1e-6 analytic-energy tolerance.
In an MPLAPACK-enabled build, --precision=fp128 runs the same U(1) BlockTensor DMRG path with uni20::float128 or uni20::complex<uni20::float128> storage. Precision selection does not change the symmetry or block-sparse model.
--block-threads=N installs a TbbScheduler with concurrency N. Sparse environments use ParallelPackedSparseBlockStorage; transient two-site centers and their Krylov vectors use ParallelPackedCompleteBlockStorage. Independent output blocks execute as synchronous lightweight batch items. Dense BLAS should normally remain single-threaded when block-level concurrency is greater than one.
For CUDA execution, --cuda-submitters=N independently limits the number of lightweight batch runners that may submit device work. It defaults to one and is bounded by --block-threads; --cuda-streams=N separately controls the number of device streams available to those runners. The cuBLAS pool defaults to one handle per stream and the cuSOLVER pool defaults to two handles, capped by the stream count. --cuda-cublas-handles=N and --cuda-cusolver-handles=N override those provider capacities. CPU execution leaves lightweight batch concurrency uncapped within the scheduler arena.
On CPU, --mps-storage=packed retains the default serial packed storage for MPS sites. --mps-storage=parallel-packed enables scheduler batches while retaining one packed site allocation. --mps-storage=parallel-aligned-packed pads each block start to a 64-byte boundary within that allocation, while --mps-storage=parallel-separate gives every dense site block an independently aligned allocation. The environment and transient center policies are unchanged, so these modes isolate MPS-site storage and execution effects. CUDA execution currently selects parallel packed CUDA storage for the MPS, environments, and transient centers independently of --mps-storage.
--measurements=coarse records and prints inclusive DMRG phase wall times. --measurements=detailed additionally times every per-charge block-SVD item and reports aggregate overlap and scheduler-tail fields. The default is --measurements=off, which selects the compile-time disabled library path. Detailed measurements intentionally perturb fine-grained work and should be used for attribution, not final benchmark numbers.
--steady-sweeps=N selects the fixed-dimension benchmark path instead of the energy-convergence path. Starting from the same rank-one Néel state, it runs unmeasured alternating growth traversals until a completed traversal actually reports max-m equal to --max-states. That traversal remains part of warm-up. It then times N additional traversals and reports growth, steady, and combined wall time separately. For spin one-half, the bond dimension can grow by at most a factor of two per directional traversal, so m=128, 256, and 512 normally require seven, eight, and nine growth traversals respectively. The example allows two additional growth traversals before reporting that the requested dimension is not reachable for the selected fixture. --max-sweeps is used only by the convergence path.
For example, the following commands compare the same optimized executable and initial state after reaching m=512:
The CUDA runtime, initial host-to-device MPS transfer, MPO construction, and environment-cache construction occur before either reported timer. CUDA stream work is synchronized at every traversal boundary so each wall time includes completion of the submitted device work.
The first end-to-end scaling tables, local Matrix Product Toolkit orientation, and exact current parallel boundary are recorded in DMRG Performance Baselines.
Its comparison energy, -8.682473334398985, was calculated with Matrix Product Toolkit mp-dmrg-2site from a Néel product state using m=128. The reference is reproducible with:
The example remains entirely symmetry-aware. The reference energy is a terminal assertion and does not feed a dense state back into the DMRG path.