You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CHANGELOG.md
+5Lines changed: 5 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -13,6 +13,9 @@ Also see the [GitHub Releases](https://github.com/JuliaQuantumControl/QuantumPro
13
13
14
14
## [Unreleased]
15
15
16
+
* Added: `QuantumPropagators.Interfaces.check_storage`, which verifies that a storage implementation fulfills the storage contract [[#119], [#120]]
17
+
* Fixed: `write_to_storage!` now stores a copy of any mutable data, so that the storage owns its data. Previously, storing the state of an in-place propagation at every time step could leave all slots aliasing a single buffer [[#119], [#120]]
18
+
16
19
## [v0.9.0] — 2026-06-15
17
20
18
21
* Added: `ExponentialUtilitiesPropagator`, a propagator based on `expv` from [ExponentialUtilities.jl](https://github.com/SciML/ExponentialUtilities.jl), with Krylov-subspace caching for in-place propagation [[#97]]
Copy file name to clipboardExpand all lines: docs/src/storage.md
+18-5Lines changed: 18 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -22,12 +22,12 @@ After each propagation step, with a propagated state at time slot `i`,
22
22
1.[`data = QuantumPropagators.Storage.map_observables(observables, tlist, i, state)`](@ref QuantumPropagators.Storage.map_observables) generates `data` from the propagated state
23
23
2.[`QuantumPropagators.Storage.write_to_storage!(storage, i, data)`](@ref QuantumPropagators.Storage.write_to_storage!) places that `data` into `storage` for time slot `i`
24
24
25
-
After [`propagate`](@ref) returns, the [`QuantumPropagators.Storage.get_from_storage!`](@ref) routine can be used to extract data from any time slot. This interface hides the internal memory organization of `storage`, which is set up by [`init_storage`](@ref QuantumPropagators.Storage.init_storage) based on the type of `state` and the given `observables`. This system can be extended with multiple dispatch, allowing to optimize the `storage` for custom data types. Obviously, [`init_storage`](@ref QuantumPropagators.Storage.init_storage), [`map_observables`](@ref QuantumPropagators.Storage.map_observables), [`write_to_storage!`](@ref QuantumPropagators.Storage.write_to_storage!), and [`get_from_storage!`](@ref QuantumPropagators.Storage.get_from_storage!) must all be consistent.
25
+
After [`propagate`](@ref) returns, the [`QuantumPropagators.Storage.get_from_storage!`](@ref) routine can be used to extract data from any time slot. This interface hides the internal memory organization of `storage`, which is set up by [`init_storage`](@ref QuantumPropagators.Storage.init_storage) based on the type of `state` and the given `observables`. This system can be extended with multiple dispatch, allowing to optimize the `storage` for custom data types. Obviously, [`init_storage`](@ref QuantumPropagators.Storage.init_storage), [`map_observables`](@ref QuantumPropagators.Storage.map_observables), [`write_to_storage!`](@ref QuantumPropagators.Storage.write_to_storage!), and [`get_from_storage!`](@ref QuantumPropagators.Storage.get_from_storage!) must all be consistent, see [the storage contract](@ref storage-contract).
26
26
27
27
The default implementation of these routine uses either a standard Vector or a Matrix as `storage`.
28
28
29
-
Roughly speaking, when storing states, if the state of some arbitrary type,
30
-
the storage will be a Vector where the i'th entry points to a copy of the propagated state at the i'th time slot. If the state is a Vector, the storage will be a Matrix containing the state for the i'th time slot in the i'th column.
29
+
Roughly speaking, when storing states, if the state is of some arbitrary type,
30
+
the storage will be a Vector where the i'th entry holds a copy of the propagated state at the i'th time slot. If the state is a Vector, the storage will be a Matrix containing the state for the i'th time slot in the i'th column.
31
31
32
32
When a tuple of `observables` is passed to [`propagate`](@ref), if [`map_observables`](@ref QuantumPropagators.Storage.map_observables) returns data of the same type for each observable, `storage` will be a Matrix containing the values from the different observables for the i'th time slot in the i'th column. This would be typical for the storage of expectation values, e.g. with
where `count_poplevels` is a function that counts the number of levels with
63
63
non-zero population, the resulting `storage` would be a `Vector{Tuple{Float64, Int64}}`.
64
64
65
-
If there is a single observable that yields a vector, that vector is stored in the i'th column of a `storage` matrix. This is in fact what happens when storing the propagated states (`observables=(Ψ->copy(Ψ), )`) if `Ψ` is a Vector, but there are other use cases, such as calculating the population in all levels in one go, with
65
+
If there is a single observable that yields a vector, that vector is stored in the i'th column of a `storage` matrix. This is in fact what happens when storing the propagated states (`observables=(Ψ->Ψ, )`) if `Ψ` is a Vector, but there are other use cases, such as calculating the population in all levels in one go, with
66
66
`observables=(Ψ -> abs.(Ψ).^2, )`.
67
67
68
-
If there is a single variable that yields a non-vector object, `storage` will be a Vector where the i'th entry points to the object. This is in fact what happens by default when storing states that are e.g. instances of
68
+
If there is a single observable that yields a non-vector object, `storage` will be a Vector where the i'th entry holds a copy of the object. This is in fact what happens by default when storing states that are e.g. instances of
69
69
`QuantumOptics.Ket`. In such a case, it might be advisable to add new methods for [`QuantumPropagators.Storage.init_storage`](@ref) and [`QuantumPropagators.Storage.write_to_storage!`](@ref) that implement a more efficient in-place storage.
70
+
71
+
72
+
## [The storage contract](@id storage-contract)
73
+
74
+
A `storage` object is created by [`init_storage`](@ref QuantumPropagators.Storage.init_storage), written to by [`write_to_storage!`](@ref QuantumPropagators.Storage.write_to_storage!), and read back by [`get_from_storage`](@ref QuantumPropagators.Storage.get_from_storage) or [`get_from_storage!`](@ref QuantumPropagators.Storage.get_from_storage!). Together, these four functions guarantee the following, for any `storage = init_storage(data, nt)` and any slot `i` in `1:nt`:
75
+
76
+
1. What you put in is what you get out. After `write_to_storage!(storage, i, data)`, `get_from_storage(storage, i)` reproduces the value that `data` had at the time of the write, and so does `get_from_storage!(buffer, storage, i)` for data that supports in-place extraction.
77
+
78
+
2. The storage owns its data. Modifying `data` after the write does not change what is stored, and writing to one slot does not change any other slot. This holds even when the caller passes the same object on every write, which is the normal situation when storing the states of an in-place propagation: [`prop_step!`](@ref) returns the same buffer at every time step.
79
+
80
+
3. The result of [`get_from_storage`](@ref QuantumPropagators.Storage.get_from_storage) is read-only. It may alias the internals of `storage`, so mutating it may corrupt the storage. Use [`get_from_storage!`](@ref QuantumPropagators.Storage.get_from_storage!) to obtain data the caller may modify.
81
+
82
+
Custom [`init_storage`](@ref QuantumPropagators.Storage.init_storage), [`write_to_storage!`](@ref QuantumPropagators.Storage.write_to_storage!), and [`get_from_storage!`](@ref QuantumPropagators.Storage.get_from_storage!) methods must be mutually consistent, and must uphold the guarantees above. Use [`check_storage`](@ref QuantumPropagators.Interfaces.check_storage) to verify them.
0 commit comments