Repository navigation
JAX vectorisation: vmap/einsum/jit + padded Davidson subspace with thick-restart + 4-min CI timeout - #7
Conversation
- Add timeout-minutes: 4 to both CI jobs - get_hamiltonian_diagonal: use jax.vmap over all determinants - _diagonal_element: replace Python for-loops with einsum (enables vmap) - _single_excitation_element: replace Python for-loops with dot products - _apply_h_vec_prod (Davidson): replace list comprehension with jax.vmap - apply_epv_and_single_tagging: replace Python loops with JAX array ops Agent-Logs-Url: https://github.com/jphaupt/CIPSIpy/sessions/4c6f53ff-f278-439a-b632-e418f5512768 Co-authored-by: jphaupt <10662835+jphaupt@users.noreply.github.com>
…n cipsi.py Agent-Logs-Url: https://github.com/jphaupt/CIPSIpy/sessions/ef80cec4-b098-4b5b-b6b9-eb6332c0482c Co-authored-by: jphaupt <10662835+jphaupt@users.noreply.github.com>
…clarify cipsi.py:462 comment Agent-Logs-Url: https://github.com/jphaupt/CIPSIpy/sessions/e7cf4da9-61fb-4612-b56b-4c8a51537518 Co-authored-by: jphaupt <10662835+jphaupt@users.noreply.github.com>
|
@copilot another optimisation: at every davidson iteration, the JAX arrays are being rebuilt. Please address this. My suggestion is to use a padded matrix with masking (to avoid computing zeros). The padding can be an input parameter. Once the end of the padding is reached, the calculation can start again with the latest best guess. |
…ilation - Add max_subspace parameter to Diagonaliser.__init__ (default (max_micro_iterations+1)*nstate, capped at dim). - Preallocate fixed-shape (dim, max_subspace) Vmat/Wmat buffers. - Apply H only to new correction columns each iteration (at most nstate columns); avoids the original jnp.hstack which grew Vmat and forced JAX to recompile H_vec_prod on every iteration. - Mutual MGS among correction columns handles the nstate>1 case where DPR corrections are parallel; near-zero columns are skipped to prevent rank-deficient Tmat and garbage eigenvalues. - Thick-restart triggered at m >= max_subspace, placed after the convergence check so the full subspace is tested before reset. - Fix pre-existing NaN bug in DPR denominator guard: sign(0.0)*eps=0 in IEEE 754, replaced by explicit zero-out when |denom| < eps. - Remove redundant float() casts on JAX scalars; add comment noting that davidson() uses concrete Python control flow intentionally and is not designed to be jax.jit-compiled itself. - Add test_davidson_thick_restart to cover forced-restart scenario. Agent-Logs-Url: https://github.com/jphaupt/CIPSIpy/sessions/0262b266-2f5d-4d93-8809-51e5986824ec Co-authored-by: jphaupt <10662835+jphaupt@users.noreply.github.com>
Done in 21367d0. Here's what changed in Preallocated fixed-size buffers – H applied only to new columns – instead of calling Mutual MGS + skip near-zero columns – when Thick-restart at NaN fix – the original DPR denominator guard used A new |
|
@copilot After the Davidson padding optimisation, the program seems to run slower (e.g. tests take longer, especially diagonaliser and fci tests). Please investigate why this might happen. Is your optimisation really causing more problems (if so, please remove it!)? Are the default parameters chosen too cautious? |
…ick-restart with n_keep Agent-Logs-Url: https://github.com/jphaupt/CIPSIpy/sessions/a3b11576-51d9-4c6e-acd6-e40f4472236f Co-authored-by: jphaupt <10662835+jphaupt@users.noreply.github.com>
Investigated and fixed in 530416b. The regression had three root causes:
The default parameters are unchanged. Performance is now within ~10% of the original for |
|
@copilot This time I did a more rigorous benchmark and I have committed both the scripts and the results for the last three commits. Actually 21367d0 seems to be the fastest, which is before your revert. Sorry about that. Please go back to this commit (though keep or add small optimisations if possible). |
Agent-Logs-Url: https://github.com/jphaupt/CIPSIpy/sessions/5d3439bb-b2d7-4004-8ea8-5d3882ca50ca Co-authored-by: jphaupt <10662835+jphaupt@users.noreply.github.com>
…/CIPSIpy into copilot/optimize-jax-performance
Phase 2 of the performance plan: replace Python-level loops with JAX vectorised operations (
vmap,einsum,dot) across the hot paths, applyjax.jitto the most performance-critical kernels, use the existingget_occupied_indiceshelper consistently, and eliminate per-iteration JAX recompilation in the Davidson solver via preallocated subspace buffers with thick-restart. Also adds a 4-minute CI timeout as a safety guard.CI
timeout-minutes: 4on bothtest-pr-not-slowandtest-main-fulljobshamiltonian.py_diagonal_element: three nestedfor-loops (α-α, β-β, α-β two-electron terms) →jnp.einsumover occupancy masks; function is now fully JAX-traceable. Decorated with@partial(jax.jit, static_argnums=(2,))(n_orbis static as it determines array shapes)._single_excitation_element: same-spin and opposite-spin loops →jnp.dotover occupancy masks;k=aexclusion handled by zeroing the mask entryget_hamiltonian_diagonal: serial loop + scalar.at[i].set()→jax.vmapover the full determinant batch. Decorated with@partial(jax.jit, static_argnums=(3,))to JIT-compile the entire vmapped diagonal-batch computation (the main hot path for the Davidson solver).diagonaliser.py_apply_h_vec_prod: list comprehension over Davidson subspace columns →jax.vmap(H_vec_prod, in_axes=1, out_axes=1)davidson(): eliminated per-iteration JAX recompilation caused by growingVmatviajnp.hstack(dim, max_subspace)buffers (Vmat,Wmat) are preallocated once; their shape never changes across iterationsH_vec_prodis called only on the newly added columns (at mostnstateper iteration), so it sees at most two distinct input shapes and is compiled at most twice rather than once per iterationnstatecorrection columns handles the case where DPR corrections are parallel (common fornstate > 1); near-zero residual columns are skipped to prevent rank-deficient subspace matricesm >= max_subspace, placed after the convergence check so the full subspace is always tested before any vectors are discardedmax_subspaceconstructor parameter (default(max_micro_iterations + 1) * nstate, capped atdim) controls the padding/restart cadencesign(0.0) * eps = 0in IEEE 754, leaving a zero denominator and producing0/0 = NaN; replaced withjnp.where(|denom| < eps, 0.0, residuals / denom)test_davidson_thick_restarttest verifies convergence under a deliberately smallmax_subspacethat forces multiple restartscipsi.pyapply_epv_and_single_tagging: all Python loops replaced with JAX array ops(det >> idx) & 1bit-shift patterns replaced throughout withget_occupied_indiceshelper (imported fromdeterminants.py)Bmat & rows_to_tag[:, None] & rows_to_tag[None, :]— outer-product mask where entry(r, s)isTrueonly when both spin-orbitalrand spin-orbitalsare eligible (virtual inG_pqand neither of the annihilated spin-orbitals)jnp.argmaxon occupancy arrayjnp.wherescatter