diff --git a/recirq/qaoa/placement.py b/recirq/qaoa/placement.py index 76f39b09..1a1b2b50 100644 --- a/recirq/qaoa/placement.py +++ b/recirq/qaoa/placement.py @@ -6,7 +6,7 @@ import networkx as nx import numpy as np import pytket -import pytket.cirq +import pytket.extensions.cirq from pytket.circuit import Node, Qubit from pytket.passes import SequencePass, RoutingPass, PlacementPass from pytket.predicates import CompilationUnit, ConnectivityPredicate @@ -87,7 +87,7 @@ def place_on_device(circuit: cirq.Circuit, initial_map: Initial placement of qubits final_map: The final placement of qubits after action of the circuit """ - tk_circuit = pytket.cirq.cirq_to_tk(circuit) + tk_circuit = pytket.extensions.cirq.cirq_to_tk(circuit) tk_device = _device_to_tket_device(device) unit = CompilationUnit(tk_circuit, [ConnectivityPredicate(tk_device)]) @@ -103,7 +103,7 @@ def place_on_device(circuit: cirq.Circuit, for n1, n2 in unit.initial_map.items()} final_map = {tk_to_cirq_qubit(n1): tk_to_cirq_qubit(n2) for n1, n2 in unit.final_map.items()} - routed_circuit = pytket.cirq.tk_to_cirq(unit.circuit) + routed_circuit = pytket.extensions.cirq.tk_to_cirq(unit.circuit) return routed_circuit, initial_map, final_map diff --git a/recirq/quantum_chess/circuit_transformer.py b/recirq/quantum_chess/circuit_transformer.py index 61b552d1..359cecd7 100644 --- a/recirq/quantum_chess/circuit_transformer.py +++ b/recirq/quantum_chess/circuit_transformer.py @@ -22,19 +22,31 @@ class CircuitTransformer: + """Abstract interface for circuit transformations. + + For example: NamedQubit -> GridQubit transformations. + """ + def __init__(self): + pass + + def transform(self, circuit: cirq.Circuit) -> cirq.Circuit: + """Applies the transformation to the circuit.""" + return None + + +class ConnectivityHeuristicCircuitTransformer(CircuitTransformer): """Optimizer that will transform a circuit using NamedQubits and transform it to use GridQubits. This will use a breadth-first search to find a suitable mapping into the specified grid. It will then transform all operations to use the new qubits. """ - def __init__(self, device: cirq.Device): + super().__init__() self.device = device self.mapping = None self.starting_qubit = self.find_start_qubit(device.qubits) self.qubit_list = device.qubits - super().__init__() def qubits_within(self, depth: int, qubit: cirq.GridQubit, qubit_list: Iterable[cirq.GridQubit]) -> int: @@ -54,7 +66,8 @@ def qubits_within(self, depth: int, qubit: cirq.GridQubit, c += self.qubits_within(depth - 1, qubit + diff, qubit_list) return c - def find_start_qubit(self, qubit_list: List[cirq.Qid], + def find_start_qubit(self, + qubit_list: List[cirq.Qid], depth=3) -> Optional[cirq.GridQubit]: """Finds a reasonable starting qubit to start the mapping. @@ -278,7 +291,7 @@ def qubit_mapping(self, self.mapping = mapping return mapping - def optimize_circuit(self, circuit: cirq.Circuit) -> cirq.Circuit: + def transform(self, circuit: cirq.Circuit) -> cirq.Circuit: """ Creates a new qubit mapping for a circuit and transforms it. This uses `qubit_mapping` to create a mapping from the qubits @@ -299,10 +312,9 @@ class SycamoreDecomposer(cirq.PointOptimizer): Currently supported are controlled ISWAPs with a single control and control-X gates with multiple controls (TOFFOLI gates).:w """ - - def optimization_at(self, circuit: cirq.Circuit, index: int, - op: cirq.Operation - ) -> Optional[cirq.PointOptimizationSummary]: + def optimization_at( + self, circuit: cirq.Circuit, index: int, + op: cirq.Operation) -> Optional[cirq.PointOptimizationSummary]: if len(op.qubits) > 3: raise ValueError(f'Four qubit ops not yet supported: {op}') new_ops = None diff --git a/recirq/quantum_chess/circuit_transformer_test.py b/recirq/quantum_chess/circuit_transformer_test.py index f7bb5d60..49b6514c 100644 --- a/recirq/quantum_chess/circuit_transformer_test.py +++ b/recirq/quantum_chess/circuit_transformer_test.py @@ -1,5 +1,5 @@ # Copyright 2020 Google -# +# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at @@ -33,59 +33,59 @@ @pytest.mark.parametrize('device', (cirq.google.Sycamore23, cirq.google.Sycamore)) def test_single_qubit_ops(device): - transformer = ct.CircuitTransformer(device) + transformer = ct.ConnectivityHeuristicCircuitTransformer(device) c = cirq.Circuit(cirq.X(a1), cirq.X(a2), cirq.X(a3)) transformer.qubit_mapping(c) - c = transformer.optimize_circuit(c) + c = transformer.transform(c) device.validate_circuit(c) @pytest.mark.parametrize('device', (cirq.google.Sycamore23, cirq.google.Sycamore)) def test_single_qubit_with_two_qubits(device): - transformer = ct.CircuitTransformer(device) + transformer = ct.ConnectivityHeuristicCircuitTransformer(device) c = cirq.Circuit(cirq.X(a1), cirq.X(a2), cirq.X(a3), cirq.ISWAP(a3, a4) ** 0.5) transformer.qubit_mapping(c) - device.validate_circuit(transformer.optimize_circuit(c)) + device.validate_circuit(transformer.transform(c)) @pytest.mark.parametrize('device', (cirq.google.Sycamore23, cirq.google.Sycamore)) def test_three_split_moves(device): - transformer = ct.CircuitTransformer(device) + transformer = ct.ConnectivityHeuristicCircuitTransformer(device) c = cirq.Circuit(qm.split_move(a1, a2, b1), qm.split_move(a2, a3, b3), qm.split_move(b1, c1, c2)) transformer.qubit_mapping(c) - device.validate_circuit(transformer.optimize_circuit(c)) + device.validate_circuit(transformer.transform(c)) @pytest.mark.parametrize('device', (cirq.google.Sycamore23, cirq.google.Sycamore)) def test_disconnected(device): - transformer = ct.CircuitTransformer(device) + transformer = ct.ConnectivityHeuristicCircuitTransformer(device) c = cirq.Circuit(qm.split_move(a1, a2, a3), qm.split_move(a3, a4, d1), qm.split_move(b1, b2, b3), qm.split_move(c1, c2, c3)) transformer.qubit_mapping(c) - device.validate_circuit(transformer.optimize_circuit(c)) + device.validate_circuit(transformer.transform(c)) @pytest.mark.parametrize('device', (cirq.google.Sycamore23, cirq.google.Sycamore)) def test_move_around_square(device): - transformer = ct.CircuitTransformer(device) + transformer = ct.ConnectivityHeuristicCircuitTransformer(device) c = cirq.Circuit(qm.normal_move(a1, a2), qm.normal_move(a2, b2), qm.normal_move(b2, b1), qm.normal_move(b1, a1)) transformer.qubit_mapping(c) - device.validate_circuit(transformer.optimize_circuit(c)) + device.validate_circuit(transformer.transform(c)) @pytest.mark.parametrize('device', (cirq.google.Sycamore23, cirq.google.Sycamore)) def test_split_then_merge(device): - transformer = ct.CircuitTransformer(device) + transformer = ct.ConnectivityHeuristicCircuitTransformer(device) c = cirq.Circuit(qm.split_move(a1, a2, b1), qm.split_move(a2, a3, b3), qm.split_move(b1, c1, c2), qm.normal_move(c1, d1), qm.normal_move(a3, a4), qm.merge_move(a4, d1, a1)) transformer.qubit_mapping(c) - device.validate_circuit(transformer.optimize_circuit(c)) + device.validate_circuit(transformer.transform(c)) diff --git a/recirq/quantum_chess/quantum_board.py b/recirq/quantum_chess/quantum_board.py index 6edd31ca..44d48180 100644 --- a/recirq/quantum_chess/quantum_board.py +++ b/recirq/quantum_chess/quantum_board.py @@ -24,7 +24,7 @@ square_to_bit, xy_to_bit, ) -import recirq.quantum_chess.circuit_transformer as circuit_transformer +import recirq.quantum_chess.circuit_transformer as ct import recirq.quantum_chess.enums as enums import recirq.quantum_chess.move as move import recirq.quantum_chess.quantum_moves as qm @@ -60,6 +60,8 @@ class CirqBoard: an error or post-selects the result away. noise_mitigation: Threshold of samples to overcome in order to be considered not noise. + transformer: The CircuitTransformer to use to convert the board's + NamedQubit circuit into a GridQubit circuit. """ def __init__(self, @@ -68,11 +70,14 @@ def __init__(self, device: Optional[cirq.Device] = None, error_mitigation: Optional[ enums.ErrorMitigation] = enums.ErrorMitigation.Nothing, - noise_mitigation: Optional[float] = 0.0): + noise_mitigation: Optional[float] = 0.0, + transformer: Optional[ct.CircuitTransformer] = None): self.device = device self.sampler = sampler if device is not None: - self.transformer = circuit_transformer.CircuitTransformer(device) + self.transformer = ( + transformer + or ct.ConnectivityHeuristicCircuitTransformer(device)) self.with_state(init_basis_state) self.error_mitigation = error_mitigation self.noise_mitigation = noise_mitigation @@ -179,11 +184,9 @@ def sample_with_ancilla(self, num_samples: int # Translate circuit to grid qubits and sqrtISWAP gates if self.device is not None: # Decompose 3-qubit operations - circuit_transformer.SycamoreDecomposer().optimize_circuit( - measure_circuit) + ct.SycamoreDecomposer().optimize_circuit(measure_circuit) # Create NamedQubit to GridQubit mapping and transform - measure_circuit = self.transformer.optimize_circuit( - measure_circuit) + measure_circuit = self.transformer.transform(measure_circuit) # For debug, ensure that the circuit correctly validates self.device.validate_circuit(measure_circuit)