Skip to content

Commit

Permalink
Merge branch 'master' into grbs
Browse files Browse the repository at this point in the history
  • Loading branch information
renatomello committed Aug 14, 2024
2 parents 1c0ed7f + 37e1010 commit 58541ab
Show file tree
Hide file tree
Showing 9 changed files with 87 additions and 22 deletions.
2 changes: 0 additions & 2 deletions src/qibo/backends/numpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -654,9 +654,7 @@ def aggregate_shots(self, shots):
return self.cast(shots, dtype=shots[0].dtype)

def samples_to_binary(self, samples, nqubits):
### This is faster just staying @ NumPy.
qrange = np.arange(nqubits - 1, -1, -1, dtype=np.int32)
samples = self.to_numpy(samples)
return np.mod(np.right_shift(samples[:, None], qrange), 2)

def samples_to_decimal(self, samples, nqubits):
Expand Down
11 changes: 6 additions & 5 deletions src/qibo/measurements.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,11 +167,12 @@ def samples(self, binary=True, registers=False):
# calculate samples for the whole circuit so that
# individual register samples are registered here
self.circuit.final_state.samples()

if binary:
return self._samples
else:
qubits = self.measurement_gate.target_qubits
return self.backend.samples_to_decimal(self._samples, len(qubits))

qubits = self.measurement_gate.target_qubits
return self.backend.samples_to_decimal(self._samples, len(qubits))

def frequencies(self, binary=True, registers=False):
"""Returns the frequencies of measured samples.
Expand All @@ -198,8 +199,8 @@ def frequencies(self, binary=True, registers=False):
if binary:
qubits = self.measurement_gate.target_qubits
return frequencies_to_binary(self._frequencies, len(qubits))
else:
return self._frequencies

return self._frequencies

def apply_bitflips(self, p0, p1=None): # pragma: no cover
return apply_bitflips(self, p0, p1)
7 changes: 1 addition & 6 deletions src/qibo/result.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,12 +325,7 @@ def samples(self, binary: bool = True, registers: bool = False):
if self._samples is None:
if self.measurements[0].result.has_samples():
self._samples = self.backend.np.concatenate(
[
self.backend.cast(
gate.result.samples(), dtype=self.backend.np.int32
)
for gate in self.measurements
],
[gate.result.samples() for gate in self.measurements],
axis=1,
)
else:
Expand Down
9 changes: 9 additions & 0 deletions src/qibo/transpiler/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,15 @@ def remove_block(self, block: "Block"):
"The block you are trying to remove is not present in the circuit blocks.",
)

def return_last_block(self):
"""Return the last block in the circuit blocks."""
if len(self.block_list) == 0:
raise_error(
BlockingError,
"No blocks found in the circuit blocks.",
)
return self.block_list[-1]


def block_decomposition(circuit: Circuit, fuse: bool = True):
"""Decompose a circuit into blocks of gates acting on two qubits.
Expand Down
1 change: 1 addition & 0 deletions src/qibo/transpiler/placer.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@ def __init__(
):
self.connectivity = connectivity
self.routing_algorithm = routing_algorithm
self.routing_algorithm.connectivity = connectivity
self.depth = depth

def __call__(self, circuit: Circuit):
Expand Down
28 changes: 20 additions & 8 deletions src/qibo/transpiler/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,18 @@ def update(self, swap: tuple):
), self._circuit_logical.index(swap[1])
self._circuit_logical[idx_0], self._circuit_logical[idx_1] = swap[1], swap[0]

def undo(self):
"""Undo the last swap. Method works in-place."""
last_swap_block = self._routed_blocks.return_last_block()
swap = tuple(self.physical_to_logical(q) for q in last_swap_block.qubits)
self._routed_blocks.remove_block(last_swap_block)
self._swaps -= 1

idx_0, idx_1 = self._circuit_logical.index(
swap[0]
), self._circuit_logical.index(swap[1])
self._circuit_logical[idx_0], self._circuit_logical[idx_1] = swap[1], swap[0]

def get_logical_qubits(self, block: Block):
"""Returns the current logical qubits where a block is acting on.
Expand Down Expand Up @@ -659,8 +671,7 @@ def __init__(
self.circuit = None
self._memory_map = None
self._final_measurements = None
self._temporary_added_swaps = 0
self._saved_circuit = None
self._temp_added_swaps = []
random.seed(seed)

def __call__(self, circuit: Circuit, initial_layout: dict):
Expand All @@ -674,7 +685,6 @@ def __call__(self, circuit: Circuit, initial_layout: dict):
(:class:`qibo.models.circuit.Circuit`, dict): routed circuit and final layout.
"""
self._preprocessing(circuit=circuit, initial_layout=initial_layout)
self._saved_circuit = deepcopy(self.circuit)
longest_path = np.max(self._dist_matrix)

while self._dag.number_of_nodes() != 0:
Expand All @@ -687,9 +697,12 @@ def __call__(self, circuit: Circuit, initial_layout: dict):
# If the number of added swaps is too high, the algorithm is stuck.
# Reset the circuit to the last saved state and make the nearest gate executable by manually adding SWAPs.
if (
self._temporary_added_swaps > self.swap_threshold * longest_path
len(self._temp_added_swaps) > self.swap_threshold * longest_path
): # threshold is arbitrary
self.circuit = deepcopy(self._saved_circuit)
while self._temp_added_swaps:
swap = self._temp_added_swaps.pop()
self.circuit.undo()
self._temp_added_swaps = []
self._shortest_path_routing()

circuit_kwargs = circuit.init_kwargs
Expand Down Expand Up @@ -800,7 +813,7 @@ def _find_new_mapping(self):
for qubit in self.circuit.logical_to_physical(best_candidate, index=True):
self._delta_register[qubit] += self.delta
self.circuit.update(best_candidate)
self._temporary_added_swaps += 1
self._temp_added_swaps.append(best_candidate)

def _compute_cost(self, candidate: int):
"""Compute the cost associated to a possible SWAP candidate."""
Expand Down Expand Up @@ -897,8 +910,7 @@ def _execute_blocks(self, blocklist: list):
self._update_front_layer()
self._memory_map = []
self._delta_register = [1.0 for _ in self._delta_register]
self._temporary_added_swaps = 0
self._saved_circuit = deepcopy(self.circuit)
self._temp_added_swaps = []

def _shortest_path_routing(self):
"""Route a gate in the front layer using the shortest path. This method is executed when the standard SABRE fails to find an optimized solution.
Expand Down
24 changes: 24 additions & 0 deletions tests/test_transpiler_blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,3 +367,27 @@ def test_block_on_qubits():
assert new_block.gates[2].qubits == (3,)
assert new_block.gates[3].qubits == (3, 2)
assert new_block.gates[4].qubits == (3,)


def test_return_last_block():
circ = Circuit(4)
circ.add(gates.CZ(0, 1))
circ.add(gates.CZ(1, 3))
circ.add(gates.CZ(1, 2))
circ.add(gates.CZ(2, 3))
circuit_blocks = CircuitBlocks(circ)
last_block = circuit_blocks.return_last_block()
assert_gates_equality(last_block.gates, [gates.CZ(2, 3)])

circuit_blocks.remove_block(last_block)
last_block_2 = circuit_blocks.return_last_block()
assert_gates_equality(last_block_2.gates, [gates.CZ(1, 2)])


def test_return_last_block_error():
circ = Circuit(4)
circuit_blocks = CircuitBlocks(circ)

# No blocks in the circuit
with pytest.raises(BlockingError):
last_block = circuit_blocks.return_last_block()
2 changes: 1 addition & 1 deletion tests/test_transpiler_pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ def test_custom_passes(placer, routing, gates, qubits):
@pytest.mark.parametrize("gates", [5, 20])
@pytest.mark.parametrize("placer", [Random, Trivial, ReverseTraversal])
@pytest.mark.parametrize("routing", [ShortestPaths, Sabre])
def test_custom_passes_restict(gates, placer, routing):
def test_custom_passes_restrict(gates, placer, routing):
circ = generate_random_circuit(nqubits=3, ngates=gates)
custom_passes = []
custom_passes.append(Preprocessing(connectivity=star_connectivity()))
Expand Down
25 changes: 25 additions & 0 deletions tests/test_transpiler_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from qibo.models import Circuit
from qibo.quantum_info.random_ensembles import random_unitary
from qibo.transpiler._exceptions import ConnectivityError
from qibo.transpiler.blocks import Block
from qibo.transpiler.optimizer import Preprocessing
from qibo.transpiler.pipeline import (
assert_circuit_equivalence,
Expand Down Expand Up @@ -472,3 +473,27 @@ def test_star_router(nqubits, depth, middle_qubit, measurements, unitaries):
final_map=final_qubit_map,
initial_map=initial_layout,
)


def test_undo():
circ = Circuit(4)
initial_layout = {"q0": 0, "q1": 1, "q2": 2, "q3": 3}
circuit_map = CircuitMap(initial_layout=initial_layout, circuit=circ)

# Two SWAP gates are added
circuit_map.update((1, 2))
circuit_map.update((2, 3))
assert circuit_map._circuit_logical == [0, 3, 1, 2]
assert len(circuit_map._routed_blocks.block_list) == 2

# Undo the last SWAP gate
circuit_map.undo()
assert circuit_map._circuit_logical == [0, 2, 1, 3]
assert circuit_map._swaps == 1
assert len(circuit_map._routed_blocks.block_list) == 1

# Undo the first SWAP gate
circuit_map.undo()
assert circuit_map._circuit_logical == [0, 1, 2, 3]
assert circuit_map._swaps == 0
assert len(circuit_map._routed_blocks.block_list) == 0

0 comments on commit 58541ab

Please sign in to comment.