Skip to content

Commit 5110d93

Browse files
authored
Improve Bloch sphere rendering for animation (#520)
1 parent 13709a1 commit 5110d93

File tree

4 files changed

+108
-11
lines changed

4 files changed

+108
-11
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased](https://github.com/qutip/QuantumToolbox.jl/tree/main)
99

10+
- Improve Bloch sphere rendering for animation. ([#520])
11+
1012
## [v0.34.0]
1113
Release date: 2025-07-29
1214

@@ -292,3 +294,4 @@ Release date: 2024-11-13
292294
[#513]: https://github.com/qutip/QuantumToolbox.jl/issues/513
293295
[#515]: https://github.com/qutip/QuantumToolbox.jl/issues/515
294296
[#517]: https://github.com/qutip/QuantumToolbox.jl/issues/517
297+
[#520]: https://github.com/qutip/QuantumToolbox.jl/issues/520

docs/src/users_guide/plotting_the_bloch_sphere.md

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,4 +211,66 @@ These properties can also be accessed via the `print` command:
211211
```@example Bloch_sphere_rendering
212212
b = Bloch()
213213
print(b)
214-
```
214+
```
215+
216+
## Animating with the Bloch sphere
217+
218+
The [`Bloch`](@ref) structure was designed from the outset to generate animations. To animate a set of vectors or data points, the basic idea is: plot the data at time ``t_1``, save the sphere, clear the sphere, plot data at ``t_2``, and so on. The easiest way to animate data on the Bloch sphere is to use the `record` function provided by [`Makie.jl`](https://docs.makie.org/stable/). We will demonstrate this functionality with the following example: the decay of a qubit on the Bloch sphere.
219+
220+
```@example Bloch_sphere_rendering
221+
# system parameters
222+
ω = 2π
223+
θ = 0.2π
224+
n_th = 0.5 # temperature
225+
γ1 = 0.5
226+
γ2 = 0.2
227+
228+
# operators and the Hamiltonian
229+
sx = sigmax()
230+
sy = sigmay()
231+
sz = sigmaz()
232+
sm = sigmam()
233+
H = ω * (cos(θ) * sz + sin(θ) * sx)
234+
235+
# collapse operators
236+
c_op_list = (
237+
√(γ1 * (n_th + 1)) * sm,
238+
√(γ1 * n_th) * sm',
239+
√γ2 * sz
240+
)
241+
242+
# solving evolution
243+
ψ0 = basis(2, 0)
244+
tlist = LinRange(0, 4, 250)
245+
sol = mesolve(H, ψ0, tlist, c_op_list, e_ops = (sx, sy, sz), progress_bar = Val(false))
246+
```
247+
248+
To animate a set of vectors or data points, we use the `record` function provided by [`Makie.jl`](https://docs.makie.org/stable/):
249+
250+
```@example Bloch_sphere_rendering
251+
# expectation values
252+
x = real(sol.expect[1,:])
253+
y = real(sol.expect[2,:])
254+
z = real(sol.expect[3,:])
255+
256+
# create Bloch sphere
257+
b = Bloch()
258+
b.view = [50,30]
259+
fig, lscene = render(b)
260+
261+
# save animation
262+
record(fig, "qubit_decay.mp4", eachindex(tlist), framerate = 20) do idx
263+
clear!(b)
264+
add_vectors!(b, [sin(θ), 0, cos(θ)])
265+
add_points!(b, [x[1:idx], y[1:idx], z[1:idx]])
266+
render(b, location = lscene)
267+
end
268+
nothing # hide
269+
```
270+
271+
```@raw html
272+
<video autoplay loop muted playsinline controls src="./qubit_decay.mp4" />
273+
```
274+
275+
!!! note
276+
Here, we set the keyword argument `location = lscene` in the last `render` function to update the existing Bloch sphere without creating new `Figure` and `LScene`. This is efficient when drawing animations.

ext/QuantumToolboxMakieExt.jl

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -337,14 +337,24 @@ Render the Bloch sphere visualization from the given [`Bloch`](@ref) object `b`.
337337
# Arguments
338338
339339
- `b::Bloch`: The Bloch sphere object containing states, vectors, and settings to visualize.
340-
- `location::Union{GridPosition,Nothing}`: The location of the plot in the layout. If `nothing`, the plot is created in a new figure. Default is `nothing`.
340+
- `location::Union{GridPosition,LScene,Nothing}`: The location of the plot in the layout, or `Makie.LScene`. Default is `nothing`.
341+
341342
342343
# Returns
343344
344345
- A tuple `(fig, lscene)` where `fig` is the figure object and `lscene` is the LScene object used for plotting. These can be further manipulated or saved by the user.
346+
347+
# Notes
348+
349+
The keyword argument `location` can be in the either type:
350+
351+
- `Nothing` (default): Create a new figure and plot the Bloch sphere.
352+
- `GridPosition`: Plot the Bloch sphere in the specified location of the plot in the layout.
353+
- `LScene`: Update the existing Bloch sphere using new data and settings in `b::Bloch` without creating new `Figure` and `LScene` (efficient for drawing animation).
345354
"""
346355
function QuantumToolbox.render(b::Bloch; location = nothing)
347-
fig, lscene = _setup_bloch_plot!(b, location)
356+
fig, lscene = _setup_bloch_plot!(location)
357+
_setup_bloch_camara!(b, lscene)
348358
_draw_bloch_sphere!(b, lscene)
349359
_add_labels!(b, lscene)
350360

@@ -358,30 +368,44 @@ function QuantumToolbox.render(b::Bloch; location = nothing)
358368
end
359369

360370
raw"""
361-
_setup_bloch_plot!(b::Bloch, location) -> (fig, lscene)
371+
_setup_bloch_plot!(location) -> (fig, lscene)
362372
363-
Initialize the figure and `3D` axis for Bloch sphere visualization.
373+
Initialize the Figure and LScene for Bloch sphere visualization.
364374
365375
# Arguments
366-
- `b::Bloch`: Bloch sphere object containing view parameters
367-
- `location`: Figure layout position specification
376+
- `location`: Figure layout position specification, or directly `Makie.LScene` for updating Bloch sphere.
368377
369378
# Returns
370379
- `fig`: Created Makie figure
371380
- `lscene`: Configured LScene object
372-
373-
Sets up the `3D` coordinate system with appropriate limits and view angles.
374381
"""
375-
function _setup_bloch_plot!(b::Bloch, location)
382+
function _setup_bloch_plot!(location)
376383
fig, location = _getFigAndLocation(location)
377384
lscene = LScene(location, show_axis = false, scenekw = (clear = true,))
385+
return fig, lscene
386+
end
387+
388+
function _setup_bloch_plot!(lscene::LScene)
389+
# this function only removes all existing Plots in lscene
390+
# it is useful for users to just update Bloch sphere without creating new figure and lscene (efficient for drawing animation)
391+
fig = lscene.parent
392+
empty!(lscene.scene.plots)
393+
return fig, lscene
394+
end
395+
396+
raw"""
397+
_setup_bloch_camara!(b::Bloch, lscene)
398+
399+
Setup the distance and view angle of the camara.
400+
"""
401+
function _setup_bloch_camara!(b::Bloch, lscene)
378402
length(b.view) == 2 || throw(ArgumentError("The length of `Bloch.view` must be 2."))
379403
cam3d!(lscene.scene, center = false)
380404
cam = cameracontrols(lscene)
381405
cam.fov[] = 12 # Set field of view to 12 degrees
382406
dist = 12 # Set distance from the camera to the Bloch sphere
383407
update_cam!(lscene.scene, cam, deg2rad(b.view[1]), deg2rad(b.view[2]), dist)
384-
return fig, lscene
408+
return nothing
385409
end
386410

387411
raw"""

test/ext-test/cpu/makie/makie_ext.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,4 +220,12 @@ end
220220
@test false
221221
@info "Render threw unexpected error" exception=e
222222
end
223+
224+
# if render location is given as lscene, should return the same Figure and LScene
225+
b = Bloch()
226+
fig1, lscene1 = render(b)
227+
add_states!(b, ψ₁)
228+
fig2, lscene2 = render(b, location = lscene1)
229+
@test fig2 === fig1
230+
@test lscene2 === lscene1
223231
end

0 commit comments

Comments
 (0)