This page was generated from the Jupyter notebook user_defined_gate.ipynb. Open in Google Colab.

User-Defined Gate

The software comes with classes for various Rydberg gates with two, three, or four atoms. Although many geometries and setups are covered, not every conceivable gate is implemented. If you want to optimize a pulse for a gate that does not yet exist in the RydOpt software, you can define your own gate class. For optimization and fidelity simulation, the class should provide at least these methods:

  • initial_basis_states(): return tuple of basis states

  • hamiltonian_functions_for_basis_states(): return tuple of Hamiltonians under which basis states evolve

  • process_fidelity_helper(): return the process fidelity calculated from the time-evolved basis states

  • cost(): return the objective value used by the optimizer

  • dim(): return the Hilbert-space dimension

As an example, we implement a class that describes a Y gate.

[ ]:
# %pip install -q --progress-bar off rydopt # Uncomment for installation on Colab

import jax
import rydopt as ro
from rydopt.protocols import PulseAnsatzLike
from rydopt.types import HamiltonianFunction, ParamsFloatLike
import jax.numpy as jnp  # jax.numpy should be imported after rydopt


class YGate:
    def initial_basis_states(self) -> tuple[jax.Array, ...]:
        return jnp.array([1.0 + 0.0j, 0.0 + 0.0j]), jnp.array([0.0 + 0.0j, 1.0 + 0.0j])

    def hamiltonian_functions_for_basis_states(self) -> tuple[HamiltonianFunction, ...]:
        def hamiltonian(
            Delta_1: float | jax.Array,
            Delta_r: float | jax.Array,
            Xi: float | jax.Array,
            Omega: float | jax.Array,
        ) -> jax.Array:
            Delta = Delta_r - Delta_1
            return jnp.array(
                [
                    [0.0, 0.5 * Omega * jnp.exp(-1j * Xi)],
                    [0.5 * Omega * jnp.exp(1j * Xi), -Delta],
                ]
            )

        return hamiltonian, hamiltonian

    def process_fidelity_helper(
        self, final_basis_states: tuple[jax.Array, ...]
    ) -> jax.Array:
        obtained_gate = jnp.stack(final_basis_states, axis=1)
        targeted_gate = jnp.array([[0.0 + 0.0j, -1j], [1j, 0.0 + 0.0j]])
        return jnp.abs(jnp.trace(targeted_gate.conj().T @ obtained_gate)) ** 2 / 4.0

    def cost(
        self, pulse: PulseAnsatzLike, params: ParamsFloatLike, tol: float = 1e-7
    ) -> jax.Array:
        return jnp.abs(1.0 - ro.simulation.process_fidelity(self, pulse, params, tol))

    def dim(self) -> int:
        return 2

An instance of the user-defined gate can be passed to RydOpt’s optimization method to find a pulse that implements the gate. For our educational example of a Y gate, a simple pulse ansatz where only the gate duration and a constant laser phase can be optimized is sufficient.

[ ]:
# Create an instance of our gate class
gate = YGate()

# Pulse ansatz: constant phase
pulse_ansatz = ro.pulses.PulseAnsatz(phase_ansatz=ro.pulses.Const())

# Initial pulse parameter guess:
# duration, detuning parameters, phase parameters, Rabi parameters
initial_params = ro.pulses.PulseParams(1.0, [], [1.0], [])

# Optimize the pulse parameters
opt_result = ro.optimization.optimize(gate, pulse_ansatz, initial_params)
optimized_params = opt_result.params

Started optimization using 1 process


=== Optimization finished using Adam ===

Runtime: 4.647 seconds
Gates with infidelity below tol=1.0e-07: 1

Optimized gate:
> infidelity <= tol
> parameters = (3.1420389754087643, [], [1.57088436], [])
> duration = 3.1420389754087643

We obtain a pi-pulse (duration = 3.142038975408764) that rotates about the y-axis (phase = 1.57088436). Finally, we plot the pulse.

[3]:
ro.characterization.plot_pulse(pulse_ansatz, optimized_params);
../_images/examples_user_defined_gate_6_0.png