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

Handling global backend for protocols involving circuits #1076

Merged
merged 12 commits into from
Feb 12, 2025
57 changes: 40 additions & 17 deletions src/qibocal/auto/transpile.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,32 @@
from typing import Optional

from qibo import Circuit
from qibo.backends.abstract import Backend
from qibo.backends import construct_backend, get_backend
from qibo.transpiler.pipeline import Passes
from qibo.transpiler.unroller import NativeGates, Unroller
from qibolab import MetaBackend
from qibolab.platform import Platform
from qibolab.qubits import QubitId


def _get_platforms():
"""Qibolab platforms."""
try:
platforms = list(MetaBackend().list_available())
except RuntimeError:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a comment on this PR, but just a point raised as a consequence.

Maybe we should actually drop this exception: if there is no path, we should already return an empty list.
It should be sufficient to do it here:
https://github.com/qiboteam/qibolab/blob/6b2a7b790e8d58d52be937c2f41b8606459a3bf1/src/qibolab/_core/platform/load.py#L23-L26

This should result in .list_available() returning an empty list, and .load() raising a different error.

@stavros11

platforms = []

return platforms + ["dummy"]


AVAILABLE_PLATFORMS = _get_platforms()
"""Available qibolab platforms."""


def transpile_circuits(
circuits: list[Circuit],
qubit_maps: list[list[QubitId]],
backend: Backend,
platform: Platform,
transpiler: Optional[Passes],
):
"""Transpile and pad `circuits` according to the platform.
Expand All @@ -28,14 +44,13 @@ def transpile_circuits(
are all string or all integers.
"""
transpiled_circuits = []

qubits = list(backend.platform.qubits)
if isinstance(qubit_maps[0][0], str):
for i, qubit_map in enumerate(qubit_maps):
qubit_map = map(lambda x: qubits.index(x), qubit_map)
qubit_maps[i] = list(qubit_map)
if backend.name == "qibolab":
platform_nqubits = backend.platform.nqubits
if platform.name in AVAILABLE_PLATFORMS:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I just forgot, but in which case is this condition supposed to be false?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be False when we transpile circuits that are executed on simultation so no transpilation at all.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be False when we transpile circuits that are executed on simultation so no transpilation at all.

Ah, I see. But in that case there should be no Platform at all, since it is the qibolab.Platform.

My advice would be to drop any simulation reference, and assume it is always Qibolab (possibly the emulator, but still Qibolab).
I would reintroduce the option for simulation as part of a larger refactor, taking into account #913. I'd expect there should be other as well not supporting a backend other than Qibolab.

qubits = list(platform.qubits)
if isinstance(qubit_maps[0][0], str):
for i, qubit_map in enumerate(qubit_maps):
qubit_map = map(lambda x: qubits.index(x), qubit_map)
qubit_maps[i] = list(qubit_map)
platform_nqubits = platform.nqubits
for circuit, qubit_map in zip(circuits, qubit_maps):
new_circuit = pad_circuit(platform_nqubits, circuit, qubit_map)
transpiled_circ, _ = transpiler(new_circuit)
Expand All @@ -48,7 +63,7 @@ def transpile_circuits(
def execute_transpiled_circuits(
circuits: list[Circuit],
qubit_maps: list[list[QubitId]],
backend: Backend,
platform: Platform,
transpiler: Optional[Passes],
initial_states=None,
nshots=1000,
Expand All @@ -66,9 +81,13 @@ def execute_transpiled_circuits(
transpiled_circuits = transpile_circuits(
circuits,
qubit_maps,
backend,
platform,
transpiler,
)
if platform.name in AVAILABLE_PLATFORMS:
backend = construct_backend(backend="qibolab", platform=platform)
else:
backend = get_backend()
return transpiled_circuits, backend.execute_circuits(
transpiled_circuits, initial_states=initial_states, nshots=nshots
)
Expand All @@ -77,7 +96,7 @@ def execute_transpiled_circuits(
def execute_transpiled_circuit(
circuit: Circuit,
qubit_map: list[QubitId],
backend: Backend,
platform: Platform,
transpiler: Optional[Passes],
initial_state=None,
nshots=1000,
Expand All @@ -96,22 +115,26 @@ def execute_transpiled_circuit(
transpiled_circ = transpile_circuits(
[circuit],
[qubit_map],
backend,
platform,
transpiler,
)[0]
if platform.name in AVAILABLE_PLATFORMS:
backend = construct_backend(backend="qibolab", platform=platform)
else:
backend = get_backend()
return transpiled_circ, backend.execute_circuit(
transpiled_circ, initial_state=initial_state, nshots=nshots
)


def dummy_transpiler(backend) -> Optional[Passes]:
def dummy_transpiler(platform) -> Optional[Passes]:
"""
If the backend is `qibolab`, a transpiler with just an unroller is returned,
otherwise None.
"""
if backend.name == "qibolab":
if platform.name in AVAILABLE_PLATFORMS:
unroller = Unroller(NativeGates.default())
return Passes(connectivity=backend.platform.topology, passes=[unroller])
return Passes(connectivity=platform.topology, passes=[unroller])
return None


Expand Down
8 changes: 4 additions & 4 deletions src/qibocal/protocols/randomized_benchmarking/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,7 @@ def get_circuits(
return circuits, indexes, npulses_per_clifford


def execute_circuits(circuits, targets, params, backend, single_qubit=True):
def execute_circuits(circuits, targets, params, platform, single_qubit=True):
"""
Executes a list of circuits on a given backend.

Expand All @@ -455,7 +455,7 @@ def execute_circuits(circuits, targets, params, backend, single_qubit=True):

"""
# Execute the circuits
transpiler = dummy_transpiler(backend)
transpiler = dummy_transpiler(platform)
qubit_maps = (
[[i] for i in targets] * (len(params.depths) * params.niter)
if single_qubit
Expand All @@ -465,7 +465,7 @@ def execute_circuits(circuits, targets, params, backend, single_qubit=True):
_, executed_circuits = execute_transpiled_circuits(
circuits,
qubit_maps=qubit_maps,
backend=backend,
platform=platform,
nshots=params.nshots,
transpiler=transpiler,
)
Expand All @@ -474,7 +474,7 @@ def execute_circuits(circuits, targets, params, backend, single_qubit=True):
execute_transpiled_circuit(
circuit,
qubit_map=qubit_map,
backend=backend,
platform=platform,
nshots=params.nshots,
transpiler=transpiler,
)[1]
Expand Down
8 changes: 3 additions & 5 deletions src/qibocal/protocols/readout_mitigation_matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import numpy.typing as npt
import plotly.express as px
from qibo import gates
from qibo.backends import get_backend
from qibo.models import Circuit
from qibolab.platform import Platform
from qibolab.qubits import QubitId
Expand Down Expand Up @@ -61,9 +60,8 @@ def _acquisition(
data = ReadoutMitigationMatrixData(
nshots=params.nshots, qubit_list=[list(qq) for qq in targets]
)
backend = get_backend()
backend.platform = platform
transpiler = dummy_transpiler(backend)

transpiler = dummy_transpiler(platform)
qubit_map = [i for i in range(platform.nqubits)]
for qubits in targets:
nqubits = len(qubits)
Expand All @@ -77,7 +75,7 @@ def _acquisition(
c.add(gates.X(q))
c.add(gates.M(*range(nqubits)))
_, results = execute_transpiled_circuit(
c, qubits, backend, nshots=params.nshots, transpiler=transpiler
c, qubits, platform, nshots=params.nshots, transpiler=transpiler
)
frequencies = np.zeros(2 ** len(qubits))
for i, freq in results.frequencies().items():
Expand Down
8 changes: 3 additions & 5 deletions src/qibocal/protocols/state_tomography.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import plotly.graph_objects as go
from plotly.subplots import make_subplots
from qibo import Circuit, gates
from qibo.backends import NumpyBackend, get_backend, matrices
from qibo.backends import NumpyBackend, matrices
from qibo.quantum_info import fidelity, partial_trace
from qibolab.platform import Platform
from qibolab.qubits import QubitId
Expand Down Expand Up @@ -102,9 +102,7 @@ def _acquisition(
if params.circuit is None:
params.circuit = Circuit(len(targets))

backend = get_backend()
backend.platform = platform
transpiler = dummy_transpiler(backend)
transpiler = dummy_transpiler(platform)

data = StateTomographyData(
circuit=params.circuit, targets={target: i for i, target in enumerate(targets)}
Expand All @@ -121,7 +119,7 @@ def _acquisition(
_, results = execute_transpiled_circuit(
basis_circuit,
targets,
backend,
platform,
nshots=params.nshots,
transpiler=transpiler,
)
Expand Down
7 changes: 2 additions & 5 deletions src/qibocal/protocols/two_qubit_interaction/chsh/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import numpy as np
import numpy.typing as npt
import plotly.graph_objects as go
from qibo.backends import get_backend
from qibolab import ExecutionParameters
from qibolab.platform import Platform
from qibolab.qubits import QubitId, QubitPairId
Expand Down Expand Up @@ -222,9 +221,7 @@ def _acquisition_circuits(
bell_states=params.bell_states,
thetas=thetas.tolist(),
)
backend = get_backend()
backend.platform = platform
transpiler = dummy_transpiler(backend)
transpiler = dummy_transpiler(platform)
if params.apply_error_mitigation:
mitigation_data = mitigation_acquisition(
mitigation_params(nshots=params.nshots), platform, targets
Expand Down Expand Up @@ -252,7 +249,7 @@ def _acquisition_circuits(
circuit,
nshots=params.nshots,
transpiler=transpiler,
backend=backend,
platform=platform,
qubit_map=pair,
)
frequencies = result.frequencies()
Expand Down
8 changes: 3 additions & 5 deletions src/qibocal/protocols/two_qubit_state_tomography.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import plotly.graph_objects as go
from plotly.subplots import make_subplots
from qibo import Circuit, gates
from qibo.backends import NumpyBackend, get_backend
from qibo.backends import NumpyBackend
from qibo.quantum_info import fidelity, partial_trace
from qibo.result import QuantumState
from qibolab.platform import Platform
Expand Down Expand Up @@ -97,10 +97,8 @@ def _acquisition(
if params.circuit is None:
params.circuit = Circuit(len(qubits))

backend = get_backend()
backend.platform = platform
simulator = NumpyBackend()
transpiler = dummy_transpiler(backend)
transpiler = dummy_transpiler(platform)

simulated_state = simulator.execute_circuit(deepcopy(params.circuit))
data = StateTomographyData(simulated=simulated_state)
Expand All @@ -127,7 +125,7 @@ def _acquisition(
_, results = execute_transpiled_circuit(
basis_circuit,
qubits,
backend,
platform,
nshots=params.nshots,
transpiler=transpiler,
)
Expand Down
10 changes: 6 additions & 4 deletions tests/test_transpile.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@ def test_execute_transpiled_circuit():
qubit_map = [1, 2]
set_backend("qibolab", platform="dummy")
backend = get_backend()
transpiler = dummy_transpiler(backend)
platform = backend.platform
transpiler = dummy_transpiler(platform)
transpiled_circuit, _ = execute_transpiled_circuit(
circuit, qubit_map, backend, transpiler=transpiler
circuit, qubit_map, platform, transpiler=transpiler
)
true_circuit = Circuit(5)
true_circuit.add(gates.GPI2(1, np.pi / 2))
Expand All @@ -53,9 +54,10 @@ def test_execute_transpiled_circuits():
qubit_map = [1, 2]
set_backend("qibolab", platform="dummy")
backend = get_backend()
transpiler = dummy_transpiler(backend)
platform = backend.platform
transpiler = dummy_transpiler(platform)
transpiled_circuits, _ = execute_transpiled_circuits(
[circuit], [qubit_map], backend, transpiler=transpiler
[circuit], [qubit_map], platform, transpiler=transpiler
)
true_circuit = Circuit(5)
true_circuit.add(gates.GPI2(1, np.pi / 2))
Expand Down