Skip to content

Commit

Permalink
Merge pull request #810 from qiboteam/fix_transpiling
Browse files Browse the repository at this point in the history
Fix Transpiling
  • Loading branch information
andrea-pasquale authored May 8, 2024
2 parents c358b07 + 655c503 commit aad9f40
Show file tree
Hide file tree
Showing 10 changed files with 450 additions and 266 deletions.
485 changes: 244 additions & 241 deletions poetry.lock

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ classifiers = [

[tool.poetry.dependencies]
python = ">=3.9,<3.12"
qibolab = "^0.1.6"
qibo ="^0.2.6"
qibolab = { git = "https://github.com/qiboteam/qibolab.git"}
qibo = "^0.2.7"
numpy = "^1.26.4"
scipy = "^1.10.1"
pandas = "^1.4.3"
Expand All @@ -30,7 +30,7 @@ dash = "^2.6.0"
skops = "^0.6.0"
scikit-learn = "^1.2.1"
# Explicit dependency required to cope for dash: https://github.com/plotly/dash/issues/2557
setuptools = "^67.8.0"
setuptools = "^69.0.0"
matplotlib = { version = "^3.7.0", optional = true }
seaborn = { version = "^0.12.2", optional = true }
pydot = { version = "^1.4.2", optional = true }
Expand Down
7 changes: 1 addition & 6 deletions src/qibocal/auto/runcard.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

from pydantic.dataclasses import dataclass
from qibo.backends import Backend, GlobalBackend
from qibo.transpiler.pipeline import Passes
from qibolab.platform import Platform
from qibolab.qubits import QubitId, QubitPairId

Expand Down Expand Up @@ -61,11 +60,7 @@ def __post_init__(self):
def backend_obj(self) -> Backend:
"""Allocate backend."""
GlobalBackend.set_backend(self.backend, platform=self.platform)
backend = GlobalBackend()
if backend.platform is not None:
backend.transpiler = Passes(connectivity=backend.platform.topology)
backend.transpiler.passes = backend.transpiler.passes[-1:]
return backend
return GlobalBackend()

@property
def platform_obj(self) -> Platform:
Expand Down
87 changes: 87 additions & 0 deletions src/qibocal/auto/transpile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
from typing import Optional

from qibo import Circuit
from qibo.backends import Backend
from qibo.transpiler.pipeline import Passes
from qibo.transpiler.unroller import NativeGates, Unroller


def execute_transpiled_circuits(
circuits: list[Circuit],
qubit_maps: list[list[int]],
backend: Backend,
initial_states=None,
nshots=1000,
transpiler: Optional[Passes] = None,
):
"""
If the `qibolab` backend is used, this function pads the `circuits` in new
ones with a number of qubits equal to the one provided by the platform.
At the end, the circuits are transpiled, executed and the results returned.
The input `transpiler` is optional, but it should be provided if the backend
is `qibolab`.
For the qubit map look :func:`dummy_transpiler`.
This function returns the list of transpiled circuits and the execution results.
"""
new_circuits = []
if backend.name == "qibolab":
platform_nqubits = backend.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)
new_circuits.append(transpiled_circ)
else:
new_circuits = circuits
return new_circuits, backend.execute_circuits(
new_circuits, initial_states=initial_states, nshots=nshots
)


def execute_transpiled_circuit(
circuit: Circuit,
qubit_map: list[int],
backend: Backend,
initial_state=None,
nshots=1000,
transpiler: Optional[Passes] = None,
):
"""
If the `qibolab` backend is used, this function pads the `circuit` in new a
one with a number of qubits equal to the one provided by the platform.
At the end, the circuit is transpiled, executed and the results returned.
The input `transpiler` is optional, but it should be provided if the backend
is `qibolab`.
For the qubit map look :func:`dummy_transpiler`.
This function returns the transpiled circuit and the execution results.
"""
if backend.name == "qibolab":
platform_nqubits = backend.platform.nqubits
new_circuit = pad_circuit(platform_nqubits, circuit, qubit_map)
transpiled_circ, _ = transpiler(new_circuit)
else:
transpiled_circ = circuit
return transpiled_circ, backend.execute_circuit(
transpiled_circ, initial_state=initial_state, nshots=nshots
)


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


def pad_circuit(nqubits, circuit: Circuit, qubit_map: list[int]) -> Circuit:
"""
Pad `circuit` in a new one with `nqubits` qubits, according to `qubit_map`.
`qubit_map` is a list `[i, j, k, ...]`, where the i-th physical qubit is mapped
into the 0th logical qubit and so on.
"""
new_circuit = Circuit(nqubits)
new_circuit.add(circuit.on_qubits(*qubit_map))
return new_circuit
5 changes: 0 additions & 5 deletions src/qibocal/protocols/characterization/allxy/allxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,9 @@ def add_gate_pair_pulses_to_sequence(

for gate in gates:
if gate == "I":
# print("Transforming to sequence I gate")
pass

if gate == "Xp":
# print("Transforming to sequence Xp gate")
if beta_param == None:
RX_pulse = platform.create_RX_pulse(
qubit,
Expand All @@ -163,7 +161,6 @@ def add_gate_pair_pulses_to_sequence(
sequence.add(RX_pulse)

if gate == "X9":
# print("Transforming to sequence X9 gate")
if beta_param == None:
RX90_pulse = platform.create_RX90_pulse(
qubit,
Expand All @@ -178,7 +175,6 @@ def add_gate_pair_pulses_to_sequence(
sequence.add(RX90_pulse)

if gate == "Yp":
# print("Transforming to sequence Yp gate")
if beta_param == None:
RY_pulse = platform.create_RX_pulse(
qubit,
Expand All @@ -195,7 +191,6 @@ def add_gate_pair_pulses_to_sequence(
sequence.add(RY_pulse)

if gate == "Y9":
# print("Transforming to sequence Y9 gate")
if beta_param == None:
RY90_pulse = platform.create_RX90_pulse(
qubit,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def layer_circuit(rb_gen: Callable, depth: int, qubit) -> tuple[Circuit, dict]:
new_layer, random_index = rb_gen.layer_gen()
# Ensure new_layer is a circuit
if isinstance(new_layer, Gate):
new_circuit = Circuit(1, wire_names=qubits_str)
new_circuit = Circuit(1)
new_circuit.add(new_layer)
random_indexes.append(random_index)

Expand All @@ -48,7 +48,7 @@ def layer_circuit(rb_gen: Callable, depth: int, qubit) -> tuple[Circuit, dict]:
f"layer_gen must return type Circuit or Gate, but it is type {type(new_layer)}.",
)
if full_circuit is None: # instantiate in first loop
full_circuit = Circuit(new_circuit.nqubits, wire_names=qubits_str)
full_circuit = Circuit(new_circuit.nqubits)
full_circuit = full_circuit + new_circuit
return full_circuit, random_indexes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
from qibolab.qubits import QubitId

from qibocal.auto.operation import Data, Parameters, Results, Routine
from qibocal.auto.transpile import (
dummy_transpiler,
execute_transpiled_circuit,
execute_transpiled_circuits,
)
from qibocal.config import raise_error
from qibocal.protocols.characterization.randomized_benchmarking import noisemodels

Expand Down Expand Up @@ -240,19 +245,32 @@ def _acquisition(
circuits.extend(circuits_depth)
for qubit in random_indexes.keys():
indexes[(qubit, depth)] = random_indexes[qubit]
transpiler = dummy_transpiler(backend)
qubit_maps = [[i] for i in targets] * (len(params.depths) * params.niter)
# Execute the circuits
if params.unrolling:
executed_circuits = backend.execute_circuits(circuits, nshots=params.nshots)
_, executed_circuits = execute_transpiled_circuits(
circuits,
qubit_maps=qubit_maps,
backend=backend,
nshots=params.nshots,
transpiler=transpiler,
)
else:
executed_circuits = [
backend.execute_circuit(circuit, nshots=params.nshots)
for circuit in circuits
execute_transpiled_circuit(
circuit,
qubit_map=qubit_map,
backend=backend,
nshots=params.nshots,
transpiler=transpiler,
)[1]
for circuit, qubit_map in zip(circuits, qubit_maps)
]

for circ in executed_circuits:
samples.extend(circ.samples())
samples = np.reshape(samples, (-1, nqubits, params.nshots))

for i, depth in enumerate(params.depths):
index = (i * params.niter, (i + 1) * params.niter)
for nqubit, qubit_id in enumerate(targets):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
import numpy.typing as npt
import plotly.express as px
from qibo import gates
from qibo.backends import GlobalBackend
from qibo.models import Circuit
from qibolab import ExecutionParameters
from qibolab.platform import Platform
from qibolab.pulses import PulseSequence
from qibolab.qubits import QubitId

from qibocal.auto.operation import Data, Parameters, Results, Routine
from qibocal.auto.transpile import dummy_transpiler, execute_transpiled_circuit
from qibocal.config import log

from .utils import calculate_frequencies
Expand Down Expand Up @@ -95,6 +97,9 @@ def _acquisition(
data = ReadoutMitigationMatrixData(
nshots=params.nshots, qubit_list=[list(qq) for qq in targets]
)
backend = GlobalBackend()
transpiler = dummy_transpiler(backend)
qubit_map = [i for i in range(platform.nqubits)]
for qubits in targets:
nqubits = len(qubits)
for i in range(2**nqubits):
Expand All @@ -121,13 +126,17 @@ def _acquisition(
tuple(qubits), state, calculate_frequencies(results, tuple(qubits))
)
else:
c = Circuit(platform.nqubits)
c = Circuit(
platform.nqubits,
wire_names=[str(i) for i in range(platform.nqubits)],
)
for q, bit in enumerate(state):
if bit == "1":
c.add(gates.X(qubits[q]))
c.add(gates.M(qubits[q]))
results = c(nshots=params.nshots)

_, results = execute_transpiled_circuit(
c, qubit_map, backend, nshots=params.nshots, transpiler=transpiler
)
data.add(tuple(qubits), state, dict(results.frequencies()))
return data

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@
import numpy as np
import numpy.typing as npt
import plotly.graph_objects as go
from qibo.backends import GlobalBackend
from qibolab import ExecutionParameters
from qibolab.platform import Platform
from qibolab.qubits import QubitId, QubitPairId

from qibocal.auto.operation import Data, Parameters, Results, Routine
from qibocal.auto.transpile import dummy_transpiler, execute_transpiled_circuit

from ...readout_mitigation_matrix import (
ReadoutMitigationMatrixParameters as mitigation_params,
Expand Down Expand Up @@ -211,7 +213,9 @@ def _acquisition_circuits(
bell_states=params.bell_states,
thetas=thetas.tolist(),
)

backend = GlobalBackend()
transpiler = dummy_transpiler(backend)
qubit_map = [i for i in range(platform.nqubits)]
if params.apply_error_mitigation:
mitigation_data = mitigation_acquisition(
mitigation_params(pulses=False, nshots=params.nshots), platform, targets
Expand All @@ -232,7 +236,13 @@ def _acquisition_circuits(
native=params.native,
)
for basis, circuit in chsh_circuits.items():
result = circuit(nshots=params.nshots)
_, result = execute_transpiled_circuit(
circuit,
nshots=params.nshots,
transpiler=transpiler,
backend=backend,
qubit_map=qubit_map,
)
frequencies = result.frequencies()
data.register_basis(pair, bell_state, basis, frequencies)

Expand Down
67 changes: 67 additions & 0 deletions tests/test_transpile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import numpy as np
from qibo import Circuit, gates, set_backend
from qibo.backends import GlobalBackend

from qibocal.auto.transpile import (
dummy_transpiler,
execute_transpiled_circuit,
execute_transpiled_circuits,
pad_circuit,
)


def test_padd_circuit():
small_circuit = Circuit(2)
small_circuit.add(gates.X(0))
small_circuit.add(gates.X(1))
qubit_map = [1, 2]
big_circuit = pad_circuit(4, small_circuit, qubit_map)

true_circ = Circuit(4)
true_circ.add(gates.X(1))
true_circ.add(gates.X(2))
assert np.all(true_circ.unitary() == big_circuit.unitary())


def test_execute_transpiled_circuit():

circuit = Circuit(2)
circuit.add(gates.X(0))
circuit.add(gates.X(1))
qubit_map = [1, 2]
set_backend("qibolab", platform="dummy")
backend = GlobalBackend()
transpiler = dummy_transpiler(backend)
transpiled_circuit, _ = execute_transpiled_circuit(
circuit, qubit_map, backend, transpiler=transpiler
)
true_circuit = Circuit(5)
true_circuit.add(gates.GPI2(1, np.pi / 2))
true_circuit.add(gates.GPI2(1, np.pi / 2))
true_circuit.add(gates.GPI2(2, np.pi / 2))
true_circuit.add(gates.GPI2(2, np.pi / 2))
true_circuit.add(gates.Z(1))
true_circuit.add(gates.Z(2))
assert np.all(true_circuit.unitary() == transpiled_circuit.unitary())


def test_execute_transpiled_circuits():

circuit = Circuit(2)
circuit.add(gates.X(0))
circuit.add(gates.X(1))
qubit_map = [1, 2]
set_backend("qibolab", platform="dummy")
backend = GlobalBackend()
transpiler = dummy_transpiler(backend)
transpiled_circuits, _ = execute_transpiled_circuits(
[circuit], [qubit_map], backend, transpiler=transpiler
)
true_circuit = Circuit(5)
true_circuit.add(gates.GPI2(1, np.pi / 2))
true_circuit.add(gates.GPI2(1, np.pi / 2))
true_circuit.add(gates.GPI2(2, np.pi / 2))
true_circuit.add(gates.GPI2(2, np.pi / 2))
true_circuit.add(gates.Z(1))
true_circuit.add(gates.Z(2))
assert np.all(true_circuit.unitary() == transpiled_circuits[0].unitary())

0 comments on commit aad9f40

Please sign in to comment.