diff --git a/EC.jl b/EC.jl new file mode 100644 index 0000000..bc8805a --- /dev/null +++ b/EC.jl @@ -0,0 +1,110 @@ +using SparseArrays, LinearAlgebra, Arpack +include("helper.jl") + +"EC model for a Hamiltonian family H(c) = H0 + c * H1" +mutable struct affine_EC + H0::SparseMatrixCSC{ComplexF64} + H1::SparseMatrixCSC{ComplexF64} + weights::Vector{ComplexF64} + trained::Bool + + H0_EC + H1_EC + N_EC + + training_E::Vector{ComplexF64} + exact_E::Vector{ComplexF64} + extrapolated_E::Vector{ComplexF64} + + affine_EC(H0::SparseMatrixCSC{ComplexF64}, H1::SparseMatrixCSC{ComplexF64}, weights::Vector{ComplexF64}=ones(ComplexF64, size(H0, 1))) = new(H0, H1, weights, false, nothing, nothing, nothing, ComplexF64[], ComplexF64[], ComplexF64[]) +end + +"Train an EC model for a given range of c values. +If a list is provided for ref_eval, they are used as reference values for picking the closest eigenvalues at each sampling point. +If a single number is provided for ref_eval, it is used as a reference for the first point, and the previous eigenvalue is used as the reference for each successive point. +Set orthonormalize_threshold=0 to skip Gram-Schmidt orthonormalization and use GEVP. Otherwise this value is used as the threshold for dropping redundant vectors." +function train!(EC::affine_EC, c_vals; ref_eval=nothing, orthonormalize_threshold=1e-5, CAEC=false, verbose=true, tol=1e-5) + training_vecs = Vector{ComplexF64}[] + current_E = -10.0 # randomly chosen reference value + + for c in c_vals + verbose && println("Training for c = $c") + + if ref_eval isa Number + current_E = ref_eval + ref_eval = nothing + elseif !isnothing(ref_eval) + current_E = pop!(ref_eval) + end + + H = EC.H0 + c .* EC.H1 + + evals, evecs = eigs(H, sigma=current_E, maxiter=5000, tol=tol, ritzvec=true, check=1) + current_E = nearest(evals, current_E) + + push!(EC.training_E, current_E) + push!(training_vecs, evecs[:, nearestIndex(evals, current_E)]) + end + + CAEC && append!(training_vecs, conj.(training_vecs)) + + if orthonormalize_threshold ≠ 0 + training_vecs = gram_schmidt!(training_vecs, EC.weights, orthonormalize_threshold; verbose=verbose) + end + + EC_basis = hcat(training_vecs...) + weights_mat = spdiagm(EC.weights) + EC.H0_EC = transpose(EC_basis) * weights_mat * EC.H0 * EC_basis + EC.H1_EC = transpose(EC_basis) * weights_mat * EC.H1 * EC_basis + + if orthonormalize_threshold == 0 + EC.N_EC = transpose(EC_basis) * weights_mat * EC_basis + end + + EC.trained = true +end + +"Extrapolate using a train EC model for a given range of c values +If a list is provided for ref_eval, they are used as reference values for picking the closest eigenvalues at each point. +If a single number is provided for ref_eval, it is used as a reference for the first point, and the previous eigenvalue is used as the reference for each successive point." +function extrapolate!(EC::affine_EC, c_vals; ref_eval=nothing, verbose=true, tol=1e-5) + @assert EC.trained "EC model must be trained using train() before extrapolation" + + current_E = -10.0 # randomly chosen reference value + + for c in c_vals + verbose && println("Extact solution for c = $c") + + if ref_eval isa Number + current_E = ref_eval + ref_eval = nothing + elseif !isnothing(ref_eval) + current_E = pop!(ref_eval) + end + + H = EC.H0 + c .* EC.H1 + + evals, _ = eigs(H, sigma=current_E, maxiter=5000, tol=tol, ritzvec=false, check=1) + current_E = nearest(evals, current_E) + + push!(EC.exact_E, current_E) + + verbose && println("Extrapolating for c = $c") + + H_EC = EC.H0_EC + c .* EC.H1_EC + + evals = isnothing(EC.N_EC) ? eigvals(H_EC) : eigvals(H_EC, EC.N_EC) + push!(EC.extrapolated_E, nearest(evals, current_E)) + end +end + +"Export EC data as CSV file" +exportCSV(EC::affine_EC, filename) = exportCSV(filename, (EC.training_E, EC.exact_E, EC.extrapolated_E), ("training", "exact", "extrapolated")) + +"Plot EC data and optionally save figure to a file" +function plot(EC::affine_EC, save_fig_filename=nothing) + scatter(real.(EC.training_E), imag.(EC.training_E), label="training") + scatter!(real.(EC.exact_E), imag.(EC.exact_E), label="exact") + scatter!(real.(EC.extrapolated_E), imag.(EC.extrapolated_E), label="extrapolated") + isnothing(save_fig_filename) || savefig(save_fig_filename) +end diff --git a/calculations/3body_HO_B2R_EC.jl b/calculations/3body_HO_B2R_EC.jl index f94b988..8dc134c 100644 --- a/calculations/3body_HO_B2R_EC.jl +++ b/calculations/3body_HO_B2R_EC.jl @@ -1,4 +1,5 @@ using Plots +include("../EC.jl") training_ref = -0.72763 exact_ref = 4.0766890719636635 - 0.01275892774109674im @@ -13,52 +14,9 @@ H0 = H Vp_of_r(r) = -exp(-(r/3)^2) @time "Vp" Vp = get_src_V_matrix(Vp_of_r, basis, μω, μω_global) -exact = ComplexF64[] -training = ComplexF64[] -extrapolated = ComplexF64[] -training_vecs = Vector{ComplexF64}[] +EC = affine_EC(H0, Vp) +train!(EC, training_c; ref_eval=training_ref, CAEC=true) +extrapolate!(EC, extrapolating_c; ref_eval=exact_ref) -current_E = training_ref - -for c in training_c - println("Training for c = $c") - local H = H0 + c .* Vp - local evals, evecs = eigs(H, nev=3, ncv=24, which=:LI, maxiter=5000, tol=1e-5, ritzvec=true, check=1) - - global current_E = nearest(evals, current_E) - push!(training, current_E) - push!(training_vecs, evecs[:, nearestIndex(evals, current_E)]) -end - -# CA-EC -training_vecs = vcat(training_vecs, conj(training_vecs)) - -println("Original EC dimensionality = $(length(training_vecs))") -@time "Gram-Schmidt" training_vecs = gram_schmidt!(training_vecs; verbose=true) # orthonormalization - -EC_basis = hcat(training_vecs...) -H0_EC = transpose(EC_basis) * H0 * EC_basis -Vp_EC = transpose(EC_basis) * Vp * EC_basis - -current_E = exact_ref - -for c in extrapolating_c - println("Extrapolating for c = $c") - local H = H0 + c .* Vp - local evals, _ = eigs(H, nev=3, ncv=24, which=:LI, maxiter=5000, tol=1e-5, ritzvec=false, check=1) - - global current_E = nearest(evals, current_E) - push!(exact, current_E) - - # extrapolation - H_EC = H0_EC + c .* Vp_EC - evals = eigvals(H_EC) - push!(extrapolated, nearest(evals, current_E)) -end - -exportCSV("temp/HO_B2R.csv", (training, exact, extrapolated), ("training", "exact", "extrapolated")) - -scatter(real.(training),imag.(training), label="training") -scatter!(real.(exact),imag.(exact), label="exact") -scatter!(real.(extrapolated),imag.(extrapolated), label="extrapolated") -savefig("temp/HO_B2R.pdf") \ No newline at end of file +exportCSV(EC, "temp/HO_B2R.csv") +plot(EC, "temp/HO_B2R.pdf")