Skip to content

Commit 7cb56f7

Browse files
authored
Observables (#278)
* Allow Vector's of AbstractHamiltonian's in AllOverlaps * allows_address_type replaces allowed_address_type in the AbstractHamiltonians interface * valtype for AbstractHamiltonian and ProjectorMonteCarloProblem error message if using complex Hamiltonian with ProjectorMonteCarloProblem * fix docs * attempt to fix docs * AbstractOperator Define a new supertype for AbstractHamiltonian with less stringent requirements on the interface * use VectorInterface.scalartype for AbstractOperators (instead of Base.valtype) * move dot_from_right to Interfaces.jl as part of the AbstractOperator Interface * fix tests * make correlators AbstractOperator * fix docs * docs * fix error in test * test_operator_interface IsDeterministic default for complex PDVec * fix test * test operator interface for G2RealSpace empty constructor for AbstractDVec accepts a style argument * fix test * changes to call structure of StringCorrelator test more AbstractOperators * fix docs * demote ParticleNumberOperator to AbstractHamiltonian; it now will not accept passing an address * make SingleParticleExcitation and TwoParticleExcitation AbstractOperator * Momentum is AbstractOperator * Hamiltonian interface test for momentum
1 parent ecda02c commit 7cb56f7

35 files changed

+796
-437
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "Rimu"
22
uuid = "c53c40cc-bd84-11e9-2cf4-a9fde2b9386e"
33
authors = ["Joachim Brand <j.brand@massey.ac.nz>"]
4-
version = "0.12.1"
4+
version = "0.13.0"
55

66
[deps]
77
Arrow = "69666777-d1a9-59fb-9406-91d4454c9d45"

docs/src/hamiltonians.md

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,16 @@ Stoquastic
6969
```
7070

7171
## Observables
72-
Observables are [`AbstractHamiltonian`](@ref)s that represent a physical
73-
observable. Their ground state expectation values can be sampled by passing
74-
them into [`AllOverlaps`](@ref).
72+
Observables are [`AbstractOperator`](@ref)s that represent a physical
73+
observable. Their expectation values can be sampled during a
74+
[`ProjectorMonteCarloProblem`](@ref) simulation by passing
75+
them into a suitable [`ReplicaStrategy`](@ref), e.g.
76+
[`AllOverlaps`](@ref). [`AbstractOperator`](@ref) is a supertype of
77+
[`AbstractHamiltonian`](@ref) and has less stringent
78+
requirements. Some observables are also [`AbstractHamiltonian`](@ref)s.
79+
7580
```@docs
81+
AbstractOperator
7682
ParticleNumberOperator
7783
G2RealCorrelator
7884
G2RealSpace
@@ -114,7 +120,10 @@ random_offdiagonal
114120
Hamiltonians.LOStructure
115121
dimension
116122
has_adjoint
117-
allowed_address_type
123+
allows_address_type
124+
Base.eltype
125+
VectorInterface.scalartype
126+
mul!
118127
```
119128

120129
This interface relies on unexported functionality, including

scripts/G2-example.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ G2list = ((G2RealCorrelator(d) for d in dvals)...,)
4747
# To obtain an unbiased result, at least two replicas should be used. One can also use more
4848
# than two to improve the statistics. This is particularly helpful when the walker number is
4949
# low.
50-
num_replicas = 3
51-
replica_strategy = AllOverlaps(num_replicas; operator = G2list)
50+
n_replicas = 3
51+
replica_strategy = AllOverlaps(n_replicas; operator=G2list)
5252

5353
# Other FCIQMC parameters and strategies can be set in the same way as before.
5454
steps_equilibrate = 1_000
@@ -93,7 +93,7 @@ println(filter(contains("Op"), names(df)))
9393
# equilibration steps.
9494

9595
# Now, we can calculate the correlation function for each value of ``d``.
96-
println("Two-body correlator from $num_replicas replicas:")
96+
println("Two-body correlator from $n_replicas replicas:")
9797
for d in dvals
9898
r = rayleigh_replica_estimator(df; op_name = "Op$(d+1)", skip=steps_equilibrate)
9999
println(" G2($d) = $(r.f) ± $(r.σ_f)")
@@ -105,8 +105,8 @@ end
105105

106106
# Since we ran multiple independent replicas, we also have multiple estimates of the shift
107107
# energy.
108-
println("Shift energy from $num_replicas replicas:")
109-
for i in 1:num_replicas
108+
println("Shift energy from $n_replicas replicas:")
109+
for i in 1:n_replicas
110110
se = shift_estimator(df; shift="shift_$i", skip=steps_equilibrate)
111111
println(" Replica $i: $(se.mean) ± $(se.err)")
112112
end

src/BitStringAddresses/multicomponent.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ function CompositeFS(adds::Vararg{SingleComponentFockAddress})
7272
return CompositeFS{length(adds),N,M1,typeof(adds)}(adds)
7373
end
7474

75-
num_components(::CompositeFS{C}) where {C} = C
75+
num_components(::Type{<:CompositeFS{C}}) where {C} = C
7676
Base.hash(c::CompositeFS, u::UInt) = hash(c.components, u)
7777

7878
function print_address(io::IO, c::CompositeFS{C}; compact=false) where {C}

src/DictVectors/DictVectors.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ import MPI
2121
using ..Interfaces: Interfaces, AbstractDVec, AdjointUnknown,
2222
CompressionStrategy, IsDiagonal, LOStructure,
2323
apply_column!, apply_operator!, compress!,
24-
diagonal_element, offdiagonals, step_stats, AbstractHamiltonian
24+
diagonal_element, offdiagonals, step_stats, AbstractHamiltonian, AbstractOperator,
25+
dot_from_right
2526
using ..StochasticStyles: StochasticStyles, IsDeterministic
2627

2728
import ..Interfaces: deposit!, storage, StochasticStyle, default_style, freeze, localpart,

src/DictVectors/abstractdvec.jl

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ walkernumber_and_length(v) = walkernumber(v), length(v)
263263
###
264264
### Vector-operator functions
265265
###
266-
function LinearAlgebra.mul!(w::AbstractDVec, h::AbstractHamiltonian, v::AbstractDVec)
266+
function LinearAlgebra.mul!(w::AbstractDVec, h::AbstractOperator, v::AbstractDVec)
267267
empty!(w)
268268
for (key, val) in pairs(v)
269269
w[key] += diagonal_element(h, key) * val
@@ -274,36 +274,34 @@ function LinearAlgebra.mul!(w::AbstractDVec, h::AbstractHamiltonian, v::Abstract
274274
return w
275275
end
276276

277-
function Base.:*(h::AbstractHamiltonian, v::AbstractDVec)
278-
return mul!(zerovector(v, promote_type(eltype(h), valtype(v))), h, v)
277+
function Base.:*(h::AbstractOperator, v::AbstractDVec)
278+
T = promote_type(scalartype(h), scalartype(v))
279+
if eltype(h) scalartype(h)
280+
throw(ArgumentError("Operators with non-scalar eltype don't support "*
281+
"multiplication with `*`. Use `mul!` or `dot` instead."))
282+
end
283+
# first argument in mul! requires IsDeterministic style
284+
w = empty(v, T; style=IsDeterministic{T}())
285+
return mul!(w, h, v)
279286
end
280287

281-
"""
282-
dot(w, op::AbstractHamiltonian, v)
283-
284-
Evaluate `w⋅op(v)` minimizing memory allocations.
285-
"""
286-
function LinearAlgebra.dot(w::AbstractDVec, op::AbstractHamiltonian, v::AbstractDVec)
288+
# docstring in Interfaces
289+
function LinearAlgebra.dot(w::AbstractDVec, op::AbstractOperator, v::AbstractDVec)
287290
return dot(LOStructure(op), w, op, v)
288291
end
289-
function LinearAlgebra.dot(::AdjointUnknown, w, op::AbstractHamiltonian, v)
292+
function LinearAlgebra.dot(::AdjointUnknown, w, op::AbstractOperator, v)
290293
return dot_from_right(w, op, v)
291294
end
292-
function LinearAlgebra.dot(::LOStructure, w, op::AbstractHamiltonian, v)
295+
function LinearAlgebra.dot(::LOStructure, w, op::AbstractOperator, v)
293296
if length(w) < length(v)
294297
return conj(dot_from_right(v, op', w)) # turn args around to execute faster
295298
else
296299
return dot_from_right(w, op, v) # original order
297300
end
298301
end
299302

300-
"""
301-
dot_from_right(w, op::AbstractHamiltonian, v)
302-
303-
Internal function evaluates the 3-argument `dot()` function in order from right
304-
to left.
305-
"""
306-
function dot_from_right(w, op, v::AbstractDVec)
303+
# docstring in Interfaces
304+
function Interfaces.dot_from_right(w, op, v::AbstractDVec)
307305
T = typeof(zero(valtype(w)) * zero(eltype(op)) * zero(valtype(v)))
308306
result = zero(T)
309307
for (key, val) in pairs(v)

src/DictVectors/dvec.jl

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ DVec{Symbol,Float64} with 2 entries, style = IsDeterministic{Float64}()
4141
:b => 3.0
4242
```
4343
"""
44-
struct DVec{K,V,S<:StochasticStyle{V},D<:AbstractDict{K,V}} <: AbstractDVec{K,V}
44+
struct DVec{K,V,S<:StochasticStyle,D<:AbstractDict{K,V}} <: AbstractDVec{K,V}
4545
storage::D
4646
style::S
4747
end
@@ -50,16 +50,40 @@ end
5050
### Constructors
5151
###
5252
# Vararg
53-
function DVec(args...; kwargs...)
53+
function DVec(args...; style = nothing, kwargs...)
5454
storage = Dict(args...)
55-
return DVec(storage; kwargs...)
55+
if style === nothing
56+
return DVec(storage; kwargs...)
57+
else
58+
# if style is given, check value type compatibility and convert if necessary
59+
V = valtype(storage)
60+
S = scalartype(style)
61+
if V <: AbstractArray
62+
if eltype(V) != S
63+
throw(ArgumentError("Style $(style) is incompatible with value type $(V). "*
64+
"Use a style with scalartype(style) == $(eltype(V)) instead."))
65+
end
66+
# no conversion necessary, the types are compatible
67+
else # V is a scalar type
68+
# try to convert storage to the appropriate type
69+
K = keytype(storage)
70+
storage = Dict{K,S}(storage)
71+
end
72+
return DVec(storage; style, kwargs...)
73+
end
5674
end
5775

76+
# from Dict; check style compatibility
5877
function DVec(
59-
dict::Dict{K}; style::StochasticStyle{V}=default_style(valtype(dict)), capacity=0
78+
dict::Dict{K,V};
79+
style::StochasticStyle=default_style(V),
80+
capacity=0
6081
) where {K,V}
61-
storage = convert(Dict{K,V}, dict)
62-
return DVec(storage, style)
82+
if !(V == scalartype(style) || eltype(V) == scalartype(style))
83+
throw(ArgumentError("Style $(style) is incompatible with value type $(V). "*
84+
"Use a style with scalartype(style) == $(eltype(V)) instead."))
85+
end
86+
return DVec{K,V,typeof(style),typeof(dict)}(dict, style)
6387
end
6488
# Empty constructor.
6589
function DVec{K,V}(; style::StochasticStyle=default_style(V), capacity=0) where {K,V}
@@ -71,17 +95,17 @@ function DVec(dv::AbstractDVec{K,V}; style=StochasticStyle(dv), capacity=0) wher
7195
return copyto!(dvec, dv)
7296
end
7397

74-
function Base.empty(dvec::DVec{K,V}) where {K,V}
75-
return DVec{K,V}(; style=StochasticStyle(dvec))
98+
function Base.empty(dvec::DVec{K,V}; style=dvec.style) where {K,V}
99+
return DVec{K,V}(; style)
76100
end
77-
function Base.empty(dvec::DVec{K,V}, ::Type{V}) where {K,V}
78-
return empty(dvec)
101+
function Base.empty(dvec::DVec{K,V}, ::Type{V}; style=dvec.style) where {K,V}
102+
return empty(dvec; style)
79103
end
80-
function Base.empty(dvec::DVec{K,V}, ::Type{W}) where {K,V,W}
81-
return DVec{K,W}()
104+
function Base.empty(::DVec{K}, ::Type{V}; style=default_style(V)) where {K,V}
105+
return DVec{K,V}(; style)
82106
end
83-
function Base.empty(dvec::DVec, ::Type{K}, ::Type{V}) where {K,V}
84-
return DVec{K,V}()
107+
function Base.empty(::DVec, ::Type{K}, ::Type{V}; style=default_style(V)) where {K,V}
108+
return DVec{K,V}(; style)
85109
end
86110

87111
###

src/DictVectors/initiatordvec.jl

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -120,17 +120,17 @@ function InitiatorDVec(
120120
return InitiatorDVec(copy(storage(dv)); style, initiator, capacity)
121121
end
122122

123-
function Base.empty(dvec::InitiatorDVec{K,V}) where {K,V}
124-
return InitiatorDVec{K,V}(; style=dvec.style, initiator=dvec.initiator)
123+
function Base.empty(dvec::InitiatorDVec{K,V}; style=dvec.style) where {K,V}
124+
return InitiatorDVec{K,V}(; style, initiator=dvec.initiator)
125125
end
126-
function Base.empty(dvec::InitiatorDVec{K,V}, ::Type{V}) where {K,V}
127-
return empty(dvec)
126+
function Base.empty(dvec::InitiatorDVec{K,V}, ::Type{V}; style=dvec.style) where {K,V}
127+
return empty(dvec; style)
128128
end
129-
function Base.empty(dvec::InitiatorDVec{K}, ::Type{V}) where {K,V}
130-
return InitiatorDVec{K,V}(; initiator=dvec.initiator)
129+
function Base.empty(dvec::InitiatorDVec{K}, ::Type{V}; style=default_style(V)) where {K,V}
130+
return InitiatorDVec{K,V}(; style, initiator=dvec.initiator)
131131
end
132-
function Base.empty(dvec::InitiatorDVec, ::Type{K}, ::Type{V}) where {K,V}
133-
return InitiatorDVec{K,V}(; initiator=dvec.initiator)
132+
function Base.empty(dvec::InitiatorDVec, ::Type{K}, ::Type{V}; style=default_style(V)) where {K,V}
133+
return InitiatorDVec{K,V}(; style, initiator=dvec.initiator)
134134
end
135135

136136
###

src/DictVectors/pdvec.jl

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -377,11 +377,15 @@ end
377377
function Base.empty(t::PDVec{K,V}, ::Type{V}; kwargs...) where {K,V}
378378
return empty(t; kwargs...)
379379
end
380-
function Base.empty(t::PDVec{K,<:Any,N}, ::Type{V}; kwargs...) where {K,V,N}
381-
return PDVec{K,V,N}(; kwargs...)
380+
function Base.empty(
381+
t::PDVec{K,<:Any,N}, ::Type{V}; style=default_style(V), kwargs...
382+
) where {K,V,N}
383+
return PDVec{K,V,N}(; style, kwargs...)
382384
end
383-
function Base.empty(t::PDVec{<:Any,<:Any,N}, ::Type{K}, ::Type{V}; kwargs...) where {K,V,N}
384-
return PDVec{K,V,N}(; kwargs...)
385+
function Base.empty(
386+
t::PDVec{<:Any,<:Any,N}, ::Type{K}, ::Type{V}; style=default_style(V), kwargs...
387+
) where {K,V,N}
388+
return PDVec{K,V,N}(; style, kwargs...)
385389
end
386390
function Base.empty!(t::PDVec)
387391
Folds.foreach(empty!, t.segments, )
@@ -744,53 +748,53 @@ end
744748
### Operator linear algebra operations
745749
###
746750
"""
747-
mul!(y::PDVec, A::AbstractHamiltonian, x::PDVec[, w::PDWorkingMemory])
751+
mul!(y::PDVec, A::AbstractOperator, x::PDVec[, w::PDWorkingMemory])
748752
749753
Perform `y = A * x` in-place. The working memory `w` is required to facilitate
750754
threaded/distributed operations. If not passed a new instance will be allocated. `y` and `x`
751755
may be the same vector.
752756
753-
See [`PDVec`](@ref), [`PDWorkingMemory`](@ref).
757+
See [`PDVec`](@ref), [`PDWorkingMemory`](@ref), [`AbstractOperator`](@ref).
754758
"""
755759
function LinearAlgebra.mul!(
756-
y::PDVec, op::AbstractHamiltonian, x::PDVec,
757-
w=PDWorkingMemory(y; style=IsDeterministic()),
760+
y::PDVec, op::AbstractOperator, x::PDVec,
761+
w=PDWorkingMemory(y; style=StochasticStyle(y)),
758762
)
759-
if w.style IsDeterministic()
763+
if !(w.style isa IsDeterministic)
760764
throw(ArgumentError(
761765
"Attempted to use `mul!` with non-deterministic working memory. " *
762766
"Use `apply_operator!` instead."
763767
))
764768
end
765-
_, _, wm, y = apply_operator!(w, y, x, op)
769+
_, _, _, y = apply_operator!(w, y, x, op)
766770
return y
767771
end
768772

769773
"""
770-
dot(y::PDVec, A::AbstractHamiltonian, x::PDVec[, w::PDWorkingMemory])
774+
dot(y::PDVec, A::AbstractOperator, x::PDVec[, w::PDWorkingMemory])
771775
772776
Perform `y ⋅ A ⋅ x`. The working memory `w` is required to facilitate threaded/distributed
773777
operations with non-diagonal `A`. If needed and not passed a new instance will be
774778
allocated. `A` can be replaced with a tuple of operators.
775779
776780
See [`PDVec`](@ref), [`PDWorkingMemory`](@ref).
777781
"""
778-
function LinearAlgebra.dot(t::PDVec, op::AbstractHamiltonian, u::PDVec, w)
782+
function LinearAlgebra.dot(t::PDVec, op::AbstractOperator, u::PDVec, w)
779783
return dot(LOStructure(op), t, op, u, w)
780784
end
781-
function LinearAlgebra.dot(t::PDVec, op::AbstractHamiltonian, u::PDVec)
785+
function LinearAlgebra.dot(t::PDVec, op::AbstractOperator, u::PDVec)
782786
return dot(LOStructure(op), t, op, u)
783787
end
784788
function LinearAlgebra.dot(
785-
::IsDiagonal, t::PDVec, op::AbstractHamiltonian, u::PDVec, w=nothing
789+
::IsDiagonal, t::PDVec, op::AbstractOperator, u::PDVec, w=nothing
786790
)
787791
T = typeof(zero(valtype(t)) * zero(valtype(u)) * zero(eltype(op)))
788792
return sum(pairs(u); init=zero(T)) do (k, v)
789793
T(conj(t[k]) * diagonal_element(op, k) * v)
790794
end
791795
end
792796
function LinearAlgebra.dot(
793-
::LOStructure, left::PDVec, op::AbstractHamiltonian, right::PDVec, w=nothing
797+
::LOStructure, left::PDVec, op::AbstractOperator, right::PDVec, w=nothing
794798
)
795799
# First two cases: only one vector is distrubuted. Avoid shuffling things around
796800
# by placing that one on the left to reduce the need for communication.
@@ -809,7 +813,7 @@ function LinearAlgebra.dot(
809813
end
810814
# Default variant: also called from other LOStructures.
811815
function LinearAlgebra.dot(
812-
::AdjointUnknown, t::PDVec, op::AbstractHamiltonian, source::PDVec, w=nothing
816+
::AdjointUnknown, t::PDVec, op::AbstractOperator, source::PDVec, w=nothing
813817
)
814818
if is_distributed(t)
815819
if isnothing(w)
@@ -823,7 +827,7 @@ function LinearAlgebra.dot(
823827
return dot_from_right(target, op, source)
824828
end
825829

826-
function dot_from_right(target, op, source::PDVec)
830+
function Interfaces.dot_from_right(target, op, source::PDVec)
827831
T = typeof(zero(valtype(target)) * zero(valtype(source)) * zero(eltype(op)))
828832

829833
result = sum(pairs(source); init=zero(T)) do (k, v)

src/DictVectors/projectors.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ struct UniformProjector <: AbstractProjector end
4040
VectorInterface.inner(::UniformProjector, y::DVecOrVec) = sum(values(y))
4141
Base.getindex(::UniformProjector, add) = 1
4242

43-
function VectorInterface.inner(::UniformProjector, op::AbstractHamiltonian, v::AbstractDVec)
43+
function LinearAlgebra.dot(::UniformProjector, op::AbstractOperator, v::AbstractDVec)
4444
return sum(pairs(v)) do (key, val)
4545
diag = diagonal_element(op, key) * val
4646
offdiag = sum(offdiagonals(op, key)) do (add, elem)

0 commit comments

Comments
 (0)