Skip to content

Commit

Permalink
Implement propagate_sequence
Browse files Browse the repository at this point in the history
  • Loading branch information
goerz committed Mar 21, 2024
1 parent 1946cd6 commit bf771c2
Show file tree
Hide file tree
Showing 8 changed files with 499 additions and 61 deletions.
12 changes: 10 additions & 2 deletions docs/generate_api.jl
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ end


PREAMBLE = raw"""
The highest-level API of the `QuantumPropagators.jl` package consists of a single function:
The highest-level API of the `QuantumPropagators.jl` package (apart from some [convenience functions](@ref "Convenience functions")) consists of a single function:
* [`propagate`](@ref) — Propagate a quantum state over an entire time grid under a given generator
Expand All @@ -97,7 +97,15 @@ The `QuantumPropagators` package re-exports the two main initialization routines
To set up the time-dependent control fields in a Hamiltonian, methods from the submodules [`QuantumPropagators.Controls`](@ref QuantumPropagatorsControlsAPI), [`QuantumPropagators.Shapes`](@ref QuantumPropagatorsShapesAPI), and [`QuantumPropagators.Amplitudes`](@ref QuantumPropagatorsAmplitudesAPI) can be used.
The above constitutes the main interface of `QuantumPropagators`. At the lowest level, further functionality is provided by sub-modules like [`QuantumPropagators.Cheby`](@ref QuantumPropagatorsChebyAPI), which defines a standalone API specifically for the Chebychev propagation method.
""" * summarize_submodules(QuantumPropagators)
""" * summarize_submodules(QuantumPropagators) * """
### Convenience functions
There are some "convenience functions" that wrap around [`propagate`](@ref) for common tasks:
* [`propagate_sequence`](@ref)
"""


function write_module_api(out, mod, description; reference_title="Reference")
Expand Down
30 changes: 16 additions & 14 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,21 @@ using QuantumPropagators
using QuantumPropagators: AbstractPropagator, set_t!, set_state!
using Documenter
using Pkg
using OrdinaryDiffEq # ensure ODE extension is loaded
import OrdinaryDiffEq # ensure ODE extension is loaded
using DocumenterCitations
using DocumenterInterLinks

DocMeta.setdocmeta!(
QuantumPropagators,
:DocTestSetup,
:(using QuantumPropagators);
recursive=true
)

PROJECT_TOML = Pkg.TOML.parsefile(joinpath(@__DIR__, "..", "Project.toml"))
VERSION = PROJECT_TOML["version"]
NAME = PROJECT_TOML["name"]
AUTHORS = join(PROJECT_TOML["authors"], ", ") * " and contributors"
GITHUB = "https://github.com/JuliaQuantumControl/QuantumPropagators.jl"

DEV_OR_STABLE = "stable/"
if endswith(VERSION, "dev")
DEV_OR_STABLE = "dev/"
end

links = InterLinks(
"TimerOutputs" => (
Expand All @@ -21,14 +26,11 @@ links = InterLinks(
# We'll use `@extref` for links from docstrings to sections so that the
# docstrings can also be rendered as part of the QuantumControl
# documentation.
"QuantumPropagators" => "https://juliaquantumcontrol.github.io/QuantumPropagators.jl/dev/"
"QuantumPropagators" => "https://juliaquantumcontrol.github.io/QuantumPropagators.jl/$DEV_OR_STABLE",
"QuantumControlBase" => "https://juliaquantumcontrol.github.io/QuantumControlBase.jl/$DEV_OR_STABLE",
)

PROJECT_TOML = Pkg.TOML.parsefile(joinpath(@__DIR__, "..", "Project.toml"))
VERSION = PROJECT_TOML["version"]
NAME = PROJECT_TOML["name"]
AUTHORS = join(PROJECT_TOML["authors"], ", ") * " and contributors"
GITHUB = "https://github.com/JuliaQuantumControl/QuantumPropagators.jl"
externals = ExternalFallbacks("Trajectory" => "@extref QuantumControlBase.Trajectory")

println("Starting makedocs")

Expand All @@ -43,7 +45,7 @@ end


makedocs(;
plugins=[bib, links],
plugins=[bib, links, externals],
authors=AUTHORS,
sitename="QuantumPropagators.jl",
# Link checking is disabled in REPL, see `devrepl.jl`.
Expand Down
88 changes: 44 additions & 44 deletions docs/src/inventories/TimerOutputs.toml
Original file line number Diff line number Diff line change
@@ -1,84 +1,84 @@
[Inventory]
format = "DocInventories v0"
# DocInventory version 1
project = "TimerOutputs.jl"
version = "0.5.23"

[[jl.function]]
name = "TimerOutputs.print_timer"
uri = "#settings-for-printing"
[[jl.function]]
name = "TimerOutputs.reset_timer!"
uri = "#resetting"

[[jl.type]]
name = "TimerOutputs.TimerOutput"
uri = "#usage"

[[std.doc]]
dispname = "TimerOutputs"
name = "readme"
uri = ""
dispname = "TimerOutputs"

[[std.label]]
name = "timeroutputs"
dispname = "Acknowledgments"
name = "acknowledgments"
uri = "#$"
dispname = "TimerOutputs"
[[std.label]]
name = "example-output"
dispname = "Autor"
name = "author"
uri = "#$"
dispname = "Example output"
[[std.label]]
name = "usage"
dispname = "Default Timer"
name = "default-timer"
uri = "#$"
dispname = "Usage"
[[std.label]]
name = "settings-for-printing"
dispname = "Example output"
name = "example-output"
uri = "#$"
dispname = "Settings for printing"
[[std.label]]
dispname = "Flattening"
name = "flattening"
uri = "#$"
dispname = "Flattening"
[[std.label]]
name = "merging"
dispname = "Indexing into a table"
name = "indexing-into-a-table"
uri = "#$"
dispname = "Merging"
[[std.label]]
name = "resetting"
dispname = "Measuring time consumed outside @timeit blocks"
name = "measuring-time-consumed-outside-timeit-blocks"
uri = "#$"
dispname = "Resetting"
[[std.label]]
name = "indexing-into-a-table"
dispname = "Merging"
name = "merging"
uri = "#$"
dispname = "Indexing into a table"
[[std.label]]
name = "querying-data"
dispname = "Overhead"
name = "overhead"
uri = "#$"
dispname = "Querying data"
[[std.label]]
name = "default-timer"
dispname = "Querying data"
name = "querying-data"
uri = "#$"
dispname = "Default Timer"
[[std.label]]
name = "measuring-time-consumed-outside-timeit-blocks"
dispname = "Resetting"
name = "resetting"
uri = "#$"
dispname = "Measuring time consumed outside @timeit blocks"
[[std.label]]
name = "shared-timers"
dispname = "Serialization"
name = "serialization"
uri = "#$"
dispname = "Shared Timers"
[[std.label]]
name = "serialization"
dispname = "Settings for printing"
name = "settings-for-printing"
uri = "#$"
dispname = "Serialization"
[[std.label]]
name = "overhead"
dispname = "Shared Timers"
name = "shared-timers"
uri = "#$"
dispname = "Overhead"
[[std.label]]
name = "author"
dispname = "TimerOutputs"
name = "timeroutputs"
uri = "#$"
dispname = "Autor"
[[std.label]]
name = "acknowledgments"
dispname = "Usage"
name = "usage"
uri = "#$"
dispname = "Acknowledgments"

[[jl.type]]
name = "TimerOutputs.TimerOutput"
uri = "#usage"
[[jl.function]]
name = "TimerOutputs.print_timer"
uri = "#settings-for-printing"
[[jl.function]]
name = "TimerOutputs.reset_timer!"
uri = "#resetting"
3 changes: 3 additions & 0 deletions src/QuantumPropagators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,7 @@ include("timings.jl")
include("./propagate.jl")
export propagate

include("./propagate_sequence.jl")
export Propagation, propagate_sequence

end
131 changes: 131 additions & 0 deletions src/propagate_sequence.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
"""Wrapper around the parameters of a call to [`propagate`](@ref).
```julia
Propagation(
generator, tlist;
pre_propagation=nothing, post_propagation=nothing,
kwargs...
)
Propagation(
propagator;
pre_propagation=nothing, post_propagation=nothing,
kwargs...
)
```
is a wrapper around the arguments for [`propagate`](@ref) /
[`init_prop`](@ref), for use within [`propagate_sequence`](@ref).
The positional and keyword arguments are those accepted by the above
propagation routines, excluding the initial state. A `Propagation` may in
addition include the `pre_propagation` and `post_propagation` keyword arguments
recognized by [`propagate_sequence`](@ref).
"""
struct Propagation
args::Vector{Any}
kwargs::Dict{Symbol,Any}
function Propagation(args...; kwargs...)
new(Any[args...], kwargs)
end
end


"""Propagate a state through a sequence of generators.
```
states = propagate_sequence(
state,
propagations;
storage=nothing,
pre_propagation=nothing,
post_propagation=nothing,
kwargs...
)
```
takes an `initial` state and performs a sequence of [`propagate`](@ref) calls
using the parameters in `propagations`. The initial state for each step in the
sequence is the state resulting from the previous step. Optionally, before and
after each step, a `pre_propagation` and `post_propagation` function may modify
the state instantaneously, e.g., to perform a frame transformation. Return the
vector of states at the end of each step (after any `post_propagation`, before
any next `pre_propagation` of the next step).
# Arguments
* `state`: The initial state
* `propagations`: A vector of [`Propagation`](@ref) instances, one per step in
the sequence, each containing the arguments for the call to
[`propagate`](@ref) for that step. The [`Propagation`](@ref) contains the
generator and time grid for each step as positional parameters, or
alternatively a pre-initialized [`Propagator`](@ref AbstractPropagator), and
any keyword arguments for [`propagate`](@ref) that are specific to that step.
Note that [`propagate`](@ref) keyword arguments that are common to all steps
can be given directly to `propagate_sequence`.
* `storage`: If `storage=true`, return a vector of storage objects as returned
by `propagate(…, storage=true)` for each propagation step, instead of the
state after each step. To use a pre-initialized `storage`, each
[`Propagation`](@ref) in `propagations` should have a `storage` keyword
argument instead.
* `pre_propagation`: If not `nothing`, must be a function that receives the
same arguments as [`propagate`](@ref) and returns a state. Called immediately
before the [`propagate`](@ref) of each step, and the state returned by
`pre_propagation` will become the initial state for the subsequent call to
[`propagate`](@ref). Generally, `pre_propagation` would be different in each
step of the sequence, and should be given as a keyword argument in a
particular [`Propagation`](@ref).
* `post_propagation`: If not `nothing`, a function that receives the same
arguments as [`propagate`](@ref) and returns a state, see `pre_propagation`.
The returned state becomes the initial state for the next step in the
sequence (and may be further processed by the following `pre_propagation`).
Like `pre_propagation`, this will generally be set as a keyword argument for
a particular [`Propagation`](@ref), not as a global keyword argument to
`propagate_sequence`.
All other keyword arguments are forwarded to `propagate`. Thus, keyword
arguments that are common to all steps in the sequence should be given as
keyword arguments to `propagate_sequence` directly.
"""
function propagate_sequence(
state,
propagations::Vector{Propagation};
pre_propagation=nothing,
post_propagation=nothing,
kwargs...,
)
Ψ = state
results = []
for prop in propagations
prop_kwargs = Dict(prop.kwargs...)
_pre_propagation = pop!(prop_kwargs, :pre_propagation, pre_propagation)
_post_propagation = pop!(prop_kwargs, :post_propagation, post_propagation)
merge!(prop_kwargs, kwargs)
@assert !haskey(prop_kwargs, :pre_propagate) # from prototype version
@assert !haskey(prop_kwargs, :post_propagate) # from prototype version
storage = get(prop_kwargs, :storage, nothing)
if storage === true
observables = get(prop_kwargs, :observables, _StoreState())
if prop.args[end] isa AbstractPropagator
tlist = prop.args[end].tlist

Check warning on line 110 in src/propagate_sequence.jl

View check run for this annotation

Codecov / codecov/patch

src/propagate_sequence.jl#L110

Added line #L110 was not covered by tests
else
tlist = prop.args[end]
end
prop_kwargs[:storage] =
QuantumPropagators.Storage.init_storage(Ψ, tlist, observables)
end
if !isnothing(_pre_propagation)
Ψ = _pre_propagation(Ψ, prop.args...; prop_kwargs...)
end
Ψ = propagate(Ψ, prop.args...; prop_kwargs...)
if !isnothing(_post_propagation)
Ψ = _post_propagation(Ψ, prop.args...; prop_kwargs...)
end
if storage === true
push!(results, prop_kwargs[:storage])
else
push!(results, copy(Ψ))
end
end
return results
end
3 changes: 2 additions & 1 deletion test/init.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ include(joinpath(@__DIR__, "clean.jl"))

function servedocs(; kwargs...)
clean() # otherwise, we get an infinite loop
ENV["DOCUMENTER_WARN_ONLY"] = "1"
_servedocs(;
skip_dirs=[joinpath("docs", "src", "api"), joinpath("docs", "src", "examples")],
kwargs...
Expand Down Expand Up @@ -42,3 +41,5 @@ Revise, JuliaFormatter, LiveServer, Plots with unicode backend are active.

"""Show help"""
help() = println(REPL_MESSAGE)

ENV["DOCUMENTER_WARN_ONLY"] = "1"
5 changes: 5 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ using SafeTestsets
include("test_propagate.jl")
end

println("\n* Propagate Sequence (test_propagate_sequence.jl):")
@time @safetestset "Propagate Sequence" begin
include("test_propagate_sequence.jl")
end

println("\n* Time-dependent observables (test_timedependent_observables.jl):")
@time @safetestset "Time-dependent observables" begin
include("test_timedependent_observables.jl")
Expand Down
Loading

0 comments on commit bf771c2

Please sign in to comment.