From 1072e55720144ab83e418d03cad79b46bee3695c Mon Sep 17 00:00:00 2001 From: Matthew Date: Tue, 3 Dec 2024 14:53:00 +0800 Subject: [PATCH 1/8] Updating GST input for parameterised gates --- src/qibo/tomography/gate_set_tomography.py | 23 +++++++++++--------- tests/test_tomography_gate_set_tomography.py | 19 +++++++++------- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/qibo/tomography/gate_set_tomography.py b/src/qibo/tomography/gate_set_tomography.py index 5a8331f527..88f0444abe 100644 --- a/src/qibo/tomography/gate_set_tomography.py +++ b/src/qibo/tomography/gate_set_tomography.py @@ -1,13 +1,13 @@ 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 @@ -260,7 +260,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 = [] @@ -278,17 +286,12 @@ def GST( for gate in gate_set: if gate is not None: - 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: + nqubits = len(gate.qubits) + if nqubits not in (1, 2): raise_error( RuntimeError, f"Gate {gate} is not supported for `GST`, only 1- and 2-qubits gates are supported.", ) - gate = gate(*range(nqubits)) matrices.append( _gate_tomography( diff --git a/tests/test_tomography_gate_set_tomography.py b/tests/test_tomography_gate_set_tomography.py index be0c4d4fc6..11037695c7 100644 --- a/tests/test_tomography_gate_set_tomography.py +++ b/tests/test_tomography_gate_set_tomography.py @@ -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): @@ -215,11 +218,11 @@ 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__ 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, + gate_set=target_gates, nshots=int(1e4), include_empty=True, pauli_liouville=pauli_liouville, @@ -241,7 +244,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=target_gates, # [g.__class__ for g in target_gates], nshots=int(1e4), include_empty=True, pauli_liouville=pauli_liouville, @@ -265,10 +268,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__ for g in target_gates] # standard not transpiled GST empty_1q, empty_2q, *approx_gates = GST( - gate_set=gate_set, + gate_set=target_gates, nshots=int(1e4), include_empty=True, pauli_liouville=False, @@ -288,7 +291,7 @@ def test_GST_with_transpiler(backend, star_connectivity): ) # transpiled GST T_empty_1q, T_empty_2q, *T_approx_gates = GST( - gate_set=gate_set, + gate_set=target_gates, nshots=int(1e4), include_empty=True, pauli_liouville=False, From b92e0b752eb94de745d6178e524f595fc26e89ad Mon Sep 17 00:00:00 2001 From: Matthew Date: Tue, 3 Dec 2024 16:21:17 +0800 Subject: [PATCH 2/8] Modified GST example --- doc/source/code-examples/advancedexamples.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/code-examples/advancedexamples.rst b/doc/source/code-examples/advancedexamples.rst index 21e25e2465..24c0ead6f5 100644 --- a/doc/source/code-examples/advancedexamples.rst +++ b/doc/source/code-examples/advancedexamples.rst @@ -2240,7 +2240,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.X(0), gates.H(0), gates.CZ(0, 1)} 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: From b05337ee07f1f005362fe689b2946978ae6b7e55 Mon Sep 17 00:00:00 2001 From: Matthew Date: Wed, 11 Dec 2024 14:33:31 +0800 Subject: [PATCH 3/8] Modify gst, test_tomography_gst, advancedexamples.rst for gate params --- doc/source/code-examples/advancedexamples.rst | 3 ++- src/qibo/tomography/gate_set_tomography.py | 25 +++++++++++++++--- tests/test_tomography_gate_set_tomography.py | 26 ++++++++++++++----- 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/doc/source/code-examples/advancedexamples.rst b/doc/source/code-examples/advancedexamples.rst index 1d58ed00a7..069705bacb 100644 --- a/doc/source/code-examples/advancedexamples.rst +++ b/doc/source/code-examples/advancedexamples.rst @@ -2242,7 +2242,8 @@ Let's first define the set of gates we want to estimate: from qibo import gates - gate_set = {gates.X(0), gates.H(0), gates.CZ(0, 1)} + 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: diff --git a/src/qibo/tomography/gate_set_tomography.py b/src/qibo/tomography/gate_set_tomography.py index 88f0444abe..865867380e 100644 --- a/src/qibo/tomography/gate_set_tomography.py +++ b/src/qibo/tomography/gate_set_tomography.py @@ -1,4 +1,5 @@ from functools import cache +from inspect import signature from itertools import product from random import Random from typing import List, Union @@ -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 @@ -286,12 +292,25 @@ def GST( for gate in gate_set: if gate is not None: - nqubits = len(gate.qubits) - if nqubits not in (1, 2): + 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: + nqubits = 2 + else: raise_error( RuntimeError, f"Gate {gate} is not supported for `GST`, only 1- and 2-qubits gates are supported.", ) + gate = gate[0](*range(nqubits), **angle_values) matrices.append( _gate_tomography( diff --git a/tests/test_tomography_gate_set_tomography.py b/tests/test_tomography_gate_set_tomography.py index 11037695c7..a76fd993ba 100644 --- a/tests/test_tomography_gate_set_tomography.py +++ b/tests/test_tomography_gate_set_tomography.py @@ -218,11 +218,18 @@ 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) == 4: empty_1q, empty_2q, *approx_gates = GST( - gate_set=target_gates, + gate_set=gate_set, nshots=int(1e4), include_empty=True, pauli_liouville=pauli_liouville, @@ -244,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=target_gates, # [g.__class__ for g in target_gates], + gate_set=gate_set, nshots=int(1e4), include_empty=True, pauli_liouville=pauli_liouville, @@ -268,10 +275,17 @@ 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=target_gates, + gate_set=gate_set, nshots=int(1e4), include_empty=True, pauli_liouville=False, @@ -291,7 +305,7 @@ def test_GST_with_transpiler(backend, star_connectivity): ) # transpiled GST T_empty_1q, T_empty_2q, *T_approx_gates = GST( - gate_set=target_gates, + gate_set=gate_set, nshots=int(1e4), include_empty=True, pauli_liouville=False, From 06b78f8cc7d7908ae77cf1f220461a2473669095 Mon Sep 17 00:00:00 2001 From: Matthew Date: Fri, 17 Jan 2025 14:42:52 +0800 Subject: [PATCH 4/8] Update GST and test GST --- src/qibo/tomography/gate_set_tomography.py | 21 ++++++++----- tests/test_tomography_gate_set_tomography.py | 31 ++++++++++++-------- 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/src/qibo/tomography/gate_set_tomography.py b/src/qibo/tomography/gate_set_tomography.py index 865867380e..f470e93b65 100644 --- a/src/qibo/tomography/gate_set_tomography.py +++ b/src/qibo/tomography/gate_set_tomography.py @@ -292,13 +292,20 @@ def GST( for gate in gate_set: if gate is not None: - init_args = signature(gate[0]).parameters - params = gate[1] - - angle_names = [name for name in init_args if name in {"theta", "phi"}] + if isinstance(gate, tuple): + init_args = signature(gate[0]).parameters + params = gate[1] + gate_class = gate[0] + else: + init_args = signature(gate).parameters + params = [] + gate_class = gate + angle_names = [ + name for name in init_args if name in {"theta", "phi", "lam"} + ] angle_values = {} - for name, value in zip(angle_names, params): # Zip ensures correct order + for name, value in zip(angle_names, params): angle_values[name] = value if "q" in init_args: @@ -310,12 +317,12 @@ def GST( RuntimeError, f"Gate {gate} is not supported for `GST`, only 1- and 2-qubits gates are supported.", ) - gate = gate[0](*range(nqubits), **angle_values) + what_gate = gate_class(*range(nqubits), **angle_values) matrices.append( _gate_tomography( nqubits=nqubits, - gate=gate, + gate=what_gate, nshots=nshots, noise_model=noise_model, backend=backend, diff --git a/tests/test_tomography_gate_set_tomography.py b/tests/test_tomography_gate_set_tomography.py index a76fd993ba..aab5c62538 100644 --- a/tests/test_tomography_gate_set_tomography.py +++ b/tests/test_tomography_gate_set_tomography.py @@ -205,7 +205,13 @@ def test_gate_tomography_noise_model(backend): @pytest.mark.parametrize( "target_gates", [ - [gates.SX(0), gates.Z(0), gates.PRX(0, np.pi, np.pi / 2), gates.CY(0, 1)], + # [gates.SX(0), gates.X(0), gates.Z(0), gates.CY(0, 1)], + [ + 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)], ], ) @@ -218,12 +224,9 @@ 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__, [g.parameters[i] for i in range(len(g.parameters))]) - if g.parameters - else (g.__class__, []) - ) + ((g.__class__, list(g.parameters)) if g.parameters else g.__class__) for g in target_gates ] @@ -235,7 +238,7 @@ def test_GST(backend, target_gates, pauli_liouville): pauli_liouville=pauli_liouville, backend=backend, ) - print(type(empty_1q), type(empty_2q)) + # 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: @@ -275,12 +278,16 @@ 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__, [g.parameters[i] for i in range(len(g.parameters))]) + # if g.parameters + # else (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__, []) - ) + ((g.__class__, list(g.parameters)) if g.parameters else g.__class__) for g in target_gates ] # standard not transpiled GST From 3454611ba18b7710c76f93a27ba357ea06a2b68d Mon Sep 17 00:00:00 2001 From: Matthew Date: Fri, 17 Jan 2025 19:04:21 +0800 Subject: [PATCH 5/8] Update advancedexamples.rst, minor changes to GST and test GST --- doc/source/code-examples/advancedexamples.rst | 3 +- src/qibo/tomography/gate_set_tomography.py | 30 +++++++------------ tests/test_tomography_gate_set_tomography.py | 10 ------- 3 files changed, 11 insertions(+), 32 deletions(-) diff --git a/doc/source/code-examples/advancedexamples.rst b/doc/source/code-examples/advancedexamples.rst index 069705bacb..79b5fc50ac 100644 --- a/doc/source/code-examples/advancedexamples.rst +++ b/doc/source/code-examples/advancedexamples.rst @@ -2242,8 +2242,7 @@ Let's first define the set of gates we want to estimate: from qibo import gates - 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] + 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: diff --git a/src/qibo/tomography/gate_set_tomography.py b/src/qibo/tomography/gate_set_tomography.py index f470e93b65..4e8dd37a73 100644 --- a/src/qibo/tomography/gate_set_tomography.py +++ b/src/qibo/tomography/gate_set_tomography.py @@ -1,7 +1,6 @@ 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 @@ -230,10 +229,8 @@ def GST( Args: 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] + 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 @@ -292,21 +289,14 @@ def GST( for gate in gate_set: if gate is not None: + if isinstance(gate, tuple): - init_args = signature(gate[0]).parameters - params = gate[1] - gate_class = gate[0] + angles = ["theta", "phi", "lam"] + gate, params = gate + angle_values = dict(zip(angles[: len(params)], params)) else: - init_args = signature(gate).parameters - params = [] - gate_class = gate - - angle_names = [ - name for name in init_args if name in {"theta", "phi", "lam"} - ] - angle_values = {} - for name, value in zip(angle_names, params): - angle_values[name] = value + angle_values = {} + init_args = signature(gate).parameters if "q" in init_args: nqubits = 1 @@ -317,12 +307,12 @@ def GST( RuntimeError, f"Gate {gate} is not supported for `GST`, only 1- and 2-qubits gates are supported.", ) - what_gate = gate_class(*range(nqubits), **angle_values) + gate = gate(*range(nqubits), **angle_values) matrices.append( _gate_tomography( nqubits=nqubits, - gate=what_gate, + gate=gate, nshots=nshots, noise_model=noise_model, backend=backend, diff --git a/tests/test_tomography_gate_set_tomography.py b/tests/test_tomography_gate_set_tomography.py index aab5c62538..463684f20c 100644 --- a/tests/test_tomography_gate_set_tomography.py +++ b/tests/test_tomography_gate_set_tomography.py @@ -205,7 +205,6 @@ def test_gate_tomography_noise_model(backend): @pytest.mark.parametrize( "target_gates", [ - # [gates.SX(0), gates.X(0), gates.Z(0), gates.CY(0, 1)], [ gates.SX(0), gates.RX(0, np.pi / 4), @@ -238,7 +237,6 @@ def test_GST(backend, target_gates, pauli_liouville): 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: @@ -278,14 +276,6 @@ 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__, [g.parameters[i] for i in range(len(g.parameters))]) - # if g.parameters - # else (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 From 219dc8c58c45a739a9dec485ddb68060541beeea Mon Sep 17 00:00:00 2001 From: Matthew Date: Fri, 17 Jan 2025 19:51:32 +0800 Subject: [PATCH 6/8] Minor modification to ensure all angles can run --- src/qibo/tomography/gate_set_tomography.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/qibo/tomography/gate_set_tomography.py b/src/qibo/tomography/gate_set_tomography.py index 4e8dd37a73..d3a75c2949 100644 --- a/src/qibo/tomography/gate_set_tomography.py +++ b/src/qibo/tomography/gate_set_tomography.py @@ -293,10 +293,12 @@ def GST( if isinstance(gate, tuple): angles = ["theta", "phi", "lam"] gate, params = gate - angle_values = dict(zip(angles[: len(params)], params)) + 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 + init_args = signature(gate).parameters if "q" in init_args: nqubits = 1 From bea1c6473c23e04d71f288843353867b495c4181 Mon Sep 17 00:00:00 2001 From: Matthew <46650770+mho291@users.noreply.github.com> Date: Fri, 17 Jan 2025 21:41:47 +0800 Subject: [PATCH 7/8] Update src/qibo/tomography/gate_set_tomography.py Co-authored-by: Matteo Robbiati <62071516+MatteoRobbiati@users.noreply.github.com> --- src/qibo/tomography/gate_set_tomography.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibo/tomography/gate_set_tomography.py b/src/qibo/tomography/gate_set_tomography.py index d3a75c2949..bdc9007184 100644 --- a/src/qibo/tomography/gate_set_tomography.py +++ b/src/qibo/tomography/gate_set_tomography.py @@ -224,7 +224,7 @@ 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` and parameters to run From a26c06a02b25365e3b31cf456d03e9f4a6d6029a Mon Sep 17 00:00:00 2001 From: Matthew <46650770+mho291@users.noreply.github.com> Date: Fri, 17 Jan 2025 21:41:55 +0800 Subject: [PATCH 8/8] Update src/qibo/tomography/gate_set_tomography.py Co-authored-by: Matteo Robbiati <62071516+MatteoRobbiati@users.noreply.github.com> --- src/qibo/tomography/gate_set_tomography.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibo/tomography/gate_set_tomography.py b/src/qibo/tomography/gate_set_tomography.py index bdc9007184..7016b52a59 100644 --- a/src/qibo/tomography/gate_set_tomography.py +++ b/src/qibo/tomography/gate_set_tomography.py @@ -307,7 +307,7 @@ def GST( 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), **angle_values)