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
2 changes: 1 addition & 1 deletion doc/source/code-examples/advancedexamples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2242,7 +2242,7 @@ Let's first define the set of gates we want to estimate:

from qibo import gates

gate_set = {gates.X, gates.H, gates.CZ}
gate_set = [(gates.RX, [np.pi/3]), gates.Z, (gates.PRX, [np.pi/2, np.pi/3]), (gates.GPI, [np.pi/7]), gates.CNOT]

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
35 changes: 28 additions & 7 deletions src/qibo/tomography/gate_set_tomography.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
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 @@ -224,10 +224,13 @@ def GST(
backend=None,
transpiler=None,
):
"""Runs Gate Set Tomography on the input ``gate_set``.
"""Run 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. gate_set = [(gates.RX, [np.pi/3]), gates.Z, (gates.PRX, [np.pi/2, np.pi/3]),
(gates.GPI, [np.pi/7]), gates.CNOT]
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 +263,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,17 +289,27 @@ def GST(

for gate in gate_set:
if gate is not None:
init_args = signature(gate).parameters

if isinstance(gate, tuple):
angles = ["theta", "phi", "lam"]
gate, params = gate
init_args = signature(gate).parameters
valid_angles = [arg for arg in init_args if arg in angles]
angle_values = dict(zip(valid_angles, params))
else:
angle_values = {}
init_args = signature(gate).parameters

if "q" in init_args:
nqubits = 1
elif "q0" in init_args and "q1" in init_args and "q2" not in init_args:
nqubits = 2
else:
raise_error(
RuntimeError,
f"Gate {gate} is not supported for `GST`, only 1- and 2-qubits gates are supported.",
f"Gate {gate} is not supported for `GST`, only 1- and 2-qubit gates are supported.",
)
gate = gate(*range(nqubits))
gate = gate(*range(nqubits), **angle_values)

matrices.append(
_gate_tomography(
Expand Down
26 changes: 20 additions & 6 deletions tests/test_tomography_gate_set_tomography.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,15 @@ 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.RX(0, np.pi / 4),
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,17 +223,20 @@ 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]

if len(target_gates) == 3:
gate_set = [
((g.__class__, list(g.parameters)) if g.parameters else g.__class__)
for g in target_gates
]

if len(target_gates) == 4:
empty_1q, empty_2q, *approx_gates = GST(
gate_set=gate_set,
nshots=int(1e4),
include_empty=True,
pauli_liouville=pauli_liouville,
backend=backend,
)
print(type(empty_1q), type(empty_2q))
T_2q = np.kron(T, T)
for target, estimate in zip(target_matrices, approx_gates):
if not pauli_liouville:
Expand All @@ -241,7 +252,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 +276,10 @@ 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__, list(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