Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updating GST to accept parameterised gates #1534

Merged
merged 9 commits into from
Jan 17, 2025
3 changes: 2 additions & 1 deletion doc/source/code-examples/advancedexamples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2242,7 +2242,8 @@ Let's first define the set of gates we want to estimate:

from qibo import gates

gate_set = {gates.X, gates.H, gates.CZ}
target_gates = [gates.RX(0, np.pi/3), gates.Z(0), gates.PRX(0, np.pi/2, np.pi/3), gates.GPI(0, np.pi/7), gates.CNOT(0,1)]
gate_set = [(g.__class__, [g.parameters[i] for i in range(len(g.parameters))]) if g.parameters else (g.__class__, []) for g in target_gates]

For simulation purposes we can define a noise model. Naturally this is not needed when running on real quantum hardware, which is intrinsically noisy. For example, we can suppose that the three gates we want to estimate are going to be noisy:

Expand Down
32 changes: 27 additions & 5 deletions src/qibo/tomography/gate_set_tomography.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
from functools import cache
from inspect import signature
from itertools import product
from random import Random
from typing import List, Union

import numpy as np
from sympy import S

from qibo import Circuit, gates, symbols
from qibo.backends import _check_backend, get_transpiler
from qibo.backends import _check_backend
from qibo.config import raise_error
from qibo.hamiltonians import SymbolicHamiltonian
from qibo.transpiler.optimizer import Preprocessing
Expand Down Expand Up @@ -227,7 +228,12 @@ def GST(
"""Runs Gate Set Tomography on the input ``gate_set``.

Args:
gate_set (tuple or set or list): set of :class:`qibo.gates.Gate` to run GST on.
gate_set (tuple or set or list): set of :class:`qibo.gates.Gate` and parameters to run
GST on.
E.g. target_gates = [gates.RX(0, np.pi/3), gates.Z(0), gates.PRX(0, np.pi/2, np.pi/3),
gates.GPI(0, np.pi/7), gates.CNOT(0,1)]
gate_set = [(g.__class__, [g.parameters[i] for i in range(len(g.parameters))])
if g.parameters else (g.__class__, []) for g in target_gates]
nshots (int, optional): number of shots used in Gate Set Tomography per gate.
Defaults to :math:`10^{4}`.
noise_model (:class:`qibo.noise.NoiseModel`, optional): noise model applied to simulate
Expand Down Expand Up @@ -260,7 +266,15 @@ def GST(
backend = _check_backend(backend)

if backend.name == "qibolab" and transpiler is None: # pragma: no cover
transpiler = get_transpiler()
transpiler = Passes(
connectivity=backend.platform.topology,
passes=[
Preprocessing(backend.platform.topology),
Random(backend.platform.topology),
Sabre(backend.platform.topology),
Unroller(NativeGates.default()),
],
)

matrices = []
empty_matrices = []
Expand All @@ -278,7 +292,15 @@ def GST(

for gate in gate_set:
if gate is not None:
init_args = signature(gate).parameters
init_args = signature(gate[0]).parameters
params = gate[1]

angle_names = [name for name in init_args if name in {"theta", "phi"}]

angle_values = {}
for name, value in zip(angle_names, params): # Zip ensures correct order
angle_values[name] = value

if "q" in init_args:
nqubits = 1
elif "q0" in init_args and "q1" in init_args and "q2" not in init_args:
Expand All @@ -288,7 +310,7 @@ def GST(
RuntimeError,
f"Gate {gate} is not supported for `GST`, only 1- and 2-qubits gates are supported.",
)
gate = gate(*range(nqubits))
gate = gate[0](*range(nqubits), **angle_values)

matrices.append(
_gate_tomography(
Expand Down
27 changes: 22 additions & 5 deletions tests/test_tomography_gate_set_tomography.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,10 @@ def test_gate_tomography_noise_model(backend):

@pytest.mark.parametrize(
"target_gates",
[[gates.SX(0), gates.Z(0), gates.CY(0, 1)], [gates.TOFFOLI(0, 1, 2)]],
[
[gates.SX(0), gates.Z(0), gates.PRX(0, np.pi, np.pi / 2), gates.CY(0, 1)],
[gates.TOFFOLI(0, 1, 2)],
],
)
@pytest.mark.parametrize("pauli_liouville", [False, True])
def test_GST(backend, target_gates, pauli_liouville):
Expand All @@ -215,9 +218,16 @@ def test_GST(backend, target_gates, pauli_liouville):
target_matrices = [
to_pauli_liouville(m, normalize=True, backend=backend) for m in target_matrices
]
gate_set = [g.__class__ for g in target_gates]
gate_set = [
(
(g.__class__, [g.parameters[i] for i in range(len(g.parameters))])
if g.parameters
else (g.__class__, [])
)
for g in target_gates
]

if len(target_gates) == 3:
if len(target_gates) == 4:
empty_1q, empty_2q, *approx_gates = GST(
gate_set=gate_set,
nshots=int(1e4),
Expand All @@ -241,7 +251,7 @@ def test_GST(backend, target_gates, pauli_liouville):
else:
with pytest.raises(RuntimeError):
empty_1q, empty_2q, *approx_gates = GST(
gate_set=[g.__class__ for g in target_gates],
gate_set=gate_set,
nshots=int(1e4),
include_empty=True,
pauli_liouville=pauli_liouville,
Expand All @@ -265,7 +275,14 @@ def test_GST_with_transpiler(backend, star_connectivity):
import networkx as nx

target_gates = [gates.SX(0), gates.Z(0), gates.CNOT(0, 1)]
gate_set = [g.__class__ for g in target_gates]
gate_set = [
(
(g.__class__, [g.parameters[i] for i in range(len(g.parameters))])
if g.parameters
else (g.__class__, [])
)
for g in target_gates
]
# standard not transpiled GST
empty_1q, empty_2q, *approx_gates = GST(
gate_set=gate_set,
Expand Down
Loading