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

Constructing Parity Phase Gates

The parity phase gate, generated by a multi-qubit \(Z\) interaction, can be written as

\[U_n(\theta) = e^{-i \gamma Z_1 Z_2 \cdots Z_n},\]

where \(\gamma\) denotes the target phase.

We express this operation in terms of the following primitive gates:

  • single-qubit \(Z\) rotations, \(R_Z(\phi) = e^{-i \frac{\phi}{2} Z}\), and

  • \(k\)-qubit controlled-phase gates (\(k \ge 2\)) acting on a subset \(S = \{i_1,\dots,i_k\}\),

    \[\mathrm{CP}^{(k)}_S(\phi)=\exp\!\left(i \phi \, |1\cdots1\rangle\langle 1\cdots1|_S\right).\]

    This formulation enables the use of built-in tools in RydOpt to design optimal pulse sequences for implementing the parity gate.

The construction is based on expressing Pauli-\(Z\) operators in terms of projectors onto the computational basis state \(|1\rangle\). Using

\[|1\rangle\langle 1| = \frac{1}{2}(I - Z),\]

A Pauli-\(Z\) string can be written as

\[Z_1 Z_2 \cdots Z_n=\prod_{i=1}^{n} (I - 2 P_i),\qquad P_i = |1\rangle\langle 1|_i.\]

Expanding this product yields

\[\begin{split}Z_1 \cdots Z_n=\sum_{k=0}^{n}(-1)^k 2^k \sum_{\substack{S \subseteq \{1,\dots,n\} \\ |S| = k}}P_S,\qquad P_S = |1\cdots1\rangle\langle1\cdots1|_S,\end{split}\]

which, upon exponentiation, decomposes into a product of commuting phase gates: single-qubit \(R_Z\) rotations from the \(k=1\) terms and \(k\)-qubit controlled-phase gates for \(k \ge 2\).

For a general \(n\)-qubit parity operator, the resulting decomposition is

\[\begin{split}\begin{aligned} U_n(\theta) &= \prod_{i=1}^{n} R_Z^{(i)}(-2\gamma) \\ &\quad\times \prod_{k=2}^{n} \prod_{\substack{S \subseteq \{1,\dots,n\} \\ |S| = k}} \mathrm{CP}^{(k)}_S\!\left( (-1)^k 2^k \gamma \right). \end{aligned}\end{split}\]

As a concrete example, for \(n=3\),

\[\begin{split}\begin{aligned} e^{-i \gamma Z_1 Z_2 Z_3} &= R_Z^{(1)}(-2\gamma)\, R_Z^{(2)}(-2\gamma)\, R_Z^{(3)}(-2\gamma) \\ &\quad\times \mathrm{CP}_{12}(4\gamma)\, \mathrm{CP}_{13}(4\gamma)\, \mathrm{CP}_{23}(4\gamma) \\ &\quad\times \mathrm{CP}_{123}(-8\gamma). \end{aligned}\end{split}\]

The coefficients (\(-2, 4, -8, \dots\)) arise from the powers of two in the projector expansion and exhibit an alternating sign structure. Interestingly, at the special value \(\gamma=\pi/4\), corresponding to the maximally entangling parity phase gate, all controlled-phase gates acting on three or more qubits acquire phases that are integer multiples of \(2\pi\) and thus reduce to identities. As a result, \(U_n(\pi/4)\) can be realized entirely using single-qubit \(R_Z\) gates and two-qubit \(\mathrm{CP}(\pi)=\mathrm{CZ}\) gates. In contrast, any deviation from \(\gamma=\pi/4\) necessarily reintroduces genuine many-body controlled-phase interactions.

Fixed–Target-Phase Optimization of the Parity Gate

We seek the shortest-duration pulse that implements a three-atom parity gate in the strong Rydberg blockade regime, using a fixed set of 10 pulse parameters. To this end, we carry out multiple random initializations of the pulse parameters, optimize each candidate pulse, and select the fastest one that achieves a gate fidelity exceeding the target threshold.

First, we create the target gate object. The Rydberg decay strength is set to \(10^{-4}\), and we assume uniform interaction strengths between atoms, \(V_{nn} = V_{nnn} = 32\).

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

import rydopt as ro
import numpy as np

phase = np.pi / 4
gate = ro.gates.ThreeQubitGateIsosceles(
    phi=None,
    theta=4 * phase,
    theta_prime=4 * phase,
    lamb=-8 * phase,
    Vnn=32,
    Vnnn=32,
    decay=0.0001,
)

Next, we create the pulse ansatz and specify upper and lower bounds for the random initialization of pulse parameters.

[2]:
# Pulse ansatz: constant detuning, sweep of the laser phase according to sin_crab ansatz
n_params = 12
pulse_ansatz = ro.pulses.SinglePhotonPulseAnsatz(
    detuning_ansatz=ro.pulses.Const(), phase_ansatz=ro.pulses.SinCrab(n_params)
)

# Bounds for the initial pulse parameter guesses
min_initial_params = ro.pulses.PulseParams(15, [-1], -2 * np.ones(n_params), [])
max_initial_params = ro.pulses.PulseParams(20, [1], 2 * np.ones(n_params), [])

Perform 800 random parameter initializations, running in parallel on 4 CPU cores.

[3]:
opt_result = ro.optimization.multi_start_optimize(
    gate,
    pulse_ansatz,
    min_initial_params,
    max_initial_params,
    num_steps=100,
    tol=1e-8,
    num_initializations=100,
    num_processes=4,
    return_history=True,
    return_all=True,
)
Started optimization using 4 processes


=== Optimization finished using multi-start Adam ===

Runtime: 84.140 seconds
Gates with infidelity below tol=1.0e-08: 0

Best gate:
> infidelity = 8.786351e-04
PulseParams(
  duration =        [20.71079392],
  detuning_params = [-0.1509117],
  phase_params =    [ 0.97312533,  0.86651396, -0.58884901,  1.71954182,  1.78016487, -0.71903126, -1.21707987,
                     -0.21997073,  0.05350197, -0.07214252, -0.78337956,  1.40456329],
  rabi_params =     []
)

We plot the history of all optimization runs;

[4]:
ro.characterization.plot_optimization_history(opt_result);
../_images/examples_parity_gate_family_9_0.png

Also, we analyze the best performing gate;

[5]:
optimized_params = opt_result.params[0]

infidelity, infidelity_nodecay, ryd_time = ro.characterization.analyze_gate(
    gate,
    pulse_ansatz,
    optimized_params,
    tol=1e-8,
)

print(f"Gate infidelity:             {infidelity:.4e}")
print(f"Gate infidelity (no decay):  {infidelity_nodecay:.4e}")
print(f"Rydberg time:                {ryd_time:.4f}")
Gate infidelity:             8.7864e-04
Gate infidelity (no decay):  7.5724e-06
Rydberg time:                8.7149

Finally, we plot the optimal pulse and its spectrum.

[6]:
ro.characterization.plot_spectrum(pulse_ansatz, optimized_params)
fig, ax = ro.characterization.plot_pulse(pulse_ansatz, optimized_params)
../_images/examples_parity_gate_family_13_0.png
../_images/examples_parity_gate_family_13_1.png

Parity gate family

Here, we aim to optimize the parity gate over a range of target gate phases. To this end, we express the trainable parameters of the laser phase ansatz as polynomial functions of \(\theta=4\gamma\). We begin by defining a gate family with a list of target phases and a corresponding pulse family using a polynomial map. Duration and other pulse parameters can be treated analogously by assigning them nonzero polynomial degrees.

[ ]:
target_phases = np.linspace(0.05, 0.25, 5) * np.pi

sampled_gates = [
    ro.gates.ThreeQubitGateIsosceles(
        phi=None,
        theta=4 * phase,
        theta_prime=4 * phase,
        lamb=-8 * phase,
        Vnn=32,
        Vnnn=32,
        decay=0.0001,
    )
    for phase in target_phases
]

gate_family = ro.gates.GateFamily(
    fixed_argument_gates=sampled_gates,
    argument_values=target_phases,
)


# Family pulse ansatz (pulse ansatze + polynomial mapping)
degrees = [1, 0, 3, 0]
pulse_mal = ro.pulses.PolynomialPulseMap(degrees=degrees)
pulse_family = ro.pulses.PulseFamilyAnsatz(
    detuning_ansatz=ro.pulses.Const(),
    phase_ansatz=ro.pulses.SinCrab(n_params),
    pulse_map=pulse_mal,
)

With all components in place, we proceed to the optimization stage. We then assess the individual properties of each optimal pulse in the pulse family and visualize them.

[ ]:
min_initial_params = ro.pulses.PulseFamilyParams(
    [10.0, *[0.0] * degrees[0]],
    -1.0 * np.ones(degrees[1] + 1),
    -2 * np.ones((n_params, degrees[2] + 1)),
    [],
)

max_initial_params = ro.pulses.PulseFamilyParams(
    [20.0, *[34.0] * degrees[0]],
    1.0 * np.ones(degrees[1] + 1),
    2 * np.ones((n_params, degrees[2] + 1)),
    [],
)

opt_result_gate_family = ro.optimization.multi_start_optimize(
    gate_family,
    pulse_family,
    min_initial_params,
    max_initial_params,
    tol=1e-8,
    num_initializations=200,
    num_steps=150,
    num_processes=4,
    return_history=False,
)

optimized_params_gate_family = opt_result_gate_family.params

# Print the gate family performance measures
print("\n=== Performance analysis of the best/fastest optimized gate pulse ===\n")
pulse = pulse_family.pulse_ansatz
for gate, value in zip(gate_family.gates, gate_family.argument_values):
    params = pulse_family.generate_pulse_params(optimized_params_gate_family, value)
    infidelity, infidelity_nodecay, ryd_time = ro.characterization.analyze_gate(
        gate,
        pulse,
        params,
        tol=1e-8,
    )
    print(f"Target phase:                {value / np.pi:.2f}")
    print(f"Gate infidelity:             {infidelity:.4e}")
    print(f"Gate infidelity (no decay):  {infidelity_nodecay:.4e}")
    print(f"Rydberg time:                {ryd_time:.4f}\n")


# plot the optimal pulse family
ro.characterization.plot_pulse_family(
    pulse_family,
    optimized_params_gate_family,
    gate_family,
    plot_detuning=False,
    plot_rabi=False,
);
Started optimization using 4 processes


=== Optimization finished using multi-start Adam ===

Runtime: 1103.771 seconds
Gates with infidelity below tol=1.0e-08: 0

Best gate:
> infidelity = 2.732546e-03
PulseFamilyParams(
  duration_params = [12.84145951, 34.47266429],
  detuning_params = [-0.05502751],
  phase_params =    [[-5.47386302e-01, -2.29527937e+00, -1.58147493e+00, -3.15964634e+00],
                     [ 6.77653106e-03, -3.33171846e-02,  1.87366803e+00,  8.05054874e-01],
                     [-6.73267840e-01, -3.07006390e+00, -2.10001072e+00, -2.29809882e+00],
                     [ 6.45083313e-01, -3.82129397e+00, -5.50096907e+00, -4.37885098e+00],
                     [-1.85643027e+00, -1.56008148e+00, -2.18351522e+00, -2.53363948e+00],
                     [ 1.69460568e+00, -1.83068212e+00, -1.54980447e+00, -5.69337972e+00],
                     [-2.73743996e+00,  6.78249197e-01, -2.94002464e-03, -1.05033156e+00],
                     [ 2.42528651e-01, -3.35988491e+00, -1.41548146e+00, -4.81971892e+00],
                     [-3.86351009e-01, -2.85876933e-01, -1.06333581e-01, -1.61964379e+00],
                     [-5.05386413e-01,  1.70948026e+00,  3.53570634e+00,  2.55074031e+00],
                     [ 4.90904401e-01,  6.65822663e-01,  9.46753687e-01,  2.17052155e-01],
                     [-1.27182955e-01,  2.23905222e+00,  3.03157212e+00,  5.24091678e+00]],
  rabi_params =     []
)

=== Performance analysis of the best/fastest optimized gate pulse ===

Target phase:                0.05
Gate infidelity:             3.2741e-03
Gate infidelity (no decay):  2.7420e-03
Rydberg time:                5.3374

Target phase:                0.10
Gate infidelity:             9.4576e-04
Gate infidelity (no decay):  3.9159e-04
Rydberg time:                5.5464

Target phase:                0.15
Gate infidelity:             3.0473e-03
Gate infidelity (no decay):  2.4631e-03
Rydberg time:                5.8609

Target phase:                0.20
Gate infidelity:             2.5053e-03
Gate infidelity (no decay):  1.8813e-03
Rydberg time:                6.2547

Target phase:                0.25
Gate infidelity:             3.8902e-03
Gate infidelity (no decay):  3.2233e-03
Rydberg time:                6.6996

../_images/examples_parity_gate_family_18_6.png