• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

RimuQMC / Rimu.jl / 14949967345

10 May 2025 11:08PM UTC coverage: 94.621% (-0.09%) from 94.706%
14949967345

Pull #314

github

joachimbrand
VMSize and get_vmsize
Pull Request #314: Provide guidance for exact diagonalization algorithms

76 of 84 new or added lines in 10 files covered. (90.48%)

1 existing line in 1 file now uncovered.

6983 of 7380 relevant lines covered (94.62%)

11371990.96 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.66
/src/ExactDiagonalization/exact_diagonalization_problem.jl
1
"""
2
    ExactDiagonalizationProblem(hamiltonian::AbstractHamiltonian, [v0]; kwargs...)
3

4
Defines an exact diagonalization problem with an [`AbstractHamiltonian`](@ref) `hamiltonian`.
5
Optionally, a starting vector of type [`AbstractDVec`](@ref), or a single address or a
6
collection of addresses can be passed as `v0`.
7

8
`ExactDiagonalizationProblem`s can be solved with
9
[`solve`](@ref solve(::ExactDiagonalizationProblem)).
10

11
# Keyword arguments
12
- `algorithm=LinearAlgebraSolver()`: The algorithm to use for solving the problem. The
13
    algorithm can also be specified as the second positional argument in the `init`
14
    function.
15
- `linear_dimension` (optional): The estimated dimension of the problem. This is
16
    usually automatically determined from the Hamiltonian.
17
- `info=false`: Whether to print additional information about memory requirements.
18
- `warn=true`: Whether to print warnings if available memory is insufficient.
19
- Optional keyword arguments will be passed on to the `init` and `solve` functions.
20

21
# Algorithms
22
- [`LinearAlgebraSolver()`](@ref): An algorithm for solving the problem using the
23
    dense-matrix eigensolver from the `LinearAlgebra` standard library (eventually using
24
    LAPACK). Only suitable for small matrices.
25
- [`KrylovKitSolver(matrix_free=true)`](@ref): An algorithm for finding a few eigenvalues
26
    and vectors. With `matrix_free=true` the problem is solved without instatiating a
27
    matrix. This is suitable for large dimensions. With `matrix_free=false` the problem is
28
    solved after instantiating a sparse matrix. This is faster if sufficient memory is
29
    available. Requires `using KrylovKit`.
30
- [`ArpackSolver()`](@ref): An algorithm for solving the problem after instantiating a
31
    sparse matrix and using the Arpack Fortran library. Requires `using Arpack`.
32
- [`LOBPCGSolver()`](@ref): An algorithm for solving the problem after instantiating a
33
    sparse matrix using the LOBPCG method. Requires `using IterativeSolvers`.
34

35
# Keyword arguments for matrix-based algorithms (also accepted by [`init`](@ref init(::ExactDiagonalizationProblem)))
36
See [`BasisSetRepresentation`](@ref) for more information.
37
- `sizelim`: The maximum size of the basis set representation. The default is `10^6` for
38
    sparse matrices and `10^5` for dense matrices.
39
- `cutoff`: A cutoff value for the basis set representation.
40
- `filter`: A filter function for the basis set representation.
41
- `max_depth = Inf`: Limit the depth when building the matrix.
42
- `minimum_size = Inf`: Stop building the matrix after this size is reached.
43
- `nnzs = 0`: A hint for the number of non-zero elements in the basis set representation.
44
  Setting a non-zero value can speed up the computation.
45
- `col_hint = 0`: A hint for the number of columns in the basis set representation.
46
- `sort = false`: Whether to sort the basis set representation.
47

48
# Keyword arguments for iterative algorithms (also accepted by [`solve`](@ref solve(::ExactDiagonalizationProblem)))
49
- `verbose = false`: Whether to print additional information.
50
- `abstol = nothing`: The absolute tolerance for the solver. If `nothing`, the solver
51
    chooses a default value.
52
- `howmany = 1`: The minimum number of eigenvalues to compute.
53
- `which = :SR`: Whether to compute the largest or smallest eigenvalues.
54
- `maxiters = nothing`: The maximum number of iterations for the solver. If `nothing`, the
55
    solver chooses a default value.
56

57
# Solving an `ExactDiagonalizationProblem`
58
The [`solve`](@ref solve(::ExactDiagonalizationProblem)) function can be called directly on
59
an `ExactDiagonalizationProblem` to solve it. Alternatively, the
60
[`init`](@ref init(::ExactDiagonalizationProblem)) function can be used to initialize a
61
solver, which can then be solved with [`solve`](@ref solve(::ExactDiagonalizationProblem)).
62
The [`solve`](@ref solve(::ExactDiagonalizationProblem)) function returns a result
63
type with the eigenvalues, eigenvectors, and convergence information.
64

65
## Result type
66
The result type for the [`solve`](@ref solve(::ExactDiagonalizationProblem)) function is
67
determined by the algorithm used. It has the following fields:
68
- `values::Vector`: The eigenvalues.
69
- `vectors::Vector{<:AbstractDVec}`: The eigenvectors.
70
- `success::Bool`: A boolean flag indicating whether the solver was successful.
71
- `info`: Convergence information.
72
- `algorithm`: The algorithm used for the computation.
73
- `problem`: The `ExactDiagonalizationProblem` that was solved.
74
- Additional fields may be present depending on the algorithm used.
75

76
Iterating the result type will yield the eigenvalues, eigenvectors, and a boolean flag
77
`success` in that order.
78

79
# Examples
80
```jldoctest
81
julia> p = ExactDiagonalizationProblem(HubbardReal1D(BoseFS(1,1,1)))
82
ExactDiagonalizationProblem(
83
  HubbardReal1D(fs"|1 1 1⟩"; u=1.0, t=1.0),
84
  nothing;
85
  algorithm=LinearAlgebraSolver(),
86
  linear_dimension=10,
87
  NamedTuple()...
88
)
89

90
julia> result = solve(p) # convert to dense matrix and solve with LinearAlgebra.eigen
91
EDResult for algorithm LinearAlgebraSolver() with 10 eigenvalue(s),
92
  values = [-5.09593, -1.51882, -1.51882, 1.55611, 1.6093, 1.6093, 4.0, 4.53982, 4.90952, 4.90952]
93
  Convergence info: "Dense matrix eigensolver solution from `LinearAlgebra.eigen`", with howmany = 10 eigenvalues requested.
94
  success = true.
95

96
julia> using KrylovKit # an external package has to be installed and loaded
97

98
julia> s = init(p; algorithm = KrylovKitSolver(true)) # solve without building a matrix
99
IterativeEDSolver
100
 with algorithm KrylovKitSolver(matrix_free = true,) for hamiltonian = HubbardReal1D(fs"|1 1 1⟩"; u=1.0, t=1.0),
101
  kwargs = NamedTuple()
102
)
103

104
julia> values, vectors, success = solve(s);
105

106
julia> result.values[1] ≈ values[1]
107
true
108
```
109
See also [`solve(::ExactDiagonalizationProblem)`](@ref),
110
[`init(::ExactDiagonalizationProblem)`](@ref),
111
[`KrylovKitSolver`](@ref), [`ArpackSolver`](@ref), [`LinearAlgebraSolver`](@ref).
112
!!! note
113
    Using the `KrylovKitSolver()` algorithms requires the
114
    KrylovKit.jl package. The package can be loaded with `using KrylovKit`.
115
    Using the `ArpackSolver()` algorithm requires the Arpack.jl package. The package can be
116
    loaded with `using Arpack`.
117
    Using the `LOBPCGSolver()` algorithm requires the IterativeSolvers.jl package. The package
118
    can be loaded with `using IterativeSolvers`.
119
"""
120
struct ExactDiagonalizationProblem{H<:AbstractHamiltonian,V,ALG<:AbstractAlgorithm,AV}
121
    hamiltonian::H
334✔
122
    initial_vector::V
123
    algorithm::ALG
124
    linear_dimension::Number
125
    addr_or_vec::AV # starting address or iterable of addresses
126
    kwargs::NamedTuple
127
end
128

129
function ExactDiagonalizationProblem(
797✔
130
    hamiltonian::H, initial_vector::V=nothing;
131
    linear_dimension=nothing, algorithm::ALG=LinearAlgebraSolver(),
132
    info=false, warn=true, kwargs...
133
) where {H<:AbstractHamiltonian,V,ALG<:AbstractAlgorithm}
134
    # Set up the starting address or vector of addresses
135
    addr_or_vec = _set_up_starting_address(initial_vector, hamiltonian)
335✔
136
    if linear_dimension === nothing
334✔
137
        linear_dimension = dimension(
314✔
138
            hamiltonian,
139
            addr_or_vec isa AbstractFockAddress ? addr_or_vec : first(addr_or_vec)
140
        )
141
    end
142
    dense, matrix_free, sparse = estimate_memory_requirement(
351✔
143
        hamiltonian, linear_dimension
144
    )
145
    free_memory = Sys.free_memory() # available memory in bytes
334✔
146
    total_memory = Sys.total_memory() # total memory in bytes
668✔
147
    if info
334✔
148
        message = "Setting up ExactDiagonalizationProblem with algorithm $(algorithm)\n"*
2✔
149
            f"- Linear dimension is {float(linear_dimension):.1e}\n"*
150
            f"- Estimated memory requirements: dense = {dense/1e6:.1e} MB, "*
151
            f"matrix_free = {matrix_free/1e6:.1e} MB, "*
152
            f"sparse = {sparse/1e6:.1e} MB\n"*
153
            f"- Total available memory: {total_memory/1e6:.1e} MB, free: {free_memory/1e6:.1e} MB"
154
        @info message
1✔
155
    end
156
    if warn
334✔
157
        if algorithm isa LinearAlgebraSolver && total_memory < dense
285✔
158
            message = "ExactDiagonalizationProblem with algorithm $(algorithm):\n"*
2✔
159
                f"Available memory may be less than the required memory for a dense matrix.\n\n"*
160
                f"Total available memory: {total_memory/1e6:.1e} MB, required: {dense/1e6:.1e} MB.\n"*
161
                "Consider using a sparse algorithm like `algorithm=KrylovKitSolver()`!"
162
            @warn message
1✔
163
        elseif algorithm isa AbstractAlgorithm{false} && total_memory < sparse
284✔
NEW
164
            message = "ExactDiagonalizationProblem with algorithm $(algorithm):\n"*
×
165
                f"Available memory may be less than the required memory for a sparse matrix.\n\n"*
166
                f"Total available memory: {total_memory / 1e6:.1e} MB, required: {sparse / 1e6:.1e} MB.\n"*
167
                "Consider using a matrix-free algorithm like `algorithm=KrylovKitSolver(; matrix_free=true)`!"
NEW
168
            @warn message
×
169
        elseif algorithm isa AbstractAlgorithm{true} && total_memory < matrix_free
284✔
NEW
170
            message = "ExactDiagonalizationProblem with algorithm $(algorithm)\n"*
×
171
                f"Available memory may be less than the required memory for a matrix-free algorithm.\n"*
172
                f"Total available memory: {total_memory / 1e6:.1e} MB, required: {matrix_free/1e6:.1e} MB"
NEW
173
            @warn message
×
174
        end
175
    end
176
    return ExactDiagonalizationProblem{H,V,ALG,typeof(addr_or_vec)}(
334✔
177
        hamiltonian, initial_vector, algorithm, linear_dimension, addr_or_vec,
178
        NamedTuple(kwargs)
179
    )
180
end
181

182
function ExactDiagonalizationProblem(
96✔
183
    hamiltonian::AbstractHamiltonian, v0::AbstractDVec; kwargs...
184
)
185
    return ExactDiagonalizationProblem(
96✔
186
        hamiltonian, FrozenDVec(collect(pairs(v0))); kwargs...
187
    )
188
end
189

190
function Base.show(io::IO, p::ExactDiagonalizationProblem)
21✔
191
    io = IOContext(io, :compact => true)
21✔
192
    print(io, "ExactDiagonalizationProblem(\n  ")
21✔
193
    show(io, p.hamiltonian)
21✔
194
    print(io, ",\n  ")
21✔
195
    show(io, p.initial_vector)
21✔
196
    print(io, ";\n  ")
21✔
197
    print(io, "algorithm=$(p.algorithm),\n  ")
21✔
198
    print(io, "linear_dimension=$(p.linear_dimension),\n  ")
21✔
199
    show(io, p.kwargs)
21✔
200
    print(io, "...\n)")
21✔
201
end
202
function Base.:(==)(p1::ExactDiagonalizationProblem, p2::ExactDiagonalizationProblem)
44✔
203
    return p1.hamiltonian == p2.hamiltonian &&
44✔
204
        p1.initial_vector == p2.initial_vector &&
205
        p1.kwargs == p2.kwargs
206
end
207

208
Rimu.Hamiltonians.dimension(p::ExactDiagonalizationProblem) = p.linear_dimension
24✔
209

210
function _set_up_starting_address(v0, ham)
335✔
211
    if isnothing(v0)
335✔
212
        addr_or_vec = starting_address(ham)
190✔
213
    elseif allows_address_type(ham, v0) ||
145✔
214
           v0 isa Union{NTuple,Vector} && allows_address_type(ham, eltype(v0))
215
        addr_or_vec = v0
72✔
216
    elseif v0 isa FrozenDVec
73✔
217
        addr_or_vec = keys(v0)
72✔
218
    else
219
        throw(ArgumentError("Invalid starting vector in `ExactDiagonalizationProblem`."))
1✔
220
    end
221

222
    return addr_or_vec # single address or iterable of addresses
334✔
223
end
224

225
"""
226
    estimate_memory_requirement(::ExactDiagonalizationProblem)
227
    estimate_memory_requirement(h::AbstractHamiltonian, linear_dimension=dimension(h))
228
    -> (; dense, matrix_free, sparse)
229

230
Estimate the memory requirement for an [`ExactDiagonalizationProblem`](@ref).
231
This function estimates the memory requirement based on the linear dimension and the
232
Hamiltonian. It returns an estimate of the memory size in bytes for three
233
different types of algorithms:
234
- `dense`: The memory requirement for [`LinearAlgebraSolver()`](@ref) using a dense matrix.
235
- `matrix_free`: The memory requirement for [`KrylovKitSolver(; matrix_free=true)`](@ref)
236
  using a matrix-free algorithm.
237
- `sparse`: The memory requirement for [`KrylovKitSolver(; matrix_free=false)`](@ref)
238
  using a sparse matrix.
239

240
The memory requirements for other sparse solvers are expected to be similar to the
241
`KrylovKitSolver` estimates.
242
"""
NEW
243
function estimate_memory_requirement(prob::ExactDiagonalizationProblem)
×
NEW
244
    return estimate_memory_requirement(prob.hamiltonian, prob.linear_dimension)
×
245
end
246

247
function estimate_memory_requirement(
334✔
248
    hamiltonian::AbstractHamiltonian,
249
    linear_dimension=dimension(hamiltonian)
250
)
251
    krylovdim = 30
334✔
252
    # standard Krylov dimension in KrylovKit, could be read from the algorithm
253
    # LOBPCG should need much less memory
254
    address_size = sizeof(starting_address(hamiltonian))
334✔
255
    column_size = Rimu.num_offdiagonals(hamiltonian, starting_address(hamiltonian))
605✔
256
    coefficient_size = sizeof(eltype(hamiltonian))
334✔
257

258
    dense = 2 * linear_dimension^2 * coefficient_size + # for dense matrix and QR
334✔
259
        linear_dimension * address_size # for basis
260

261
    matrix_free = krylovdim * linear_dimension * coefficient_size + # for solver algorithm
334✔
262
        2 * linear_dimension * (address_size + coefficient_size) # for LinearMap
263

264
    sparse = linear_dimension * column_size * coefficient_size + # for sparse matrix
334✔
265
        linear_dimension * address_size + # for basis
266
        krylovdim * linear_dimension * coefficient_size # for solver algorithm
267

268
    return (; dense, matrix_free, sparse)
334✔
269
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc