• 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

93.2
/src/pmc_simulation.jl
1
"""
2
    PMCSimulation
3
Holds the state and the results of a projector quantum Monte Carlo (PMC) simulation.
4
Is returned by [`init(::ProjectorMonteCarloProblem)`](@ref) and solved with
5
[`solve!(::PMCSimulation)`](@ref).
6

7
Obtain the results of a simulation `sm` as a DataFrame with `DataFrame(sm)`.
8

9
## Fields
10
- `problem::ProjectorMonteCarloProblem`: The problem that was solved
11
- `state::Rimu.ReplicaState`: The current state of the simulation
12
- `report::Rimu.Report`: The report of the simulation
13
- `modified::Bool`: Whether the simulation has been modified
14
- `aborted::Bool`: Whether the simulation has been aborted
15
- `success::Bool`: Whether the simulation has been completed successfully
16
- `message::String`: A message about the simulation status
17
- `elapsed_time::Float64`: The time elapsed during the simulation
18

19
See also [`state_vectors`](@ref),
20
[`ProjectorMonteCarloProblem`](@ref), [`init`](@ref), [`solve!`](@ref).
21
"""
22
mutable struct PMCSimulation
23
    problem::ProjectorMonteCarloProblem
158✔
24
    state::ReplicaState
25
    report::Report
26
    modified::Bool
27
    aborted::Bool
28
    success::Bool
29
    message::String
30
    elapsed_time::Float64
31
end
32

33
function _set_up_starting_vectors(
152✔
34
    ham, start_at, n_replicas, n_spectral, style, initiator, threading, copy_vectors, minimum_size
35
)
36
    if start_at isa AbstractMatrix && size(start_at) == (n_replicas, n_spectral)
153✔
37
        if eltype(start_at) <: AbstractDVec # already dvecs
1✔
38
            if copy_vectors
1✔
39
                return SMatrix{n_replicas, n_spectral}(deepcopy(v) for v in start_at)
×
40
            else
41
                return SMatrix{n_replicas, n_spectral}(v for v in start_at)
1✔
42
            end
43
        else
44
            return SMatrix{n_replicas, n_spectral}(
×
45
                default_starting_vector(sa; style, initiator, threading) for sa in start_at
46
            )
47
        end
48
    elseif n_spectral > 1
151✔
49
        basis = build_basis(ham, start_at; minimum_size)
3✔
50
        trunc = BasisSetRepresentation(ham, basis; filter=Returns(false), sizelim=Inf)
3✔
51
        vecs = eigvecs(Matrix(trunc))
6✔
52
        if threading
3✔
53
            eigs = [PDVec(zip(basis, 10*vecs[:,i]); style, initiator) for i in 1:n_spectral]
×
54
        else
55
            if initiator == NonInitiator()
3✔
56
                eigs = [DVec(zip(basis, 10*vecs[:,i]); style) for i in 1:n_spectral]
3✔
57
            else
58
                eigs = [InitiatorDVec(zip(basis,10*vecs[:,i]);style,initiator) for i in 1:n_spectral]
×
59
            end
60
        end
61
        mat = stack([eigs for _ in 1:n_replicas], dims=1)
3✔
62
        return SMatrix{n_replicas, n_spectral}(mat)
3✔
63
    end
64
    if start_at isa AbstractVector && length(start_at) == n_replicas
148✔
65
        if eltype(start_at) <: AbstractDVec # already dvecs
3✔
66
            if copy_vectors
2✔
67
                return SMatrix{n_replicas, n_spectral}(deepcopy(v) for v in start_at)
1✔
68
            else
69
                return SMatrix{n_replicas, n_spectral}(v for v in start_at)
1✔
70
            end
71
        else
72
            return SMatrix{n_replicas, n_spectral}(
1✔
73
                default_starting_vector(sa; style, initiator, threading) for sa in start_at
74
            )
75
        end
76
    elseif start_at isa AbstractDVec
145✔
77
        if n_replicas == 1 && !copy_vectors
122✔
78
            return SMatrix{n_replicas, n_spectral}(start_at)
62✔
79
        else
80
            return SMatrix{n_replicas, n_spectral}(deepcopy(start_at) for _ in 1:n_replicas)
60✔
81
        end
82
    end
83
    return SMatrix{n_replicas, n_spectral}(
23✔
84
        default_starting_vector(start_at; style, initiator, threading) for _ in 1:n_replicas
85
    )
86
end
87

88
function PMCSimulation(problem::ProjectorMonteCarloProblem; copy_vectors=true)
304✔
89
    # @unpack algorithm, hamiltonian, starting_vectors, style, threading, simulation_plan,
90
    @unpack algorithm, hamiltonian, start_at, style, threading, simulation_plan,
152✔
91
        replica_strategy, initial_shift_parameters,
92
        reporting_strategy, post_step_strategy,
93
        max_length, metadata, initiator, random_seed, spectral_strategy, minimum_size = problem
94

95
    reporting_strategy = refine_reporting_strategy(reporting_strategy)
152✔
96

97
    n_replicas = num_replicas(replica_strategy)
152✔
98
    n_spectral = num_spectral_states(spectral_strategy)
152✔
99

100
    # seed the random number generator
101
    if !isnothing(random_seed)
211✔
102
        Random.seed!(random_seed + hash(mpi_rank()))
59✔
103
    end
104

105
    start_at = isnothing(start_at) ? starting_address(hamiltonian) : start_at
152✔
106
    vectors = _set_up_starting_vectors(
152✔
107
        hamiltonian, start_at, n_replicas, n_spectral, style, initiator,
108
        threading, copy_vectors, minimum_size
109
    )
110
    @assert vectors isa SMatrix{n_replicas, n_spectral}
152✔
111

112
    # set up initial_shift_parameters
113
    shift, time_step = nothing, nothing
152✔
114

115
    shift_parameters = if initial_shift_parameters isa Union{NamedTuple, DefaultShiftParameters}
152✔
116
        # we have previously stored shift and time_step
117
        @unpack shift, time_step = initial_shift_parameters
153✔
118
        set_up_initial_shift_parameters(algorithm, hamiltonian, vectors, shift, time_step)
152✔
119
    else
120
        SMatrix{n_replicas, n_spectral}(initial_shift_parameters...)
152✔
121
    end
122
    @assert shift_parameters isa SMatrix{n_replicas, n_spectral}
152✔
123

124
    # set up the spectral_states
125
    wm = working_memory(vectors[1, 1])
152✔
126
    spectral_states = ntuple(n_replicas) do i
152✔
127
        replica_id = if n_replicas == 1 && n_spectral == 1
290✔
128
            ""
99✔
129
        else
130
            "_r$(i)"
481✔
131
        end
132
        SpectralState(
290✔
133
            ntuple(n_spectral) do j
134
                v = vectors[i, j]
300✔
135
                sp = shift_parameters[i, j]
300✔
136
                spectral_id = if n_replicas == 1 && n_spectral == 1
300✔
137
                    ""
99✔
138
                else
139
                    "s$(j)" # j is the spectral state index, i is the replica index
501✔
140
                end
141
                SingleState(
300✔
142
                    hamiltonian, algorithm, v, zerovector(v),
143
                    wm isa PDWorkingMemory ? wm : working_memory(v), # reuse for PDVec
144
                    sp, replica_id * spectral_id
145
                )
146
            end, spectral_strategy, replica_id)
147
    end
148
    @assert spectral_states isa NTuple{n_replicas, <:SpectralState}
152✔
149

150
    # set up the initial state
151
    state = ReplicaState(
152✔
152
        spectral_states,
153
        Ref(max_length),
154
        Ref(simulation_plan.starting_step),
155
        simulation_plan,
156
        reporting_strategy,
157
        post_step_strategy,
158
        replica_strategy
159
    )
160
    report = Report()
152✔
161
    report_default_metadata!(report, state)
152✔
162
    report_metadata!(report, metadata) # add user metadata
152✔
163
    # Sanity checks.
164
    check_transform(state.replica_strategy, hamiltonian)
152✔
165

166
    @assert allequal(i->state[i].algorithm, n_replicas * n_spectral) &&
152✔
167
        first(state).algorithm == algorithm
168
    @assert allequal(i -> state[i].hamiltonian, n_replicas * n_spectral) &&
152✔
169
        first(state).hamiltonian == hamiltonian
170

171
    return PMCSimulation(
152✔
172
        problem, state, report, false, false, false, "", 0.0
173
    )
174
end
175

176
function Base.show(io::IO, sm::PMCSimulation)
1✔
177
    print(io, "PMCSimulation")
1✔
178
    st = sm.state
1✔
179
    print(io, " with ", num_replicas(st), " replica(s) and ")
1✔
180
    print(io, num_spectral_states(st), " spectral state(s).")
1✔
181
    print(io, "\n  Algorithm:   ", sm.algorithm)
1✔
182
    print(io, "\n  Hamiltonian: ", sm.hamiltonian)
1✔
183
    print(io, "\n  Step:        ", st.step[], " / ", st.simulation_plan.last_step)
1✔
184
    print(io, "\n  modified = $(sm.modified), aborted = $(sm.aborted), success = $(sm.success)")
1✔
185
    sm.message == "" || print(io, "\n  message: ", sm.message)
1✔
186
end
187

188
num_spectral_states(sm::PMCSimulation) = num_spectral_states(sm.state)
4✔
189
num_replicas(sm::PMCSimulation) = num_replicas(sm.state)
6✔
190
num_overlaps(sm::PMCSimulation) = num_overlaps(sm.state)
3✔
191

192
function report_simulation_status_metadata!(report::Report, sm::PMCSimulation)
136✔
193
    @unpack modified, aborted, success, message, elapsed_time = sm
136✔
194

195
    report_metadata!(report, "modified", modified)
136✔
196
    report_metadata!(report, "aborted", aborted)
136✔
197
    report_metadata!(report, "success", success)
136✔
198
    report_metadata!(report, "message", message)
136✔
199
    report_metadata!(report, "elapsed_time", elapsed_time)
136✔
200
    return report
136✔
201
end
202

203
# iteration for backward compatibility
204
# undocumented; may be removed in the future
205
function Base.iterate(sm::PMCSimulation, state=1)
3✔
206
    if state == 1
3✔
207
        return DataFrame(sm), 2
1✔
208
    elseif state == 2
1✔
209
        return sm.state, 3
1✔
210
    else
211
        return nothing
×
212
    end
213
end
214
# getproperty access to :df, :algorithm and :hamiltonian fields
215
# undocumented; may be removed in the future
216
function Base.getproperty(sm::PMCSimulation, key::Symbol)
1,609,832✔
217
    if key == :df
1,609,832✔
218
        return DataFrame(sm)
11✔
219
    elseif key == :algorithm
1,609,821✔
220
        return first(sm.state).algorithm
200,960✔
221
    elseif key == :hamiltonian
1,408,861✔
222
        return first(sm.state).hamiltonian
2✔
223
    else
224
        return getfield(sm, key)
1,408,859✔
225
    end
226
end
227

228
# Tables.jl integration: provide access to report.data
229
# Note that using the `Tables` interface will not include metadata in the output.
230
# To include metadata, use the `DataFrame` constructor.
231
Tables.istable(::Type{<:PMCSimulation}) = true
×
232
Tables.columnaccess(::Type{<:PMCSimulation}) = true
×
233
Tables.columns(sm::PMCSimulation) = Tables.columns(sm.report.data)
1✔
234
Tables.schema(sm::PMCSimulation) = Tables.schema(sm.report.data)
1✔
235

236
state_vectors(sim::PMCSimulation) = state_vectors(sim.state)
39✔
237

238
# TODO: interface for reading results
239

240
DataFrames.DataFrame(s::PMCSimulation) = DataFrame(s.report)
138✔
241

242
"""
243
    init(problem::ProjectorMonteCarloProblem; copy_vectors=true)::PMCSimulation
244

245
Initialise a [`Rimu.PMCSimulation`](@ref).
246

247
See also [`ProjectorMonteCarloProblem`](@ref), [`solve!`](@ref), [`solve`](@ref),
248
[`step!`](@ref), [`Rimu.PMCSimulation`](@ref).
249
"""
250
function CommonSolve.init(problem::ProjectorMonteCarloProblem; copy_vectors=true)
304✔
251
    return PMCSimulation(problem; copy_vectors)
152✔
252
end
253

254
"""
255
    step!(sm::PMCSimulation)::PMCSimulation
256

257
Advance the simulation by one step.
258

259
Calling [`solve!`](@ref) will advance the simulation until the last step or the wall time is
260
exceeded. When completing the simulation without calling [`solve!`](@ref), the simulation
261
report needs to be finalised by calling [`Rimu.finalize_report!`](@ref).
262

263
See also [`ProjectorMonteCarloProblem`](@ref), [`init`](@ref), [`solve!`](@ref), [`solve`](@ref),
264
[`Rimu.PMCSimulation`](@ref).
265
"""
266
function CommonSolve.step!(sm::PMCSimulation)
200,959✔
267
    @unpack state, report, algorithm = sm
200,959✔
268
    @unpack spectral_states, simulation_plan, step, reporting_strategy,
200,959✔
269
        replica_strategy = state
270

271
    if sm.aborted || sm.success
401,917✔
272
        @warn "Simulation is already aborted or finished."
2✔
273
        return sm
2✔
274
    end
275
    if step[] >= simulation_plan.last_step
200,957✔
276
        @warn "Simulation has already reached the last step."
×
277
        return sm
×
278
    end
279

280
    step[] += 1
200,957✔
281

282
    # report step number
283
    if step[] % reporting_interval(reporting_strategy) == 0
201,657✔
284
        report!(reporting_strategy, step[], report, :step, step[])
200,707✔
285
    end
286

287
    proceed = true
200,957✔
288
    # advance all spectral_states
289
    for replica in spectral_states
200,957✔
290
        proceed &= advance!(algorithm, report, state, replica)
298,875✔
291
    end
499,832✔
292
    sm.modified = true
200,957✔
293

294
    # report replica stats
295
    if step[] % reporting_interval(reporting_strategy) == 0
201,657✔
296
        replica_names, replica_values = replica_stats(replica_strategy, spectral_states)
200,707✔
297
        report!(reporting_strategy, step[], report, replica_names, replica_values)
200,707✔
298
        report_after_step!(reporting_strategy, step[], report, state)
200,707✔
299
        ensure_correct_lengths(report)
200,707✔
300
    end
301

302
    if !proceed
200,956✔
303
        sm.aborted = true
8✔
304
        sm.message = "Aborted in step $(step[])."
8✔
305
        return sm
8✔
306
    end
307
    if step[] == simulation_plan.last_step
200,948✔
308
        sm.success = true
127✔
309
    end
310
    return sm
200,948✔
311
end
312

313
"""
314
    solve(::ProjectorMonteCarloProblem)::PMCSimulation
315

316
Initialize and solve a [`ProjectorMonteCarloProblem`](@ref) until the last step is completed
317
or the wall time limit is reached.
318

319
See also [`init`](@ref), [`solve!`](@ref), [`step!`](@ref), [`Rimu.PMCSimulation`](@ref),
320
and [`solve(::ExactDiagonalizationProblem)`](@ref).
321
"""
322
CommonSolve.solve
323

324
"""
325
    solve!(sm::PMCSimulation; kwargs...)::PMCSimulation
326

327
Solve a [`Rimu.PMCSimulation`](@ref) until the last step is completed or the wall time limit
328
is reached.
329

330
To continue a previously completed simulation, set a new `last_step` or `wall_time` using
331
the keyword arguments. Optionally, changes can be made to the `replica_strategy`, the
332
`post_step_strategy`, or the `reporting_strategy`.
333

334
# Optional keyword arguments:
335
* `last_step = nothing`: Set the last step to a new value and continue the simulation.
336
* `wall_time = nothing`: Set the allowed wall time to a new value and continue the
337
    simulation.
338
* `reset_time = false`: Reset the `elapsed_time` counter and continue the simulation.
339
* `empty_report = false`: Empty the report before continuing the simulation.
340
* `replica_strategy = nothing`: Change the replica strategy. Requires the number of replicas
341
    to match the number of replicas in the simulation `sm`. Implies `empty_report = true`.
342
* `post_step_strategy = nothing`: Change the post-step strategy. Implies
343
    `empty_report = true`.
344
* `reporting_strategy = nothing`: Change the reporting strategy. Implies
345
    `empty_report = true`.
346
* `metadata = nothing`: Add metadata to the report.
347

348
See also [`ProjectorMonteCarloProblem`](@ref), [`init`](@ref), [`solve`](@ref),
349
[`step!`](@ref), [`Rimu.PMCSimulation`](@ref).
350
"""
351
function CommonSolve.solve!(sm::PMCSimulation;
280✔
352
    last_step = nothing,
353
    wall_time = nothing,
354
    reset_time = false,
355
    replica_strategy=nothing,
356
    post_step_strategy=nothing,
357
    reporting_strategy=nothing,
358
    empty_report=false,
359
    metadata=nothing,
360
    display_name=nothing,
361
)
362
    reset_flags = reset_time # reset flags if resetting time
140✔
363
    if !isnothing(last_step)
140✔
364
        state = sm.state
4✔
365
        sm.state = @set state.simulation_plan.last_step = last_step
4✔
366
        report_metadata!(sm.report, "laststep", last_step)
4✔
367
        reset_flags = true
4✔
368
    end
369
    if !isnothing(wall_time)
140✔
370
        state = sm.state
1✔
371
        sm.state = @set state.simulation_plan.wall_time = wall_time
1✔
372
        reset_flags = true
1✔
373
    end
374
    if !isnothing(replica_strategy)
140✔
375
        if num_replicas(sm) ≠ num_replicas(replica_strategy)
2✔
376
            throw(ArgumentError("Number of replicas in the strategy must match the number of replicas in the simulation."))
1✔
377
        end
378
        state = sm.state
1✔
379
        sm.state = @set state.replica_strategy = replica_strategy
1✔
380
        reset_flags = true
1✔
381
        empty_report = true
1✔
382
    end
383
    if !isnothing(post_step_strategy)
139✔
384
        # set up post_step_strategy as a tuple
385
        if post_step_strategy isa PostStepStrategy
1✔
UNCOV
386
            post_step_strategy = (post_step_strategy,)
×
387
        end
388
        state = sm.state
1✔
389
        sm.state = @set state.post_step_strategy = post_step_strategy
1✔
390
        reset_flags = true
1✔
391
        empty_report = true
1✔
392
    end
393
    if !isnothing(reporting_strategy)
139✔
394
        state = sm.state
1✔
395
        sm.state = @set state.reporting_strategy = reporting_strategy
1✔
396
        reset_flags = true
1✔
397
    end
398

399
    @unpack report = sm
139✔
400
    if empty_report
139✔
401
        empty!(report)
2✔
402
        report_default_metadata!(report, sm.state)
2✔
403
    end
404
    isnothing(metadata) || report_metadata!(report, metadata) # add user metadata
139✔
405
    isnothing(display_name) || report_metadata!(report, "display_name", display_name)
139✔
406

407
    @unpack simulation_plan, step, reporting_strategy = sm.state
139✔
408

409
    last_step = simulation_plan.last_step
139✔
410
    initial_step = step[]
139✔
411

412
    if step[] >= last_step
139✔
413
        @warn "Simulation has already reached the last step."
2✔
414
        return sm
2✔
415
    end
416

417
    if reset_flags # reset the flags
137✔
418
        sm.aborted = false
5✔
419
        sm.success = false
5✔
420
        sm.message = ""
5✔
421
    end
422
    if reset_time # reset the elapsed time
137✔
423
        sm.elapsed_time = 0.0
×
424
    end
425

426
    if sm.aborted || sm.success
274✔
427
        @warn "Simulation is already aborted or finished."
×
428
        return sm
×
429
    end
430
    un_finalize!(report)
137✔
431

432
    starting_time = time() + sm.elapsed_time # simulation time accumulates
137✔
433
    update_steps = max((last_step - initial_step) ÷ 200, 100) # log often but not too often
137✔
434
    name = get_metadata(sm.report, "display_name")
137✔
435

436
    @withprogress name = while !sm.aborted && !sm.success
137✔
437
        if time() - starting_time > simulation_plan.wall_time
200,957✔
438
            sm.aborted = true
1✔
439
            sm.message = "Wall time limit reached."
1✔
440
            @warn "Wall time limit reached. Aborting simulation."
1✔
441
        else
442
            step!(sm)
200,956✔
443
        end
444
        if step[] % update_steps == 0 # for updating progress bars
200,956✔
445
            @logprogress (step[] - initial_step) / (last_step - initial_step)
2,007✔
446
        end
447

448
    end
×
449
    sm.elapsed_time = time() - starting_time
136✔
450
    report_simulation_status_metadata!(report, sm) # potentially overwrite values
136✔
451
    finalize_report!(reporting_strategy, report)
136✔
452
    return sm
136✔
453
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