Example: Create QuantumToolbox.jl logo
Introduction
In this example, we will demonstrate how to create the logo for the QuantumToolbox.jl package. The logo represents the Wigner function of the triangular cat state, which is a linear superposition of three coherent states. The resulting Wigner function has a triangular shape that resembles the Julia logo. We will also define a custom colormap that varies based on the value of the Wigner function and the spatial coordinates, such that the three blobs corresponding to the coherent states have different colors (matching the colors of the Julia logo).
Triangular Cat State
A cat state, often referred to as a Schrödinger cat state, is a quantum state that is a superposition of two coherent states with opposite phases:
where
The triangular cat state is a generalization of the standard cat state. It is a superposition of three coherent states with phases
where
Wigner Function
The Wigner function
where
Generating the Logo
First, let's load the required packages:
using QuantumToolbox
using CairoMakie
CairoMakie.activate!(type = "svg", pt_per_unit = 1)
CairoMakie.enable_only_mime!(MIME"image/svg+xml"())
Parameters
Here we define the parameters for the triangular cat state:
N = 30 # Cutoff of the Hilbert space for the harmonic oscillator
ρ = 2.5 # Amplitude of the coherent state
θ1 = π / 2
θ2 = π / 2 + 2π / 3
θ3 = π / 2 + 4π / 3
α1 = ρ * exp(im * θ1)
α2 = ρ * exp(im * θ2)
α3 = ρ * exp(im * θ3)
2.165063509461096 - 1.250000000000001im
Constructing the State
Next, we construct the triangular cat state as a normalized superposition of three coherent states:
ψ = coherent(N, α1) + coherent(N, α2) + coherent(N, α3)
normalize!(ψ)
Quantum Object: type=Ket dims=[30] size=(30,)
30-element Vector{ComplexF64}:
0.07609684210792242 + 2.4780334358076702e-20im
8.011906616501529e-18 + 8.813097278151682e-17im
-1.7626194556303364e-16 - 3.044524514270581e-16im
-4.749279822481873e-16 - 0.4854125890665235im
-1.6023813233003058e-17 + 2.243333852620428e-16im
-4.166191440580795e-16 + 3.2047626466006115e-17im
-0.6923735018977101 + 1.3291838510784089e-15im
6.089049028541162e-16 + 2.72404824961052e-16im
8.011906616501529e-17 + 4.486667705240856e-16im
1.3522340589679618e-15 + 0.4818869843344701im
⋮
1.6162428094893032e-17 + 0.0024206665998124624im
1.8777906132425458e-18 - 4.6318835126649465e-18im
1.5961220212561639e-18 - 3.912063777588637e-19im
0.00034320343258066735 - 2.617852237969679e-18im
-6.650508421900683e-19 - 2.855806557639705e-19im
-8.019730744056706e-20 - 2.308117628777296e-19im
-3.4626027331738858e-19 - 4.0804319967534345e-5im
-3.471956602609915e-20 + 8.019730744056706e-20im
-3.4230558053900575e-20 + 1.0024663430070883e-20im
Defining the Grid and plotting the Wigner function
We define the grid for the Wigner function and plot it using the plot_wigner
function. This, internally calls the wigner
function for the computation. We shift the grid in the imaginary direction to ensure that the Wigner function is centered around the origin of the figure. The wigner
function also supports the g
scaling factor, which we put here equal to
xvec = range(-ρ, ρ, 500) .* 1.5
yvec = xvec .+ (abs(imag(α1)) - abs(imag(α2))) / 2
fig = Figure(size = (250, 250), figure_padding = 0)
fig, ax, hm = plot_wigner(ψ, xvec = xvec, yvec = yvec, g = 2, library = Val(:CairoMakie), location = fig[1,1])
hidespines!(ax)
hidexdecorations!(ax)
hideydecorations!(ax)
fig
Introducing some decoherence
The figure obtained above coulb be already a potential logo for the package. However, we see that the fringe patterns are more intense than the three coherent gaussian amplitudes. We can introduce some decoherence to reduce this effect. Thus, we evolve the system under the evolution of a damped quantum harmonic oscillator, which is described by the Lindblad master equation:
where mesolve
function.
γ = 0.012
a = destroy(N)
H = a' * a
c_ops = [sqrt(γ) * a]
tlist = range(0, 2π, 100)
sol = mesolve(H, ψ, tlist, c_ops, progress_bar = Val(false))
And the Wigner function becomes more uniform:
fig = Figure(size = (250, 250), figure_padding = 0)
fig, ax, hm = plot_wigner(sol.states[end], xvec = xvec, yvec = yvec, g = 2, library = Val(:CairoMakie), location = fig[1,1])
hidespines!(ax)
hidexdecorations!(ax)
hideydecorations!(ax)
fig
At this stage, we have finished to use the QuantumToolbox
package. From now on, we will use the CairoMakie
package to define custom colormaps and plot the Wigner function in a Julia logo style.
Custom Colormap
We define a custom colormap that changes depending on the Wigner function and spatial coordinates. Indeed, we want the three different colormaps, in the regions corresponding to the three coherent states, to match the colors of the Julia logo. We also want the colormap change to be smooth, so we use a Gaussian function to blend the colors. We introduce also a Wigner function dependent transparency to make the logo more appealing. In order to do so, we are going to need the value of the wigner function at each point of the grid, rather than its plot. We will thus call the wigner
function directly.
function set_color_julia(x, y, wig::T, α1, α2, α3, cmap1, cmap2, cmap3, δ) where {T}
amp1 = gaussian(x, real(α1), δ) * gaussian(y, imag(α1), δ)
amp2 = gaussian(x, real(α2), δ) * gaussian(y, imag(α2), δ)
amp3 = gaussian(x, real(α3), δ) * gaussian(y, imag(α3), δ)
c1 = get(cmap1, wig)
c2 = get(cmap2, wig)
c3 = get(cmap3, wig)
c_tot = (amp1 * c1 + amp2 * c2 + amp3 * c3) / (amp1 + amp2 + amp3)
wig_abs = abs(2 * (wig - 1 / 2))
# We introduce some non-linearity to increase the contrast
alpha = 2 * (1 / (1 + exp(-5 * wig_abs)) - 1 / 2)
return RGBAf(c_tot.r, c_tot.g, c_tot.b, alpha)
end
wig = wigner(sol.states[end], xvec, yvec, g = 2)
X, Y = meshgrid(xvec, yvec)
δ = 1.25 # Smoothing parameter for the Gaussian functions
1.25
Colormaps from the Julia colors
We define the colormaps for the three coherent states using the colors of the Julia logo. We use the cgrad
function from the CairoMakie
package to create the colormaps.
julia_red = RGBAf(0.796, 0.235, 0.2, 1.0)
julia_green = RGBAf(0.22, 0.596, 0.149, 1.0)
julia_blue = RGBAf(0.251, 0.388, 0.847, 1.0)
julia_purple = RGBAf(0.584, 0.345, 0.698, 1.0)
n_repeats = 2
cmap1 = cgrad(vcat(fill(julia_blue, n_repeats), fill(julia_green, n_repeats)))
cmap2 = cgrad(vcat(fill(julia_blue, n_repeats), fill(julia_red, n_repeats)))
cmap3 = cgrad(vcat(fill(julia_blue, n_repeats), fill(julia_purple, n_repeats)))
Normalizing the Wigner function and applying the custom colormap
The colormaps require the input to be in the range
vmax = maximum(wig)
wig_normalized = wig ./ (vmax * 2) .+ 1 / 2
And we now apply this custom colormap to make an image (a Matrix{RGBAf}
).
img = set_color_julia.(X, Y, wig_normalized, α1, α2, α3, Ref(cmap1), Ref(cmap2), Ref(cmap3), δ)
500×500 Matrix{ColorTypes.RGBA{Float32}}:
RGBA(0.523498, 0.311502, 0.523505, 6.26417f-6) … RGBA(0.417504, 0.366499, 0.772491, 6.26426f-6)
RGBA(0.523498, 0.311502, 0.523505, 7.01657f-6) RGBA(0.417504, 0.366499, 0.772491, 7.01667f-6)
RGBA(0.523498, 0.311502, 0.523504, 7.85268f-6) RGBA(0.417504, 0.366499, 0.772491, 7.8528f-6)
RGBA(0.523499, 0.311502, 0.523504, 8.78099f-6) RGBA(0.417504, 0.366499, 0.772491, 8.78112f-6)
RGBA(0.523499, 0.311501, 0.523504, 9.81069f-6) RGBA(0.417505, 0.366499, 0.772491, 9.81085f-6)
RGBA(0.523499, 0.311501, 0.523503, 1.09518f-5) … RGBA(0.417505, 0.366499, 0.77249, 1.0952f-5)
RGBA(0.5235, 0.311501, 0.523503, 1.22153f-5) RGBA(0.417505, 0.366498, 0.77249, 1.22155f-5)
RGBA(0.5235, 0.311501, 0.523502, 1.36129f-5) RGBA(0.417505, 0.366498, 0.77249, 1.36131f-5)
RGBA(0.523501, 0.311501, 0.523502, 1.51574f-5) RGBA(0.417506, 0.366498, 0.77249, 1.51577f-5)
RGBA(0.523501, 0.311501, 0.523501, 1.68628f-5) RGBA(0.417506, 0.366498, 0.77249, 1.68631f-5)
⋮ ⋱
RGBA(0.237399, 0.49081, 0.498168, 1.87583f-12) RGBA(0.2367, 0.491173, 0.49981, 1.87583f-12)
RGBA(0.237332, 0.490852, 0.498162, 1.64313f-12) RGBA(0.236658, 0.491202, 0.499746, 1.64269f-12)
RGBA(0.237267, 0.490892, 0.498157, 1.43729f-12) RGBA(0.236617, 0.49123, 0.499684, 1.43729f-12)
RGBA(0.237205, 0.490931, 0.498151, 1.25633f-12) RGBA(0.236577, 0.491257, 0.499625, 1.25633f-12)
RGBA(0.237145, 0.490969, 0.498146, 1.09757f-12) … RGBA(0.23654, 0.491283, 0.499568, 1.0969f-12)
RGBA(0.237087, 0.491005, 0.498141, 9.57678f-13) RGBA(0.236503, 0.491308, 0.499513, 9.57678f-13)
RGBA(0.237031, 0.49104, 0.498136, 8.35554f-13) RGBA(0.236468, 0.491333, 0.499459, 8.35554f-13)
RGBA(0.236977, 0.491074, 0.498131, 7.28972f-13) RGBA(0.236433, 0.491356, 0.499408, 7.28972f-13)
RGBA(0.236925, 0.491107, 0.498126, 6.35714f-13) RGBA(0.236401, 0.491379, 0.499358, 6.35714f-13)
Final Plot
Finally, we plot the Wigner function with the custom colormap.
fig = Figure(size = (250, 250), figure_padding = 0, backgroundcolor = :transparent)
ax = Axis(fig[1, 1], backgroundcolor = :transparent)
image!(ax, img', rasterize = 1)
hidespines!(ax)
hidexdecorations!(ax)
hideydecorations!(ax)
fig
Conclusion
This example demonstrates how to generate the QuantumToolbox.jl logo using the package itself and Makie.jl for visualization. The logo is a visualization of the Wigner function of a triangular cat state, with a custom colormap that highlights the different coherent states with colors matching the Julia logo.