Skip to content

Commit 493f59a

Browse files
committed
Merge #108 from branch J-a-fluence-non-uniform
2 parents 5d4e99f + 894696b commit 493f59a

2 files changed

Lines changed: 51 additions & 12 deletions

File tree

src/functionals.jl

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,12 @@ vector `∇J_a` containing the vectorized elements ``∂J_a/∂ϵ_{ln}``.
379379
The function `J_a` must have the interface `J_a(pulsevals, tlist)`,
380380
see, e.g., [`J_a_fluence`](@ref).
381381
382+
In `pulsevals`, the values ``ϵ_{nl}`` are vectorized with `n` (time interval
383+
index) varying faster than `l` (control index), i.e.,
384+
`pulsevals = [ϵ₁₁, ϵ₂₁, …, ϵ_{N_T,1}, ϵ₁₂, ϵ₂₂, …, ϵ_{N_T,2}, …]`,
385+
where ``N_T = `` `length(tlist) - 1`. The pulse values for each control are
386+
contiguous.
387+
382388
The parameters `mode` and `automatic` are handled as in [`make_chi`](@ref),
383389
where `mode` is one of `:any`, `:analytic`, `:automatic`, and `automatic` is
384390
he loaded module of an automatic differentiation framework, where `:default`
@@ -924,16 +930,24 @@ J_a = J_a_fluence(pulsevals, tlist)
924930
calculates
925931
926932
```math
927-
J_a = \sum_l \int_0^T |ϵ_l(t)|^2 dt = \left(\sum_{nl} |ϵ_{nl}|^2 \right) dt
933+
J_a = \sum_l \int_0^T |ϵ_l(t)|^2 dt \approx \sum_{nl} |ϵ_{nl}|^2 \, dt_n
928934
```
929935
930-
where ``ϵ_{nl}`` are the values in the (vectorized) `pulsevals`, `n` is the
931-
index of the intervals of the time grid, and ``dt`` is the time step, taken
932-
from the first time interval of `tlist` and assumed to be uniform.
936+
where ``ϵ_{nl}`` are the values in `pulsevals`, with `n`
937+
the index of the time interval and `l` the index of the control, and
938+
``dt_n = `` `tlist[n+1] - tlist[n]` is the duration of interval `n`.
939+
The `pulsevals` are vectorized as ``[ϵ₁₁, ϵ₂₁, …, ϵ_{N_T,1}, ϵ₁₂, ϵ₂₂, …]``,
940+
where `N_T = length(tlist) - 1`. Supports non-uniform time grids.
941+
942+
# See also
943+
944+
* [`grad_J_a_fluence`](@ref) — analytic (automatic) gradient
933945
"""
934946
function J_a_fluence(pulsevals, tlist)
935-
dt = tlist[begin+1] - tlist[begin]
936-
return sum(abs2.(pulsevals)) * dt
947+
N_T = length(tlist) - 1
948+
dt = reshape(diff(tlist), :, 1) # (N_T, 1) for column broadcasting
949+
pv = reshape(pulsevals, N_T, :) # (N_T, N_L), no copy
950+
return sum(abs2.(pv) .* dt)
937951
end
938952

939953

@@ -943,14 +957,16 @@ end
943957
∇J_a = grad_J_a_fluence(pulsevals, tlist)
944958
```
945959
946-
returns the `∇J_a`, which contains the (vectorized) elements ``2 ϵ_{nl} dt``,
947-
where ``ϵ_{nl}`` are the (vectorized) elements of `pulsevals` and ``dt`` is the
948-
time step, taken from the first time interval of `tlist` and assumed to be
949-
uniform.
960+
returns `∇J_a`, which contains the (vectorized) elements ``2 ϵ_{nl} dt_n``,
961+
where ``ϵ_{nl}`` are the (vectorized) elements of `pulsevals` and
962+
``dt_n = `` `tlist[n+1] - tlist[n]` is the duration of interval `n`.
963+
Supports non-uniform time grids.
950964
"""
951965
function grad_J_a_fluence(pulsevals, tlist)
952-
dt = tlist[begin+1] - tlist[begin]
953-
return (2 * dt) * pulsevals
966+
N_T = length(tlist) - 1
967+
dt = reshape(diff(tlist), :, 1) # (N_T, 1) for column broadcasting
968+
pv = reshape(pulsevals, N_T, :) # (N_T, N_L), no copy
969+
return vec(2 .* dt .* pv)
954970
end
955971

956972

test/test_functionals.jl

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,29 @@ end
125125
end
126126

127127

128+
@testset "J_a_fluence non-uniform grid" begin
129+
130+
# Non-uniform tlist with 4 intervals, 2 controls
131+
# pulsevals layout: [ϵ₁₁, ϵ₂₁, ϵ₃₁, ϵ₄₁, ϵ₁₂, ϵ₂₂, ϵ₃₂, ϵ₄₂]
132+
tlist_nu = [0.0, 0.1, 0.3, 0.6, 1.0]
133+
dt_nu = [0.1, 0.2, 0.3, 0.4]
134+
pv1 = [1.0, 2.0, 3.0, 4.0]
135+
pv2 = [0.5, 1.5, 2.5, 3.5]
136+
pulsevals_nu = vcat(pv1, pv2)
137+
138+
J_expected = sum(abs2.(pv1) .* dt_nu) + sum(abs2.(pv2) .* dt_nu)
139+
@test J_a_fluence(pulsevals_nu, tlist_nu) J_expected
140+
141+
G_expected = vcat(2 .* pv1 .* dt_nu, 2 .* pv2 .* dt_nu)
142+
@test grad_J_a_fluence(pulsevals_nu, tlist_nu) G_expected
143+
144+
grad_J_a_zygote_nu =
145+
make_grad_J_a(J_a_fluence, tlist_nu; mode = :automatic, automatic = Zygote)
146+
@test norm(grad_J_a_zygote_nu(pulsevals_nu, tlist_nu) - G_expected) < 1e-12
147+
148+
end
149+
150+
128151
@testset "J_T without analytic derivative" begin
129152

130153
QuantumControl.set_default_ad_framework(nothing; quiet = true)

0 commit comments

Comments
 (0)