Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Excited states #269

Open
wants to merge 10 commits into
base: develop
Choose a base branch
from
12 changes: 8 additions & 4 deletions src/ExactDiagonalization/basis_set_representation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ end
"""
build_basis(
ham, address=starting_address(ham);
cutoff, filter, sizelim, sort=false, kwargs...
cutoff, filter, sizelim, sort=false, minimum_size, kwargs...
) -> basis
build_basis(ham, addresses::AbstractVector; kwargs...)

Expand All @@ -109,7 +109,10 @@ than `cutoff`. Alternatively, an arbitrary `filter` function can be used instead
Addresses passed as arguments are not filtered.
A maximum basis size `sizelim` can be set which will throw an error if the expected
dimension of `ham` is larger than `sizelim`. This may be useful when memory may be a
concern. These options are disabled by default.
concern.
Setting a `minimum_size` will stop generating addresses once at least `minimum_size`
addresses have been generated, rather than returning the full basis.
These options are disabled by default.

Setting `sort` to `true` will sort the basis. Any additional keyword arguments
are passed on to `Base.sort!`.
Expand All @@ -121,19 +124,20 @@ function build_basis(
sort=false,
max_size=Inf, # retained for backwards compatibility; use sizelim instead
sizelim=max_size,
minimum_size=Inf,
kwargs...
)
check_address_type(ham, addr_or_vec)
single_addr = addr_or_vec isa Union{AbstractArray,Tuple} ? addr_or_vec[1] : addr_or_vec
if dimension(ham, single_addr) > sizelim
if minimum([dimension(ham, single_addr), minimum_size]) > sizelim
throw(ArgumentError("dimension larger than sizelim"))
end
# Set up `adds` as queue of addresses. Also returned as the basis.
adds = addr_or_vec isa Union{AbstractArray,Tuple} ? [addr_or_vec...] : [addr_or_vec]
known_basis = Set(adds) # known addresses

i = 0
while i < length(adds)
while i < length(adds) < minimum_size
i += 1
add = adds[i]

Expand Down
2 changes: 1 addition & 1 deletion src/Rimu.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Rimu
using Arrow: Arrow
using DataFrames: DataFrames, DataFrame, metadata
using DataStructures: DataStructures
using LinearAlgebra: LinearAlgebra, dot, isdiag
using LinearAlgebra: LinearAlgebra, dot, isdiag, eigvecs, norm
using OrderedCollections: OrderedCollections, LittleDict, freeze
using Parameters: Parameters, @pack!, @unpack, @with_kw
using ProgressLogging: ProgressLogging, @logprogress, @withprogress
Expand Down
24 changes: 20 additions & 4 deletions src/fciqmc.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ end
Set up the initial shift parameters for the [`FCIQMC`](@ref) algorithm.
"""
function set_up_initial_shift_parameters(algorithm::FCIQMC, hamiltonian,
starting_vectors::SMatrix{S,R}, shift, time_step
) where {S,R}
starting_vectors::SMatrix{R,S}, shift, time_step
) where {R,S}
shift_strategy = algorithm.shift_strategy

if shift === nothing
Expand All @@ -45,7 +45,7 @@ function set_up_initial_shift_parameters(algorithm::FCIQMC, hamiltonian,
initialise_shift_parameters(shift_strategy, s, walkernumber(sv), time_step)
end)
@assert length(initial_shift_parameters) == S * R
return SMatrix{S,R}(initial_shift_parameters)
return SMatrix{R,S}(initial_shift_parameters)
end

function _determine_initial_shift(hamiltonian, starting_vectors)
Expand Down Expand Up @@ -192,4 +192,20 @@ end
function advance!(algorithm, report, state::ReplicaState, replica::SpectralState{1})
return advance!(algorithm, report, state, only(replica.single_states))
end
# TODO: add advance! for SpectralState{N} where N > 1

function advance!(algorithm, report, state::ReplicaState, replica::SpectralState{N, <:Any, GramSchmidt{N}}) where {N}
proceed = true
if state.step[] % replica.spectral_strategy.orthogonalization_interval == 0
for i in 1:N
for j in 1:i-1
u = replica[i].v
v = replica[j].v
add!(u, v, -dot(u, v) / norm(v)^2)
end
end
end
for i in 1:N
proceed &= advance!(algorithm, report, state, replica[i])
end
return proceed
end
49 changes: 32 additions & 17 deletions src/pmc_simulation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,42 +21,57 @@ mutable struct PMCSimulation
end

function _set_up_starting_vectors(
start_at, n_replicas, n_spectral, style, initiator, threading, copy_vectors
ham, start_at, n_replicas, n_spectral, style, initiator, threading, copy_vectors
)
if start_at isa AbstractMatrix && size(start_at) == (n_replicas, n_spectral)
if eltype(start_at) <: Union{AbstractDVec, RMPI.MPIData} # already dvecs
if copy_vectors
return SMatrix{n_spectral, n_replicas}(deepcopy(v) for v in start_at)
return SMatrix{n_replicas, n_spectral}(deepcopy(v) for v in start_at)
else
return SMatrix{n_spectral, n_replicas}(v for v in start_at)
return SMatrix{n_replicas, n_spectral}(v for v in start_at)
end
else
return SMatrix{n_spectral, n_replicas}(
return SMatrix{n_replicas, n_spectral}(
default_starting_vector(sa; style, initiator, threading) for sa in start_at
)
end
elseif n_spectral > 1
basis = build_basis(ham; minimum_size=n_spectral*5)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I'm not sure whether the factor 5 is reasonable here. It seems a large. The larger this matrix, the better the starting vectors will be, but as soon as these numbers become larger, then eigvecs can consume a lot of resources. Maybe 2 is more reasonable?

Suggested change
basis = build_basis(ham; minimum_size=n_spectral*5)
basis = build_basis(ham; minimum_size=n_spectral*2)

We could also think about allowing the user to set the minimum_size by making it a keyword argument in ProjectorMonteCarloProblem that is passed through. What do you think?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another comment I have here is that the current code ignores the value of start_at in this branch. There might be value in allowing to pass an allowed_address_type, which can then be passed as a second argument into build_basis.

basis = sort!(basis; by=x -> diagonal_element(ham, x))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we sorting the basis here? If we later do a full diagonalisation then there is no point, as the eigenvectors returned by eigvecs will be sorted regardless of what the basis looks like.

trunc = BasisSetRepresentation(ham, basis; filter=Returns(false), sizelim=Inf)
vecs = eigvecs(Matrix(trunc))
if threading
eigs = [PDVec(zip(basis, 10*vecs[:,i]); style, initiator) for i in 1:n_spectral]
else
if initiator == NonInitiator()
eigs = [DVec(zip(basis, 10*vecs[:,i]); style) for i in 1:n_spectral]
else
eigs = [InitiatorDVec(zip(basis,10*vecs[:,i]);style,initiator) for i in 1:n_spectral]
end
end
mat = stack([eigs for _ in 1:n_replicas], dims=1)
return SMatrix{n_replicas, n_spectral}(mat)
end
n_spectral == 1 || throw(ArgumentError("The number of spectral states must match the number of starting vectors."))
if start_at isa AbstractVector && length(start_at) == n_replicas
if eltype(start_at) <: Union{AbstractDVec, RMPI.MPIData} # already dvecs
if copy_vectors
return SMatrix{n_spectral, n_replicas}(deepcopy(v) for v in start_at)
return SMatrix{n_replicas, n_spectral}(deepcopy(v) for v in start_at)
else
return SMatrix{n_spectral, n_replicas}(v for v in start_at)
return SMatrix{n_replicas, n_spectral}(v for v in start_at)
end
else
return SMatrix{n_spectral, n_replicas}(
return SMatrix{n_replicas, n_spectral}(
default_starting_vector(sa; style, initiator, threading) for sa in start_at
)
end
elseif start_at isa Union{AbstractDVec, RMPI.MPIData}
if n_replicas == 1 && !copy_vectors
return SMatrix{n_spectral, n_replicas}(start_at)
return SMatrix{n_replicas, n_spectral}(start_at)
else
return SMatrix{n_spectral, n_replicas}(deepcopy(start_at) for _ in 1:n_replicas)
return SMatrix{n_replicas, n_spectral}(deepcopy(start_at) for _ in 1:n_replicas)
end
end
return SMatrix{n_spectral,n_replicas}(
return SMatrix{n_replicas, n_spectral}(
default_starting_vector(start_at; style, initiator, threading) for _ in 1:n_replicas
)
end
Expand All @@ -80,10 +95,10 @@ function PMCSimulation(problem::ProjectorMonteCarloProblem; copy_vectors=true)

start_at = isnothing(start_at) ? starting_address(hamiltonian) : start_at
vectors = _set_up_starting_vectors(
start_at, n_replicas, n_spectral, style, initiator,
hamiltonian, start_at, n_replicas, n_spectral, style, initiator,
threading, copy_vectors
)
@assert vectors isa SMatrix{n_spectral,n_replicas}
@assert vectors isa SMatrix{n_replicas, n_spectral}

# set up initial_shift_parameters
shift, time_step = nothing, nothing
Expand All @@ -93,17 +108,17 @@ function PMCSimulation(problem::ProjectorMonteCarloProblem; copy_vectors=true)
@unpack shift, time_step = initial_shift_parameters
set_up_initial_shift_parameters(algorithm, hamiltonian, vectors, shift, time_step)
else
SMatrix{n_spectral,n_replicas}(initial_shift_parameters...)
SMatrix{n_replicas, n_spectral}(initial_shift_parameters...)
end
@assert shift_parameters isa SMatrix{n_spectral,n_replicas}
@assert shift_parameters isa SMatrix{n_replicas, n_spectral}

# set up the spectral_states
wm = working_memory(vectors[1, 1])
spectral_states = ntuple(n_replicas) do i
SpectralState(
ntuple(n_spectral) do j
v = vectors[j, i]
sp = shift_parameters[j, i]
v = vectors[i, j]
sp = shift_parameters[i, j]
id = if n_replicas * n_spectral == 1
""
elseif n_spectral == 1
Expand Down
2 changes: 2 additions & 0 deletions src/projector_monte_carlo_problem.jl
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ the [`FCIQMC`](@ref) algorithm.
- `n_replicas = 1`: Number of synchronised independent simulations.
- `replica_strategy = NoStats(n_replicas)`: Which results to report from replica
simulations, see [`ReplicaStrategy`](@ref).
- `spectral_strategy = GramSchmidt()`: The [`SpectralStrategy`](@ref) used for simulations
of spectral states.

# Example

Expand Down
26 changes: 19 additions & 7 deletions src/strategies_and_params/spectralstrategy.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

Abstract type for spectral strategies. The spectral strategy is used to control the number
of spectral states used in the simulation.

## Implemented Strategies

* [`GramSchmidt`](@ref): Orthogonalize the spectral states using the Gram-Schmidt procedure.
"""
abstract type SpectralStrategy{S} end

Expand All @@ -15,14 +19,22 @@ Return the number of spectral states used in the simulation.
num_spectral_states(::SpectralStrategy{S}) where {S} = S

"""
GramSchmidt{S} <: SpectralStrategy{S}

GramSchmidt(S; orthogonalization_interval = 1) <: SpectralStrategy{S}
Use the Gram-Schmidt procedure to orthogonalize the excited states. A total of `S` spectral
states are used in the simulation.
states are used in the simulation, and they are orthogonalized every
`orthogonalization_interval` steps.

Use with the keyword argument `spectral_strategy` in [`ProjectorMonteCarloProblem`](@ref).
"""
struct GramSchmidt{S} <: SpectralStrategy{S} end
struct GramSchmidt{S} <: SpectralStrategy{S}
orthogonalization_interval::Int
end

function GramSchmidt(S = 1; orthogonalization_interval = 1)
return GramSchmidt{S}(orthogonalization_interval)
end

function GramSchmidt(num_spectral_states = 1)
num_spectral_states > 1 && throw(ArgumentError("num_spectral_states > 1 is currently not supported."))
GramSchmidt{num_spectral_states}()
function Base.show(io::IO, gs::GramSchmidt{S}) where {S}
print(io, "GramSchmidt(", S, "; orthogonalization_interval=")
print(io, gs.orthogonalization_interval, ")")
end
41 changes: 41 additions & 0 deletions test/excited_states_tests.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using Rimu
using Test


@testset "excited state energies" begin
ham = HubbardReal1D(BoseFS(1,1,1,1,1))
pr = ExactDiagonalizationProblem(ham)
vals = solve(pr).values

spectral_strategy = GramSchmidt(3)
last_step=2000
style = IsDeterministic()
p = ProjectorMonteCarloProblem(ham; spectral_strategy, last_step, style)
s = solve(p)
df = DataFrame(s)
energy1 = shift_estimator(df, shift="shift_s1_1", skip=1000)
energy2 = shift_estimator(df, shift="shift_s2_1", skip=1000)
energy3 = shift_estimator(df, shift="shift_s3_1", skip=1000)

@test energy1.mean ≈ vals[1]
@test energy2.mean ≈ vals[2]
@test energy3.mean ≈ vals[3]

n_replicas = 2
p = ProjectorMonteCarloProblem(ham; spectral_strategy, last_step, style, n_replicas)
s = solve(p)
df = DataFrame(s)
energy1 = shift_estimator(df, shift="shift_s1_1", skip=1000)
energy2 = shift_estimator(df, shift="shift_s2_1", skip=1000)
energy3 = shift_estimator(df, shift="shift_s3_1", skip=1000)
energy4 = shift_estimator(df, shift="shift_s1_2", skip=1000)
energy5 = shift_estimator(df, shift="shift_s2_2", skip=1000)
energy6 = shift_estimator(df, shift="shift_s3_2", skip=1000)

@test energy1.mean ≈ vals[1]
@test energy2.mean ≈ vals[2]
@test energy3.mean ≈ vals[3]
@test energy4.mean ≈ vals[1]
@test energy5.mean ≈ vals[2]
@test energy6.mean ≈ vals[3]
end
4 changes: 4 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -183,4 +183,8 @@ end
# For example, replace `using Foo` with `using Foo: bar, baz`.
end

@safetestset "excited states" begin
include("excited_states_tests.jl")
end

# Note: Running Rimu with several MPI ranks is tested seperately on GitHub CI and not here.
Loading