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

Use of wire_names #1079

Merged
merged 9 commits into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions doc/source/tutorials/compiler.rst
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,10 @@ The following example shows how to modify the compiler in order to execute a cir


# define a compiler rule that translates X to the pi-pulse
def x_rule(gate, platform):
def x_rule(qubits_ids, platform, parameters=None):
"""X gate applied with a single pi-pulse."""
qubit = gate.target_qubits[0]
sequence = PulseSequence()
sequence.add(platform.create_RX_pulse(qubit, start=0))
sequence.add(platform.create_RX_pulse(qubits_ids[1][0], start=0))
return sequence, {}


Expand Down
11 changes: 11 additions & 0 deletions src/qibolab/backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,11 @@ def execute_circuit(self, circuit, initial_state=None, nshots=1000):
"Hardware backend only supports circuits as initial states.",
)

# This should be done in qibo side
# Temporary fix: overwrite the wire names
if not all(q in self.qubits for q in circuit.wire_names):
circuit._wire_names = self.qubits[: circuit.nqubits]

sequence, measurement_map = self.compiler.compile(circuit, self.platform)

if not self.platform.is_connected:
Expand Down Expand Up @@ -167,6 +172,12 @@ def execute_circuits(self, circuits, initial_states=None, nshots=1000):
"Hardware backend only supports circuits as initial states.",
)

# This should be done in qibo side
# Temporary fix: overwrite the wire names
for circuit in circuits:
if not all(q in self.qubits for q in circuit.wire_names):
circuit._wire_names = self.qubits[: circuit.nqubits]

# TODO: Maybe these loops can be parallelized
sequences, measurement_maps = zip(
*(self.compiler.compile(circuit, self.platform) for circuit in circuits)
Expand Down
24 changes: 21 additions & 3 deletions src/qibolab/compilers/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,24 @@ def inner(func):
return inner

def _compile_gate(
self, gate, platform, sequence, virtual_z_phases, moment_start, delays
self,
gate,
platform,
sequence,
virtual_z_phases,
moment_start,
delays,
wire_names,
):
"""Adds a single gate to the pulse sequence."""
rule = self[gate.__class__]

# get local sequence and phases for the current gate
gate_sequence, gate_phases = rule(gate, platform)
qubits_ids = (
[wire_names[qubit] for qubit in gate.control_qubits],
[wire_names[qubit] for qubit in gate.target_qubits],
)
gate_sequence, gate_phases = rule(qubits_ids, platform, gate.parameters)

# update global pulse sequence
# determine the right start time based on the availability of the qubits involved
Expand Down Expand Up @@ -154,7 +166,13 @@ def compile(self, circuit, platform):
delays[qubit] += gate.delay
continue
gate_sequence, gate_phases = self._compile_gate(
gate, platform, sequence, virtual_z_phases, moment_start, delays
gate,
platform,
sequence,
virtual_z_phases,
moment_start,
delays,
circuit.wire_names,
)
for qubit in gate.qubits:
delays[qubit] = 0
Expand Down
46 changes: 22 additions & 24 deletions src/qibolab/compilers/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,52 +8,48 @@
from qibolab.pulses import PulseSequence


def identity_rule(gate, platform):
def identity_rule(qubits_ids, platform, parameters=None):
"""Identity gate skipped."""
return PulseSequence(), {}


def z_rule(gate, platform):
def z_rule(qubits_ids, platform, parameters=None):
"""Z gate applied virtually."""
qubit = list(platform.qubits)[gate.target_qubits[0]]
return PulseSequence(), {qubit: math.pi}
return PulseSequence(), {qubits_ids[1][0]: math.pi}


def rz_rule(gate, platform):
def rz_rule(qubits_ids, platform, parameters=None):
"""RZ gate applied virtually."""
qubit = list(platform.qubits)[gate.target_qubits[0]]
return PulseSequence(), {qubit: -gate.parameters[0]}
return PulseSequence(), {qubits_ids[1][0]: -parameters[0]}


def gpi2_rule(gate, platform):
def gpi2_rule(qubits_ids, platform, parameters=None):
"""Rule for GPI2."""
qubit = list(platform.qubits)[gate.target_qubits[0]]
theta = gate.parameters[0]
theta = parameters[0]
sequence = PulseSequence()
pulse = platform.create_RX90_pulse(qubit, start=0, relative_phase=theta)
pulse = platform.create_RX90_pulse(qubits_ids[1][0], start=0, relative_phase=theta)
sequence.add(pulse)
return sequence, {}


def gpi_rule(gate, platform):
def gpi_rule(qubits_ids, platform, parameters=None):
"""Rule for GPI."""
qubit = list(platform.qubits)[gate.target_qubits[0]]
theta = gate.parameters[0]
theta = parameters[0]
sequence = PulseSequence()
# the following definition has a global phase difference compare to
# to the matrix representation. See
# https://github.com/qiboteam/qibolab/pull/804#pullrequestreview-1890205509
# for more detail.
pulse = platform.create_RX_pulse(qubit, start=0, relative_phase=theta)
pulse = platform.create_RX_pulse(qubits_ids[1][0], start=0, relative_phase=theta)
sequence.add(pulse)
return sequence, {}


def u3_rule(gate, platform):
def u3_rule(qubits_ids, platform, parameters=None):
"""U3 applied as RZ-RX90-RZ-RX90-RZ."""
qubit = list(platform.qubits)[gate.target_qubits[0]]
qubit = qubits_ids[1][0]
# Transform gate to U3 and add pi/2-pulses
theta, phi, lam = gate.parameters
theta, phi, lam = parameters
# apply RZ(lam)
virtual_z_phases = {qubit: lam}
sequence = PulseSequence()
Expand All @@ -79,24 +75,26 @@ def u3_rule(gate, platform):
return sequence, virtual_z_phases


def cz_rule(gate, platform):
def cz_rule(qubits_ids, platform, parameters=None):
"""CZ applied as defined in the platform runcard.

Applying the CZ gate may involve sending pulses on qubits that the
gate is not directly acting on.
"""
return platform.create_CZ_pulse_sequence(gate.qubits)
qubits = qubits_ids[0] + qubits_ids[1]
return platform.create_CZ_pulse_sequence(qubits)


def cnot_rule(gate, platform):
def cnot_rule(qubits_ids, platform, parameters=None):
"""CNOT applied as defined in the platform runcard."""
return platform.create_CNOT_pulse_sequence(gate.qubits)
qubits = qubits_ids[0] + qubits_ids[1]
return platform.create_CNOT_pulse_sequence(qubits)


def measurement_rule(gate, platform):
def measurement_rule(qubits_ids, platform, parameters=None):
"""Measurement gate applied using the platform readout pulse."""
sequence = PulseSequence()
for qubit in gate.target_qubits:
for qubit in qubits_ids[1]:
MZ_pulse = platform.create_MZ_pulse(qubit, start=0)
sequence.add(MZ_pulse)
return sequence, {}
15 changes: 13 additions & 2 deletions tests/test_backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@
from qibolab.backends import QibolabBackend


def generate_circuit_with_gate(nqubits, gate, **kwargs):
def generate_circuit_with_gate(nqubits, gate, names, **kwargs):
circuit = Circuit(nqubits)
circuit.add(gate(qubit, **kwargs) for qubit in range(nqubits))
circuit.add(gates.M(*range(nqubits)))
circuit._wire_names = names
return circuit


Expand Down Expand Up @@ -86,6 +87,7 @@ def test_execute_circuit_initial_state():
circuit = Circuit(1)
circuit.add(gates.GPI2(0, phi=0))
circuit.add(gates.M(0))
circuit._wire_names = [0]
with pytest.raises(ValueError):
backend.execute_circuit(circuit, initial_state=np.ones(2))

Expand All @@ -107,7 +109,7 @@ def test_execute_circuit_initial_state():
def test_execute_circuit(gate, kwargs):
backend = QibolabBackend("dummy")
nqubits = backend.platform.nqubits
circuit = generate_circuit_with_gate(nqubits, gate, **kwargs)
circuit = generate_circuit_with_gate(nqubits, gate, list(range(nqubits)), **kwargs)
result = backend.execute_circuit(circuit, nshots=100)


Expand All @@ -117,12 +119,14 @@ def test_measurement_samples():

circuit = Circuit(nqubits)
circuit.add(gates.M(*range(nqubits)))
circuit._wire_names = list(range(nqubits))
result = backend.execute_circuit(circuit, nshots=100)
assert result.samples().shape == (100, nqubits)
assert sum(result.frequencies().values()) == 100

circuit = Circuit(nqubits)
circuit.add(gates.M(0, 2))
circuit._wire_names = list(range(nqubits))
result = backend.execute_circuit(circuit, nshots=100)
assert result.samples().shape == (100, 2)
assert sum(result.frequencies().values()) == 100
Expand All @@ -135,6 +139,7 @@ def test_execute_circuits():
circuit = Circuit(3)
circuit.add(gates.GPI2(i, phi=np.pi / 2) for i in range(3))
circuit.add(gates.M(0, 1, 2))
circuit._wire_names = list(range(3))

results = backend.execute_circuits(
5 * [circuit], initial_states=initial_state_circuit, nshots=100
Expand All @@ -151,6 +156,7 @@ def test_multiple_measurements():
circuit = Circuit(4)
circuit.add(gates.GPI2(i, phi=np.pi / 2) for i in range(2))
circuit.add(gates.CZ(1, 2))
circuit._wire_names = list(range(4))
res0 = circuit.add(gates.M(0))
res1 = circuit.add(gates.M(3))
res2 = circuit.add(gates.M(1))
Expand All @@ -171,6 +177,7 @@ def dummy_string_qubit_names():
platform.pairs = {
(f"A{q0}", f"A{q1}"): pair for (q0, q1), pair in platform.pairs.items()
}
platform.wire_names = [f"A{q}" for q in range(platform.nqubits)]
return platform


Expand All @@ -182,6 +189,7 @@ def test_execute_circuit_str_qubit_names():
circuit.add(gates.GPI2(i, phi=np.pi / 2) for i in range(2))
circuit.add(gates.CZ(1, 2))
circuit.add(gates.M(0, 1))
circuit._wire_names = ["A0", "A1", "A2"]
result = backend.execute_circuit(circuit, nshots=20)
assert result.samples().shape == (20, 2)

Expand All @@ -195,6 +203,7 @@ def test_ground_state_probabilities_circuit(connected_backend):
nqubits = connected_backend.platform.nqubits
circuit = Circuit(nqubits)
circuit.add(gates.M(*range(nqubits)))
circuit._wire_names = list(range(nqubits))
result = connected_backend.execute_circuit(circuit, nshots=nshots)
freqs = result.frequencies(binary=False)
probs = [freqs[i] / nshots for i in range(2**nqubits)]
Expand All @@ -214,6 +223,7 @@ def test_excited_state_probabilities_circuit(connected_backend):
circuit = Circuit(nqubits)
circuit.add(gates.X(q) for q in range(nqubits))
circuit.add(gates.M(*range(nqubits)))
circuit._wire_names = list(range(nqubits))
result = connected_backend.execute_circuit(circuit, nshots=nshots)
freqs = result.frequencies(binary=False)
probs = [freqs[i] / nshots for i in range(2**nqubits)]
Expand All @@ -237,6 +247,7 @@ def test_superposition_for_all_qubits(connected_backend):
circuit = Circuit(nqubits)
circuit.add(gates.GPI2(q=q, phi=np.pi / 2))
circuit.add(gates.M(q))
circuit._wire_names = list(range(nqubits))
freqs = connected_backend.execute_circuit(circuit, nshots=nshots).frequencies(
binary=False
)
Expand Down
2 changes: 2 additions & 0 deletions tests/test_compilers_default.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ def test_u3_sim_agreement():
def compile_circuit(circuit, platform):
"""Compile a circuit to a pulse sequence."""
compiler = Compiler.default()
# Temporary fix: overwrite the wire names
circuit._wire_names = list(platform.qubits)
sequence, _ = compiler.compile(circuit, platform)
return sequence

Expand Down
Loading