Skip to content

Commit 74d833a

Browse files
committed
Use trapezoidal rule to evaluate J_b
1 parent 9bdff36 commit 74d833a

2 files changed

Lines changed: 32 additions & 31 deletions

File tree

src/functionals.jl

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1101,37 +1101,50 @@ J_b_val = J_b(storage, trajectories, tlist; g_b)
11011101
returns
11021102
11031103
```math
1104-
J_b = ∑_k ∑_n g_b(|Ψ_k(t_n)⟩) Δt_n
1104+
J_b = ∑_k ∑_{n=0}^{N_T}{}^′ g_b(|Ψ_k(t_n)⟩) Δt_n
11051105
```
11061106
1107-
as the piecewise-constant discretization of
1107+
as the [trapezoid discretization](https://en.wikipedia.org/wiki/Trapezoidal_rule)
1108+
of the integral
11081109
11091110
```math
1110-
J_b = ∑_k \\int g_b(|Ψ_k(t)⟩) dt
1111+
J_b = ∑_k ∫_0^T g_b(|Ψ_k(t)⟩) dt ,
11111112
```
11121113
11131114
where `storage[k]` contains the forward-propagated states for trajectory `k`
1114-
at each point of `tlist`; for example, the `fw_storage` component of the
1115-
[GRAPE workspace](@extref `GRAPE.GrapeWrk`). The ``Δt_n`` is the
1116-
[time step around the time grid point ``t_n``](@extref GRAPE Overview-Running-Costs).
1115+
at each time grid point in `tlist`, ``t_0 = 0, t_1, … t_{N_T} = T``;
1116+
for example, the `fw_storage` component of the
1117+
[GRAPE workspace](@extref `GRAPE.GrapeWrk`). The ``Δt_n`` are defined at
1118+
the endpoints as ``Δt_0 = t_1 - t_0`` and ``Δt_{N_T} = (t_{N_T} - t_{N_T-1})``,
1119+
and at the interior points as ``Δt_n = \\frac{(t_{n+1} - t_{n-1})}{2}``.
1120+
For uniform time grids, ``Δt_n ≡ dt``. The prime in the sum over ``n``
1121+
indicates that the terms at the endpoints ``n=0`` and ``n=N_T`` are halved.
11171122
11181123
Note that `g_b` is a mandatory keyword argument and must be a function
1119-
`g_b(Ψ, trajectory, tlist, n) -> Float64`.
1124+
`g_b(Ψ, trajectory, tlist, n) -> Float64` where `Ψ` is the state ``|Ψ(t)⟩``
1125+
at ``t`` corresponding to the the (one-based) time grid point `tlist[n]`, and
1126+
`trajectory` is a [`QuantumControl.Trajectory`](@ref) that may hold additional
1127+
data in a custom property that is relevant to the calculation of ``g_b``.
1128+
1129+
The definition of `J_b` here is compatible with the
1130+
[treatment of running costs in GRAPE](@extref GRAPE Overview-Running-Costs).
11201131
"""
11211132
function J_b(storage, trajectories, tlist; g_b)
11221133
N = length(trajectories)
11231134
result = 0.0
11241135
for k = 1:N
11251136
Ψ₁ = get_from_storage(storage[k], 1)
1126-
result += g_b(Ψ₁, trajectories[k], tlist, 1) * (tlist[2] - tlist[1])
1137+
dt = tlist[2] - tlist[1]
1138+
result += g_b(Ψ₁, trajectories[k], tlist, 1) * (dt/2)
11271139
for n_tl = 2:length(tlist)
11281140
Ψₙ = get_from_storage(storage[k], n_tl)
11291141
if n_tl < length(tlist)
11301142
dt = 0.5 * (tlist[n_tl+1] - tlist[n_tl-1])
1143+
result += g_b(Ψₙ, trajectories[k], tlist, n_tl) * dt
11311144
else
11321145
dt = tlist[end] - tlist[end-1]
1146+
result += g_b(Ψₙ, trajectories[k], tlist, n_tl) * (dt/2)
11331147
end
1134-
result += g_b(Ψₙ, trajectories[k], tlist, n_tl) * dt
11351148
end
11361149
end
11371150
return result

test/test_functionals.jl

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -771,32 +771,27 @@ end
771771
@testset "J_b" begin
772772

773773
# Test that J_b correctly integrates g_b over all trajectories and time.
774-
# Uses simple storage (vector of vectors) and a constant g_b = 1 whose
775-
# integral is fully determined by the time grid and number of trajectories.
774+
# Uses simple storage (vector of vectors) and a constant g_b = |Ψ| = 1
775+
# whose integral is fully determined by the time grid and number of
776+
# trajectories.
776777

777778
tlist = PROBLEM.tlist
779+
T = tlist[end]
778780
trajectories = PROBLEM.trajectories
781+
N = length(trajectories)
779782
N_tl = length(tlist)
780783

781784
# Build fake storage: each trajectory gets a vector of random states
782785
storage = [[random_state_vector(N_HILBERT; rng = RNG) for _ = 1:N_tl] for _ = 1:N]
783786

784787
function g_b_const(Ψ, traj, tlist, n)
785-
return 1.0
788+
return norm(Ψ)
786789
end
787790

788791
J_b_val = J_b(storage, trajectories, tlist; g_b = g_b_const)
789792

790-
# Manually reproduce J_b's integration weights for a constant g_b = 1
791-
expected = 0.0
792-
for _ = 1:N
793-
expected += tlist[2] - tlist[1]
794-
for n_tl = 2:(N_tl-1)
795-
expected += 0.5 * (tlist[n_tl+1] - tlist[n_tl-1])
796-
end
797-
expected += tlist[end] - tlist[end-1]
798-
end
799-
@test J_b_val expected
793+
# Trapezoidal rule for integral should return the exact result
794+
@test J_b_val T * N
800795

801796
# With g_b ∝ n, J_b should reflect the weighted sum over time indices
802797
function g_b_index(Ψ, traj, tlist, n)
@@ -805,14 +800,7 @@ end
805800

806801
J_b_idx = J_b(storage, trajectories, tlist; g_b = g_b_index)
807802

808-
expected_idx = 0.0
809-
for _ = 1:N
810-
expected_idx += 1.0 * (tlist[2] - tlist[1])
811-
for n_tl = 2:(N_tl-1)
812-
expected_idx += n_tl * 0.5 * (tlist[n_tl+1] - tlist[n_tl-1])
813-
end
814-
expected_idx += N_tl * (tlist[end] - tlist[end-1])
815-
end
816-
@test J_b_idx expected_idx
803+
# Again, the integral should be exact
804+
@test J_b_idx ((N_tl - 1) / 2 + 1) * T * N
817805

818806
end

0 commit comments

Comments
 (0)