This site is version 0.0.1. Most of the code on this site is drafted by an agent and needs review. I apologize for the AI-speak, we are working on rewriting it!
viv-lang.org

Notebooks · lib/hf/orthogonalize

Four ways to make the AO basis orthonormal

Atomic orbitals on neighboring atoms overlap, so the Roothaan equations F C = S C e carry the overlap matrix S. Any X with X^T S X = I turns them into an ordinary symmetric eigenproblem: with C = X C',

(X^T F X) C' = C' e

so the SCF diagonalizes F' = X^T F X and maps the vectors back. Many X satisfy X^T S X = I (if X does, so does X Q for any orthogonal Q), and the SCF energy and density do not depend on the choice. This module holds four:

  • orth_canon (member 0, production): canonical orthogonalization. Diagonalize S = U s U^T and keep X = U_k s_k^(-1/2) for the eigenvalues s_k above a threshold. A near-zero eigenvalue of S means some combination of AOs is almost the zero function; its column would carry 1/sqrt(s_k), amplifying rounding error, and it describes nothing the other functions cannot. Dropping it shrinks the orbital space from n to m. This is what PySCF's remove_linear_dep_ does, with the same default threshold, 1e-8. One Newton-Schulz step (orth_polish) then removes the eigensolver's rounding from the small-eigenvalue columns, measured with compensated sums, so X^T S X = I holds to 10−12 even on the diffuse H4 chain.
  • orth_symm: Lowdin's symmetric X = S^(-1/2) = U s^(-1/2) U^T. It is the orthonormal basis closest to the AOs, and it drops nothing.
  • orth_chol: S = L L^T (Cholesky), X = L^(-T), which is upper triangular. No eigenproblem at all.
  • orth_gs: Gram-Schmidt on the unit vectors in the S inner product, with each vector orthogonalized twice ("twice is enough"). In exact arithmetic this gives the same upper-triangular X as orth_chol, by a different sequence of operations, so the two can be compared entry by entry.

Each member has the signature (S, B, opts) -> X[n, n]; B is not read. The kept columns come first and a dropped column is all zeros, so orth_rank(X) counts m. When nothing is dropped, X X^T = S^(-1) for every member, which gives a comparison that does not depend on which X a member picked.

The canonical threshold is ScfOptions.lindep (column OPT_LINDEP), 10−8 by default; 0 in the column also means 1e-8.

orth_of(S, opts) runs member opts[OPT_ORTH] and returns its X; orth_rank(X) is the number of columns kept.

Used with import hf.orthogonalize.

Look inside this program

  • Atlas

    The program’s map: every function and which calls which.

  • There’s no recorded run of this program yet.

Shift+Enter in a cell runs it and moves to the next. Running a cell runs every cell above it too, as one program.

[1]lines 49–339