Create QuantumToolbox.jl logo

Introduction

In this tutorial, 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:

\[| \psi_{\text{cat}} \rangle = \frac{1}{\sqrt{2}} \left( | \alpha \rangle + | -\alpha \rangle \right)\]

where $| \alpha \rangle$ is a coherent state with amplitude $\alpha$.

The triangular cat state is a generalization of the standard cat state. It is a superposition of three coherent states with phases $\theta_0, \theta_1, \theta_2$ separated by $120^\circ$(or $2\pi/3$radians):

\[| \psi_{\text{tri-cat}} \rangle = \frac{1}{\sqrt{3}} \left( | \alpha_0 \rangle + | \alpha_1 \rangle + | \alpha_2 \rangle \right)\]

where $\alpha_j = \rho e^{i\theta_j}$with $\theta_j = \frac{\pi}{2} + \frac{2\pi j}{3}$and $j = 0, 1, 2$.

Wigner Function

The Wigner function $W(x, p)$is a quasi-probability distribution used in quantum mechanics to represent quantum states in phase space. It is defined as:

\[W(x, p) = \frac{1}{\pi \hbar} \int_{-\infty}^{\infty} \psi^*(x + y) \psi(x - y) e^{2ipy / \hbar} \, dy\]

where $\psi(x)$is the wave function of the quantum state, $x$is the position, $p$is the momentum, and $\hbar$is the reduced Planck constant. Unlike classical probability distributions, the Wigner function can take negative values, which indicates non-classical behavior.

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 calculating the Wigner function

We define the grid for the Wigner function and calculate it using the wigner function. 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 $2$.

xvec = range(-ρ, ρ, 500) .* 1.5
yvec = xvec .+ (abs(imag(α1)) - abs(imag(α2))) / 2

wig = wigner(ψ, xvec, yvec, g = 2)
500×500 Matrix{Float64}:
 1.2746e-6    1.40069e-6   1.53778e-6   …  1.40069e-6   1.2746e-6
 1.42358e-6   1.5645e-6    1.71771e-6      1.5645e-6    1.42358e-6
 1.58864e-6   1.74598e-6   1.91706e-6      1.74598e-6   1.58864e-6
 1.77134e-6   1.94687e-6   2.13774e-6      1.94687e-6   1.77134e-6
 1.97338e-6   2.16903e-6   2.3818e-6       2.16903e-6   1.97338e-6
 2.1966e-6    2.41449e-6   2.65146e-6   …  2.41449e-6   2.1966e-6
 2.443e-6     2.68545e-6   2.94913e-6      2.68545e-6   2.443e-6
 2.71474e-6   2.98427e-6   3.27742e-6      2.98427e-6   2.71474e-6
 3.01413e-6   3.31351e-6   3.63914e-6      3.31351e-6   3.01413e-6
 3.34369e-6   3.67593e-6   4.03733e-6      3.67593e-6   3.34369e-6
 ⋮                                      ⋱               
 5.02397e-13  5.58105e-13  6.20755e-13     5.58105e-13  5.02397e-13
 4.39263e-13  4.86875e-13  5.40279e-13     4.86875e-13  4.39263e-13
 3.83717e-13  4.24285e-13  4.69654e-13     4.24285e-13  3.83717e-13
 3.34972e-13  3.69444e-13  4.07859e-13     3.69444e-13  3.34972e-13
 2.92299e-13  3.21515e-13  3.53945e-13  …  3.21515e-13  2.92299e-13
 2.55023e-13  2.79729e-13  3.07029e-13     2.79729e-13  2.55023e-13
 2.22522e-13  2.43375e-13  2.66301e-13     2.43375e-13  2.22522e-13
 1.94233e-13  2.11808e-13  2.3102e-13      2.11808e-13  1.94233e-13
 1.69644e-13  1.84442e-13  2.00515e-13     1.84442e-13  1.69644e-13

Plotting the Wigner function

Finally, we plot the Wigner function using the heatmap function from the CairoMakie package.

fig = Figure(size = (500, 500), figure_padding = 0)
ax = Axis(fig[1, 1])
heatmap!(ax, xvec, yvec, wig', colormap = :RdBu, interpolate = true, rasterize = 1)
hidespines!(ax)
hidexdecorations!(ax)
hideydecorations!(ax)
fig
Example block output

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:

\[\frac{d \hat{\rho}}{dt} = -i [\hat{H}, \hat{\rho}] + \gamma \left( 2 \hat{a} \hat{\rho} \hat{a}^\dagger - \hat{a}^\dagger \hat{a} \hat{\rho} - \hat{\rho} \hat{a}^\dagger \hat{a} \right)\]

where $\hat{\rho}$ is the density matrix, $\hat{H} = \omega \hat{a}^\dagger \hat{a}$is the Hamiltonian of the harmonic oscillator ($\hbar = 1$), $\hat{a}$and $\hat{a}^\dagger$are the annihilation and creation operators, and $\gamma$is the damping rate. Thus, we initialize the system in the triangular cat state and evolve it under the Lindblad master equation, using the 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:

wig = wigner(sol.states[end], xvec, yvec, g = 2)

fig = Figure(size = (500, 500), figure_padding = 0)
ax = Axis(fig[1, 1])

img_wig = heatmap!(ax, xvec, yvec, wig', colormap = :RdBu, interpolate = true, rasterize = 1)
hidespines!(ax)
hidexdecorations!(ax)
hideydecorations!(ax)

fig
Example block output

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.

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

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)))
Example block output

Normalizing the Wigner function and applying the custom colormap

The colormaps require the input to be in the range $[0, 1]$. We normalize the Wigner function such that the maximum value is $1$and the zeros are set to $0.5$.

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 Array{RGBA{Float32},2} with eltype ColorTypes.RGBA{Float32}:
 RGBA{Float32}(0.523498,0.311502,0.523505,6.26417f-6)   …  RGBA{Float32}(0.417504,0.366499,0.772491,6.26426f-6)
 RGBA{Float32}(0.523498,0.311502,0.523505,7.01657f-6)      RGBA{Float32}(0.417504,0.366499,0.772491,7.01667f-6)
 RGBA{Float32}(0.523498,0.311502,0.523504,7.85268f-6)      RGBA{Float32}(0.417504,0.366499,0.772491,7.8528f-6)
 RGBA{Float32}(0.523499,0.311502,0.523504,8.78099f-6)      RGBA{Float32}(0.417504,0.366499,0.772491,8.78112f-6)
 RGBA{Float32}(0.523499,0.311501,0.523504,9.81069f-6)      RGBA{Float32}(0.417505,0.366499,0.772491,9.81085f-6)
 RGBA{Float32}(0.523499,0.311501,0.523503,1.09518f-5)   …  RGBA{Float32}(0.417505,0.366499,0.77249,1.0952f-5)
 RGBA{Float32}(0.5235,0.311501,0.523503,1.22153f-5)        RGBA{Float32}(0.417505,0.366498,0.77249,1.22155f-5)
 RGBA{Float32}(0.5235,0.311501,0.523502,1.36129f-5)        RGBA{Float32}(0.417505,0.366498,0.77249,1.36131f-5)
 RGBA{Float32}(0.523501,0.311501,0.523502,1.51574f-5)      RGBA{Float32}(0.417506,0.366498,0.77249,1.51577f-5)
 RGBA{Float32}(0.523501,0.311501,0.523501,1.68628f-5)      RGBA{Float32}(0.417506,0.366498,0.77249,1.68631f-5)
 ⋮                                                      ⋱  
 RGBA{Float32}(0.237399,0.49081,0.498168,1.87583f-12)      RGBA{Float32}(0.2367,0.491173,0.49981,1.87583f-12)
 RGBA{Float32}(0.237332,0.490852,0.498162,1.64313f-12)     RGBA{Float32}(0.236658,0.491202,0.499746,1.64269f-12)
 RGBA{Float32}(0.237267,0.490892,0.498157,1.43729f-12)     RGBA{Float32}(0.236617,0.49123,0.499684,1.43729f-12)
 RGBA{Float32}(0.237205,0.490931,0.498151,1.25633f-12)     RGBA{Float32}(0.236577,0.491257,0.499625,1.25633f-12)
 RGBA{Float32}(0.237145,0.490969,0.498146,1.09757f-12)  …  RGBA{Float32}(0.23654,0.491283,0.499568,1.0969f-12)
 RGBA{Float32}(0.237087,0.491005,0.498141,9.57678f-13)     RGBA{Float32}(0.236503,0.491308,0.499513,9.57678f-13)
 RGBA{Float32}(0.237031,0.49104,0.498136,8.35554f-13)      RGBA{Float32}(0.236468,0.491333,0.499459,8.35554f-13)
 RGBA{Float32}(0.236977,0.491074,0.498131,7.28972f-13)     RGBA{Float32}(0.236433,0.491356,0.499408,7.28972f-13)
 RGBA{Float32}(0.236925,0.491107,0.498126,6.35714f-13)     RGBA{Float32}(0.236401,0.491379,0.499358,6.35714f-13)

Final Plot

Finally, we plot the Wigner function with the custom colormap.

fig = Figure(size = (500, 500), figure_padding = 0, backgroundcolor = :transparent)
ax = Axis(fig[1, 1], backgroundcolor = :transparent)
image!(ax, img', rasterize = 1)
hidespines!(ax)
hidexdecorations!(ax)
hideydecorations!(ax)
fig
Example block output

Conclusion

This tutorial 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.