kestrelquantum / QuantumCollocation.jl

Quantum Optimal Control with Direct Collocation
MIT License
29 stars 7 forks source link

[Feature]: rydberg gate problem templates #88

Open aarontrowbridge opened 5 months ago

aarontrowbridge commented 5 months ago

Feature Description

We would love to compare Piccolo with the results in Time-Optimal Two- and Three-Qubit Gates for Rydberg Atoms.

This should be a straightforward task where one

Implementation Guidelines

Importance

3

What does this feature affect?

Other information

No response

victor-onofre commented 4 months ago

Hi, @aarontrowbridge! This issue is interesting to me. I have basic experience with Julia, but I would like to try it. Do you have any examples you recommend? Thanks!

aarontrowbridge commented 4 months ago

Hey @victor-onofre, my collaborator @andgoldschmidt is going to send some details but what we would hopefully be able to do is set up a problem analogously to the quickstart example in the docs. So what needs to be worked out -- and I think we have an idea of how to do this -- is setting up the Hamiltonian eq. (1) in the paper for some specific parameters.

The UnitarySmoothPulseProblem template has a way to enforce complex modulus bounds on the drives so it's probably possible to set up the problem directly. It might be possible even to just solve a state transfer problem that has the same interface.

I would be happy to hop on discord with you to get into the details as we really want to make a push on this problem.

andgoldschmidt commented 4 months ago

Hi @victor-onofre! Like @aarontrowbridge said, we’ll start from the Hamiltonian (1) in Jandura and Pupillo. As a first pass, we can try to get a similar result in Piccolo without needing to be as clever :smile:

First, let’s look at the Hamiltonian. It is defined as two 3 level systems, each system having $|0\rangle, |1\rangle, |r\rangle$. There is a coupling in the $|1\rangle, |r \rangle$ subspace. The control in Equation (1) is global. Units in the paper are defined using $\Omega\text{max}$. Blockade strength is $B \approx 10 \Omega\text{max}$ (see Section 6.1.1), and the duration is $T * \Delta t \approx 10 / \Omega_\text{max}$.

Setting $\Omega_\text{max} = 1/10$ might be good enough for problem scaling. Let’s start with $T = 100$ timesteps with size $\Delta t = 1$ .

1. Create a quantum system. Let’s start by trying out a two qubit system.

RYDBERG = Dict(
    "X" => [0 0 0; 0 0 1; 0 1 0], 
    "Y" => [0 0 0; 0 0 -1im; 0 1im 0],
    "N" => [0 0 0; 0 0 0; 0 0 1]
)

H_drift = kron_from_dict("NN", RYDBERG)
H_drives = [
    kron_from_dict("XI", RYDBERG) + kron_from_dict("IX", RYDBERG),
    kron_from_dict("YI", RYDBERG) + kron_from_dict("IY", RYDBERG)
]

B = 1.0
system = QuantumSystem(B * H_drift, H_drives)

2. Create a target gate. Let’s say we want to achieve a CZ gate. Notice that we only care about the qubit space, not the Rydberg states. We can encode this using the EmbeddedOperators. We made that interface a little too reliant on composite transmons :nerd_face:, so this definitely needs better constructors (I'll write an issue soon--or feel free to beat me to the punch):

CZ = [1 0 0 0; 0 1 0 0; 0 0 1 0; 0 0 0 -1]
levels = [3, 3]
embedding = [1:2, 1:2]
subspace_indices = get_subspace_indices(embedding, levels)
U_goal = EmbeddedOperator(
    embed(CZ, system, subspace=subspace_indices),
    subspace_indices,
    levels
)

3. Define a unitary smooth pulse problem Take a look at the problem templates. We should have all the pieces to set up our problem.

prob = UnitarySmoothPulseProblem(system, U_goal, T, Δt)
solve!(prob, max_iter=100)
println("Fidelity: ", unitary_fidelity(prob, subspace=subspace_indices))

4. Optimize for noise and uncertainty. Let’s try to make the problem robust to the blockade value $B$; we are uncertain about it because atom positions are imprecise. We can also minimize the gate duration---faster is usually better because we don’t want T1 to hurt our gates. For reference, look again at the grab bag of problem templates---now, we want to combine a minimum time problem and unitary robustness problem.

andgoldschmidt commented 4 months ago

Some caveats and notes:

victor-onofre commented 4 months ago

Hi @andgoldschmidt and @aarontrowbridge,

Thank you so much for the clear steps and explanations. I will start working on this tomorrow night (European time). Before starting with the actual problem, I will read your paper on Piccolo to better understand the tool. Then, I will go through the Jandura and Pupilo paper.

victor-onofre commented 4 months ago

Hi @andgoldschmidt and @aarontrowbridge,

I have read your paper and the Rydberg atoms paper. Right now I understand the general steps. I wanted to run the example you have in Quickstart Guide but I have an error. Do you know what is the issue?

image

As I mentioned before, my experience with Julia is basic. Sorry if it is a simple Julia error.

Thanks!

victor-onofre commented 4 months ago

I changed from Ubuntu to Windows, and the get_gate error still shows up. The good news is that the JuliaCon23 example works, I was able to run everything.

andgoldschmidt commented 4 months ago

It is possible that our published code is behind the source. Did you add the package with add or install locally from source with dev?

aarontrowbridge commented 4 months ago

if you run add QuantumCollocation#main to load the main branch that should also work

aarontrowbridge commented 4 months ago

this is a julia package registration issue we've been having, that honestly needs to be it's own issue.

victor-onofre commented 4 months ago

I fix the issue by installing it locally from the source. The problem was the package registration

victor-onofre commented 4 months ago

I have a couple of questions about the plots in the Quickstart Guide. What is the meaning of the different unitary operator U in the plots? For the one qubit example, you have 8? Why not 2?

image

In your paper, you have an example of a Two-Qubit CNOT Gate Problem:

image

And the results show the 4 different u in the hamiltonian:

image

To me, the plots of the two qubits CNOT make sense, it shows the optimization of the different u to get to the correct state

Thanks!

andgoldschmidt commented 4 months ago

Right! The unitary is a raw output here, so that means we have to account for all the free numbers. A single qubit means a 4 element unitary with real and imaginary parts, for a total of 8 real components. That's what the :Ũ⃗ notation says, read as "unitary_isomorphism_vector". The isomorphism is the real-imaginary isomorphism, and the vector refers to the flattening.

This is the state vector that we evolve in the code when we solve for a gate.

victor-onofre commented 4 months ago

Okay, now is clear to me. Thanks! In the one qubit example, we plot each unitary matrix element. Like in your example in the paper:

image

I think the notation in Quickstart Guide confused me, sorry!

How can I plot the pulse like in the CNOT example with Piccolo?

aarontrowbridge commented 4 months ago

there's actually a function, plot_unitary_populations, for doing exactly this

victor-onofre commented 4 months ago

Hi @andgoldschmidt! Do you think you have time for a Discord call? Maybe tomorrow? It will help me a lot. My timezone is CEST. Thanks!