|
| 1 | +############################################################################### |
| 2 | +# STO-nG regression test (H 1s example) # |
| 3 | +# ----------------------------------------------------------------------- # |
| 4 | +# • Uses numerical quadrature (QuadGK) for all radial integrals. # |
| 5 | +# • Shows two optimisation routes # |
| 6 | +# 1. Direct normal-equation solve C*c = B (strict least-squares) # |
| 7 | +# 2. Gradient descent with automatic differentiation (Zygote) # |
| 8 | +# • Prints the RMS radial error and the fitted coefficients. # |
| 9 | +# ----------------------------------------------------------------------- # |
| 10 | +# REQUIRE: julia> ] add QuadGK Zygote # |
| 11 | +############################################################################### |
| 12 | + |
| 13 | +using LinearAlgebra, QuadGK, Zygote |
| 14 | + |
| 15 | +# ---------- 1. Slater-type (target) orbital -------------------------------- |
| 16 | +""" |
| 17 | + χ_STO(r; ζ=1.24, n=1) |
| 18 | +
|
| 19 | +Unnormalised radial part r^{n-1}·exp(-ζ·r). |
| 20 | +""" |
| 21 | +χ_STO(r; ζ=1.24, n=1) = r^(n-1) * exp(-ζ * r) |
| 22 | + |
| 23 | +""" |
| 24 | + normalise_STO(ζ; n=1) |
| 25 | +
|
| 26 | +Returns the normalisation constant N such that |
| 27 | +∫₀^∞ |N·χ_STO(r)|²·r² dr = 1 |
| 28 | +""" |
| 29 | +function normalise_STO(ζ; n=1) |
| 30 | + # analytic for n = 1,2,… : N = √( (2ζ)^{2n+1} / (2n)! ) |
| 31 | + return sqrt( (2ζ)^(2n+1) / factorial(2n) ) |
| 32 | +end |
| 33 | + |
| 34 | +# ---------- 2. Primitive Gaussian ----------------------------------------- |
| 35 | +""" |
| 36 | + χ_G(r, α; l=0) |
| 37 | +
|
| 38 | +Primitive Gaussian r^{l}·exp(-α·r²) |
| 39 | +(Angular part is ignored because we only care about the radial fit.) |
| 40 | +""" |
| 41 | +χ_G(r, α; l=0) = r^l * exp(-α * r^2) |
| 42 | + |
| 43 | +# ---------- 3. Radial integrals (numerical) ------------------------------- |
| 44 | +const atol = 1e-12 # quad accuracy |
| 45 | + |
| 46 | +# ∫₀^∞ f(r) r² dr (measure already contains 4π average over angles) |
| 47 | +function radial_integral(f) |
| 48 | + value, _err = QuadGK.quadgk(r -> f(r) * r^2, 0.0, Inf; atol) |
| 49 | + return value |
| 50 | +end |
| 51 | +""" |
| 52 | + build_matrices(alphas, ζ; l=0, n=1) |
| 53 | +
|
| 54 | +Return (A, B, C) for the quadratic error |
| 55 | +R² = A − 2 cᵀ B + cᵀ C c |
| 56 | +""" |
| 57 | +function build_matrices(alphas, ζ; l=0, n=1) |
| 58 | + k = length(alphas) |
| 59 | + # Normalised STO |
| 60 | + N = normalise_STO(ζ; n) |
| 61 | + sto = r -> N * χ_STO(r; ζ, n) |
| 62 | + # A = ⟨STO|STO⟩ = 1 by construction |
| 63 | + A = 1.0 |
| 64 | + # Bᵢ = ⟨PGFᵢ|STO⟩ |
| 65 | + B = [ radial_integral(r -> χ_G(r, α; l) * sto(r)) for α in alphas ] |
| 66 | + # Cᵢⱼ = ⟨PGFᵢ|PGFⱼ⟩ |
| 67 | + C = [ radial_integral(r -> χ_G(r, αi; l) * χ_G(r, αj; l)) |
| 68 | + for αi in alphas, αj in alphas ] |
| 69 | + return A, B, Symmetric(reshape(C, k, k)) |
| 70 | +end |
| 71 | + |
| 72 | +# ---------- 4. Direct least-squares solution ------------------------------ |
| 73 | +function fit_direct(alphas, ζ; l=0, n=1) |
| 74 | + A, B, C = build_matrices(alphas, ζ; l, n) |
| 75 | + c = C \ B # solve normal equations |
| 76 | + # renormalise contracted Gaussian |
| 77 | + norm = sqrt(dot(c, C * c)) |
| 78 | + c ./= norm |
| 79 | + # RMS error |
| 80 | + R2 = A - 2dot(c, B) + dot(c, C * c) |
| 81 | + return c, sqrt(R2) |
| 82 | +end |
| 83 | + |
| 84 | +# ---------- 5. Gradient descent (AD) -------------------------------------- |
| 85 | +""" |
| 86 | + fit_AD(alphas, ζ; l=0, n=1, lr=1e-2, maxiter=2000) |
| 87 | +
|
| 88 | +Crude gradient descent on the same quadratic loss, to illustrate that |
| 89 | +AD finds the same minimum as the linear solve. |
| 90 | +""" |
| 91 | +function fit_AD(alphas, ζ; l=0, n=1, lr=1e-2, maxiter=2000) |
| 92 | + k = length(alphas) |
| 93 | + A, B, C = build_matrices(alphas, ζ; l, n) |
| 94 | + c = fill(0.3, k) # naive start |
| 95 | + loss(c) = A - 2dot(c, B) + dot(c, C * c) |
| 96 | + for it = 1:maxiter |
| 97 | + grad = Zygote.gradient(loss, c)[1] |
| 98 | + c .-= lr * grad |
| 99 | + if norm(grad) < 1e-10 |
| 100 | + break |
| 101 | + end |
| 102 | + end |
| 103 | + # renormalise and RMS |
| 104 | + c ./= sqrt(dot(c, C * c)) |
| 105 | + R = sqrt(loss(c)) |
| 106 | + return c, R |
| 107 | +end |
| 108 | + |
| 109 | +# ---------- 6. Demo: H 1s STO-3G ------------------------------------------ |
| 110 | +ζ_H = 1.24 # standard value used by STO-3G |
| 111 | +αs = [0.109818, 0.405771, 2.22766] # H 1s STO-3G exponents |
| 112 | + |
| 113 | +#c = [0.9817067, 0.949464, 0.2959065] |
| 114 | + |
| 115 | +#H 0 |
| 116 | +#S 3 1.00 |
| 117 | +# 0.3425250914D+01 0.1543289673D+00 |
| 118 | +# 0.6239137298D+00 0.5353281423D+00 |
| 119 | +# 0.1688554040D+00 0.4446345422D+00 |
| 120 | +#**** |
| 121 | + |
| 122 | +c_direct, RMS_direct = fit_direct(αs, ζ_H) |
| 123 | +c_AD, RMS_AD = fit_AD(αs, ζ_H; lr=1e-1) |
| 124 | + |
| 125 | +println("=== STO-3G (H 1s) fit with fixed exponents ===") |
| 126 | +println("Direct linear solve:") |
| 127 | +println(" c = ", round.(c_direct, sigdigits=7)) |
| 128 | +println(" RMS = ", print("%.3e", RMS_direct)) |
| 129 | +println("Automatic-diff AD gradient descent:") |
| 130 | +println(" c = ", round.(c_AD, sigdigits=7)) |
| 131 | +println(" RMS = ", print("%.3e", RMS_AD)) |
0 commit comments