diff --git a/docs/Project.toml b/docs/Project.toml index 8df91abc..eaecb613 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,8 +1,10 @@ [deps] +CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306" LiveServer = "16fef848-5104-11e9-1b77-fb7a48bbb589" NamedTrajectories = "538bc3a1-5ab9-4fc3-b776-35ca1e893e08" +PiccoloPlots = "f42a522c-b487-4f73-ad5a-ad0c3e4a12c8" PiccoloQuantumObjects = "5a402ddf-f93c-42eb-975e-5582dcda653d" QuantumCollocation = "0dc23a59-5ffb-49af-b6bd-932a8ae77adf" Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" diff --git a/docs/literate/examples/multilevel_transmon.jl b/docs/literate/examples/multilevel_transmon.jl new file mode 100644 index 00000000..ca837510 --- /dev/null +++ b/docs/literate/examples/multilevel_transmon.jl @@ -0,0 +1,124 @@ +# # Multilevel Transmon + +# In this example we will look at a multilevel transmon qubit with a Hamiltonian given by +# +# ```math +# \hat{H}(t) = -\frac{\delta}{2} \hat{n}(\hat{n} - 1) + u_1(t) (\hat{a} + \hat{a}^\dagger) + u_2(t) i (\hat{a} - \hat{a}^\dagger) +# ``` +# where $\hat{n} = \hat{a}^\dagger \hat{a}$ is the number operator, $\hat{a}$ is the annihilation operator, $\delta$ is the anharmonicity, and $u_1(t)$ and $u_2(t)$ are control fields. +# +# We will use the following parameter values: +# +# ```math +# \begin{aligned} +# \delta &= 0.2 \text{ GHz}\\ +# \abs{u_i(t)} &\leq 0.2 \text{ GHz}\\ +# T_0 &= 10 \text{ ns}\\ +# \end{aligned} +# ``` +# +# For convenience, we have defined the `TransmonSystem` function in the `QuantumSystemTemplates` module, which returns a `QuantumSystem` object for a transmon qubit. We will use this function to define the system. + +# ## Setting up the problem + +# To begin, let's load the necessary packages, define the system parameters, and create a a `QuantumSystem` object using the `TransmonSystem` function. + +using QuantumCollocation +using PiccoloQuantumObjects +using NamedTrajectories +using LinearAlgebra +using SparseArrays +using Random; Random.seed!(123) + +using PiccoloPlots +using CairoMakie + +## define the time parameters + +T₀ = 10 # total time in ns +T = 50 # number of time steps +Δt = T₀ / T # time step + +## define the system parameters +levels = 5 +δ = 0.2 + +## add a bound to the controls +a_bound = 0.2 + +## create the system +sys = TransmonSystem(levels=levels, δ=δ) + +## let's look at the parameters of the system +sys.params + + +# Since this is a multilevel transmon and we want to implement an, let's say, $X$ gate on the qubit subspace, i.e., the first two levels we can utilize the `EmbeddedOperator` type to define the target operator. + +## define the target operator +op = EmbeddedOperator(:X, sys) + +## show the full operator +op.operator |> sparse + +# In this formulation, we also use a subspace identity as the initial state, which looks like + +function get_subspace_identity(op::EmbeddedOperator) + return embed( + Matrix{ComplexF64}(I(length(op.subspace))), + op.subspace, + size(op)[1] + ) +end +get_subspace_identity(op) |> sparse + +# We can then pass this embedded operator to the `UnitarySmoothPulseProblem` template to create the problem + +## create the problem +prob = UnitarySmoothPulseProblem(sys, op, T, Δt; a_bound=a_bound) + +## solve the problem +solve!(prob; max_iter=50) + +# Let's look at the fidelity in the subspace + +fid = unitary_rollout_fidelity(prob.trajectory, sys; subspace=op.subspace) +println("Fidelity: ", fid) +@assert fid > 0.99 + +# and plot the result using the `plot_unitary_populations` function. + +plot_unitary_populations(prob.trajectory; fig_size=(900, 700)) + + +# ## Leakage suppresion +# As can be seen from the above plot, there is a substantial amount of leakage into the higher levels during the evolution. To mitigate this, we have implemented a constraint to avoid populating the leakage levels, which should ideally drive those leakage populations down to zero. +# To implement this, pass `leakage_constraint=true` and set `leakage_constraint_value={value}` and `leakage_cost={value}` to the `PiccoloOptions` instance passed to the `UnitarySmoothPulseProblem` template. + +## create the a leakage suppression problem, initializing with the previous solution + +prob_leakage = UnitarySmoothPulseProblem(sys, op, T, Δt; + a_bound=a_bound, + a_guess=prob.trajectory.a[:, :], + piccolo_options=PiccoloOptions( + leakage_constraint=true, + leakage_constraint_value=1e-2, + leakage_cost=1e-2, + ), +) + +## solve the problem + +solve!(prob_leakage; max_iter=250) + +# Let's look at the fidelity in the subspace + +fid_leakage = unitary_rollout_fidelity(prob_leakage.trajectory, sys; subspace=op.subspace) +println("Fidelity: ", fid_leakage) +@assert fid_leakage > 0.99 + +# and plot the result using the `plot_unitary_populations` function. + +plot_unitary_populations(prob_leakage.trajectory; fig_size=(900, 700)) + +# Here we can see that the leakage populations have been driven substantially down. diff --git a/docs/literate/examples/two_qubit_gates.jl b/docs/literate/examples/two_qubit_gates.jl index b0781a9b..fc0683ca 100644 --- a/docs/literate/examples/two_qubit_gates.jl +++ b/docs/literate/examples/two_qubit_gates.jl @@ -34,8 +34,8 @@ using PiccoloQuantumObjects using NamedTrajectories using LinearAlgebra -# `using PiccoloPlots` -# `using CairoMakie` +using PiccoloPlots +using CairoMakie ⊗(a, b) = kron(a, b) @@ -116,7 +116,7 @@ println(fid_final) # Looks good! # Now let's plot the pulse and the population trajectories for the first two columns of the unitary, i.e. initial state of $\ket{00}$ and $\ket{01}$. For this we provide the function [`plot_unitary_populations`](@ref). -# `plot_unitary_populations(prob.trajectory)` +plot_unitary_populations(prob.trajectory) # For fun, let's look at a minimum time pulse for this problem @@ -126,7 +126,7 @@ fid_final_min_time = unitary_rollout_fidelity(min_time_prob.trajectory, sys) println(fid_final_min_time) # And let's plot this solution -# `plot_unitary_populations(min_time_prob.trajectory)` +plot_unitary_populations(min_time_prob.trajectory) # It looks like our pulse derivative bounds are holding back the solution, but regardless, the duration has decreased: duration = get_duration(prob.trajectory) @@ -135,6 +135,7 @@ println(duration, " - ", min_time_duration, " = ", duration - min_time_duration) + # ## Mølmer–Sørensen gate # Here we will solve for a [Mølmer–Sørensen gate](https://en.wikipedia.org/wiki/M%C3%B8lmer%E2%80%93S%C3%B8rensen_gate) between two. The gate is generally described, for N qubits, by the unitary matrix @@ -183,7 +184,7 @@ println(fid_final) # Again, looks good! # Now let's plot the pulse and the population trajectories for the first two columns of the unitary, i.e. initial state of $\ket{00}$ and $\ket{01}$. -# `plot_unitary_populations(prob.trajectory)` +plot_unitary_populations(prob.trajectory) # For fun, let's look at a minimum time pulse for this problem @@ -193,7 +194,7 @@ fid_final_min_time = unitary_rollout_fidelity(min_time_prob.trajectory, sys) println(fid_final_min_time) # And let's plot this solution -# `plot_unitary_populations(min_time_prob.trajectory)` +plot_unitary_populations(min_time_prob.trajectory) # It looks like our pulse derivative bounds are holding back the solution, but regardless, the duration has decreased: diff --git a/docs/make.jl b/docs/make.jl index 7875f14f..3a296bca 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -10,6 +10,7 @@ pages = [ "Home" => "index.md", "Examples" => [ "Two Qubit Gates" => "generated/examples/two_qubit_gates.md", + "Multilevel Transmon" => "generated/examples/multilevel_transmon.md", ], "Library" => [ "Ket Problem Templates" => "generated/man/ket_problem_templates.md",