diff --git a/CHANGELOG.rst b/CHANGELOG.rst index abab2da0b..c7db812d7 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,11 @@ Changelog ========= +Version 15.6 +============ + +* CZ gates now appear in the transpilation target only in the direction they are calibrated. `#140 `_ + Version 15.5 ============ diff --git a/src/iqm/qiskit_iqm/fake_backends/fake_adonis.py b/src/iqm/qiskit_iqm/fake_backends/fake_adonis.py index 646883003..e211ca94c 100644 --- a/src/iqm/qiskit_iqm/fake_backends/fake_adonis.py +++ b/src/iqm/qiskit_iqm/fake_backends/fake_adonis.py @@ -13,24 +13,19 @@ # limitations under the License. """Fake backend for IQM's 5-qubit Adonis architecture. """ -from iqm.iqm_client import QuantumArchitectureSpecification -from iqm.qiskit_iqm.fake_backends.iqm_fake_backend import IQMErrorProfile, IQMFakeBackend +from iqm.qiskit_iqm.fake_backends.iqm_fake_backend import ( + IQMErrorProfile, + IQMFakeBackend, + generate_architecture_from_lists, +) def IQMFakeAdonis() -> IQMFakeBackend: """Return IQMFakeBackend instance representing IQM's Adonis architecture.""" - architecture = QuantumArchitectureSpecification( - name="Adonis", - operations={ - "prx": [["QB1"], ["QB2"], ["QB3"], ["QB4"], ["QB5"]], - "cc_prx": [["QB1"], ["QB2"], ["QB3"], ["QB4"], ["QB5"]], - "cz": [["QB1", "QB3"], ["QB2", "QB3"], ["QB4", "QB3"], ["QB5", "QB3"]], - "measure": [["QB1"], ["QB2"], ["QB3"], ["QB4"], ["QB5"]], - "barrier": [], - }, - qubits=["QB1", "QB2", "QB3", "QB4", "QB5"], - qubit_connectivity=[["QB1", "QB3"], ["QB2", "QB3"], ["QB3", "QB4"], ["QB3", "QB5"]], - ) + + qubits = ["QB1", "QB2", "QB3", "QB4", "QB5"] + qubit_connectivity = [["QB1", "QB3"], ["QB2", "QB3"], ["QB3", "QB4"], ["QB3", "QB5"]] + architecture = generate_architecture_from_lists(qubits, qubit_connectivity) error_profile = IQMErrorProfile( t1s={"QB1": 27000.0, "QB2": 33000.0, "QB3": 25000.0, "QB4": 40000.0, "QB5": 25000.0}, t2s={"QB1": 20000.0, "QB2": 26000.0, "QB3": 23000.0, "QB4": 26000.0, "QB5": 7000.0}, diff --git a/src/iqm/qiskit_iqm/fake_backends/fake_aphrodite.py b/src/iqm/qiskit_iqm/fake_backends/fake_aphrodite.py index 02048d15a..45b10f03b 100644 --- a/src/iqm/qiskit_iqm/fake_backends/fake_aphrodite.py +++ b/src/iqm/qiskit_iqm/fake_backends/fake_aphrodite.py @@ -13,8 +13,11 @@ # limitations under the License. """Fake (i.e. simulated) backend for IQM's 54-qubit Aphrodite architecture """ -from iqm.iqm_client import QuantumArchitectureSpecification -from iqm.qiskit_iqm.fake_backends.iqm_fake_backend import IQMErrorProfile, IQMFakeBackend +from iqm.qiskit_iqm.fake_backends.iqm_fake_backend import ( + IQMErrorProfile, + IQMFakeBackend, + generate_architecture_from_lists, +) def IQMFakeAphrodite() -> IQMFakeBackend: @@ -168,17 +171,7 @@ def IQMFakeAphrodite() -> IQMFakeBackend: ["QB52", "QB53"], ["QB53", "QB54"], ] - architecture = QuantumArchitectureSpecification( - name="Aphrodite", - operations={ - "prx": [[q] for q in qubits], - "cz": list(qubit_connectivity), - "measure": [[q] for q in qubits], - "barrier": [], - }, - qubits=qubits, - qubit_connectivity=qubit_connectivity, - ) + architecture = generate_architecture_from_lists(qubits, qubit_connectivity) error_profile = IQMErrorProfile( t1s={ diff --git a/src/iqm/qiskit_iqm/fake_backends/fake_apollo.py b/src/iqm/qiskit_iqm/fake_backends/fake_apollo.py index a3708d250..7708f87cf 100644 --- a/src/iqm/qiskit_iqm/fake_backends/fake_apollo.py +++ b/src/iqm/qiskit_iqm/fake_backends/fake_apollo.py @@ -13,8 +13,11 @@ # limitations under the License. """Fake (i.e. simulated) backend for IQM's 20-qubit Apollo architecture """ -from iqm.iqm_client import QuantumArchitectureSpecification -from iqm.qiskit_iqm.fake_backends.iqm_fake_backend import IQMErrorProfile, IQMFakeBackend +from iqm.qiskit_iqm.fake_backends.iqm_fake_backend import ( + IQMErrorProfile, + IQMFakeBackend, + generate_architecture_from_lists, +) def IQMFakeApollo() -> IQMFakeBackend: @@ -73,17 +76,7 @@ def IQMFakeApollo() -> IQMFakeBackend: ["QB18", "QB19"], ["QB19", "QB20"], ] - architecture = QuantumArchitectureSpecification( - name="Apollo", - operations={ - "prx": [[q] for q in qubits], - "cz": list(qubit_connectivity), - "measure": [[q] for q in qubits], - "barrier": [], - }, - qubits=qubits, - qubit_connectivity=qubit_connectivity, - ) + architecture = generate_architecture_from_lists(qubits, qubit_connectivity) # Note that these specs are ballpark numbers and don't correspond directly to a specific device error_profile = IQMErrorProfile( t1s={ diff --git a/src/iqm/qiskit_iqm/fake_backends/iqm_fake_backend.py b/src/iqm/qiskit_iqm/fake_backends/iqm_fake_backend.py index 5d5727c8c..b30ab21eb 100644 --- a/src/iqm/qiskit_iqm/fake_backends/iqm_fake_backend.py +++ b/src/iqm/qiskit_iqm/fake_backends/iqm_fake_backend.py @@ -394,3 +394,26 @@ def validate_compatible_architecture(self, architecture: DynamicQuantumArchitect connectivity_match = self_connectivity == target_connectivity return components_match and ops_match and connectivity_match + + +def generate_architecture_from_lists( + qubits: list[str], qubit_connectivity: list[list[str]] +) -> QuantumArchitectureSpecification: + """Generate a Dynamic Quantum Architecture from lists of qubits and qubit connectivity. + + Args: + qubits: list of qubit names + qubit_connectivity: list of lists of qubit pairs representing connectivity + """ + return QuantumArchitectureSpecification( + name="Adonis", + operations={ + "prx": [[q] for q in qubits], + "cc_prx": [[q] for q in qubits], + "cz": list(qubit_connectivity), + "measure": [[q] for q in qubits], + "barrier": [], + }, + qubits=qubits, + qubit_connectivity=qubit_connectivity, + ) diff --git a/src/iqm/qiskit_iqm/iqm_backend.py b/src/iqm/qiskit_iqm/iqm_backend.py index 165b14ac2..2bb5ee2f3 100644 --- a/src/iqm/qiskit_iqm/iqm_backend.py +++ b/src/iqm/qiskit_iqm/iqm_backend.py @@ -118,7 +118,7 @@ def _create_properties( if 'prx' in operations: target.add_instruction(RGate(Parameter('theta'), Parameter('phi')), _create_properties('prx')) if 'cz' in operations: - target.add_instruction(CZGate(), _create_properties('cz', symmetric=True)) + target.add_instruction(CZGate(), _create_properties('cz')) if 'move' in operations: target.add_instruction(MoveGate(), _create_properties('move')) if 'cc_prx' in operations: diff --git a/tests/conftest.py b/tests/conftest.py index 5faa73c08..689d111fb 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -86,7 +86,7 @@ def adonis_architecture(): 'cz': GateInfo( implementations={ 'tgss': GateImplementationInfo( - loci=(('QB1', 'QB3'), ('QB2', 'QB3'), ('QB4', 'QB3'), ('QB5', 'QB3')) + loci=(('QB1', 'QB3'), ('QB2', 'QB3'), ('QB3', 'QB4'), ('QB3', 'QB5')) ) }, default_implementation='tgss', @@ -128,7 +128,7 @@ def adonis_shuffled_names_architecture(): 'cz': GateInfo( implementations={ 'tgss': GateImplementationInfo( - loci=(('QB1', 'QB3'), ('QB2', 'QB3'), ('QB4', 'QB3'), ('QB5', 'QB3')) + loci=(('QB1', 'QB3'), ('QB2', 'QB3'), ('QB3', 'QB4'), ('QB3', 'QB5')) ) }, default_implementation='tgss', @@ -198,12 +198,12 @@ def move_architecture(): @pytest.fixture def adonis_coupling_map(): - return {(0, 2), (2, 0), (1, 2), (2, 1), (2, 3), (3, 2), (2, 4), (4, 2)} + return {(0, 2), (1, 2), (2, 3), (2, 4)} @pytest.fixture def deneb_coupling_map(): - return {(1, 0), (0, 1), (2, 0), (0, 2), (3, 0), (0, 3), (4, 0), (0, 4), (5, 0), (0, 5), (6, 0), (0, 6)} + return {(1, 0), (2, 0), (3, 0), (4, 0), (5, 0), (6, 0)} @pytest.fixture @@ -265,255 +265,3 @@ def ndonis_architecture(): ), }, ) - - -@pytest.fixture -def apollo_coupling_map(): - return { - (0, 1), - (1, 0), - (0, 3), - (3, 0), - (1, 4), - (4, 1), - (2, 3), - (3, 2), - (7, 2), - (2, 7), - (3, 4), - (4, 3), - (8, 3), - (3, 8), - (4, 5), - (5, 4), - (9, 4), - (4, 9), - (5, 6), - (6, 5), - (10, 5), - (5, 10), - (11, 6), - (6, 11), - (7, 8), - (8, 7), - (7, 12), - (12, 7), - (8, 9), - (9, 8), - (8, 13), - (13, 8), - (9, 10), - (10, 9), - (9, 14), - (14, 9), - (10, 11), - (11, 10), - (15, 10), - (10, 15), - (16, 11), - (11, 16), - (12, 13), - (13, 12), - (13, 14), - (14, 13), - (17, 13), - (13, 17), - (15, 14), - (14, 15), - (18, 14), - (14, 18), - (15, 16), - (16, 15), - (15, 19), - (19, 15), - (17, 18), - (18, 17), - (18, 19), - (19, 18), - } - - -@pytest.fixture -def aphrodite_coupling_map(): - return { - (0, 1), - (1, 0), - (0, 4), - (4, 0), - (1, 5), - (5, 1), - (2, 3), - (3, 2), - (2, 8), - (8, 2), - (3, 4), - (4, 3), - (3, 9), - (9, 3), - (4, 5), - (5, 4), - (4, 10), - (10, 4), - (5, 6), - (6, 5), - (5, 11), - (11, 5), - (6, 12), - (12, 6), - (7, 8), - (8, 7), - (7, 15), - (15, 7), - (8, 9), - (9, 8), - (8, 16), - (16, 8), - (9, 10), - (10, 9), - (9, 17), - (17, 9), - (10, 11), - (11, 10), - (10, 18), - (18, 10), - (11, 12), - (12, 11), - (11, 19), - (19, 11), - (12, 13), - (13, 12), - (12, 20), - (20, 12), - (13, 21), - (21, 13), - (14, 15), - (15, 14), - (14, 22), - (22, 14), - (15, 16), - (16, 15), - (15, 23), - (23, 15), - (16, 17), - (17, 16), - (16, 24), - (24, 16), - (17, 18), - (18, 17), - (17, 25), - (25, 17), - (18, 19), - (19, 18), - (18, 26), - (26, 18), - (19, 20), - (20, 19), - (19, 27), - (27, 19), - (20, 21), - (21, 20), - (20, 28), - (28, 20), - (21, 29), - (29, 21), - (22, 23), - (23, 22), - (23, 24), - (24, 23), - (23, 31), - (31, 23), - (24, 25), - (25, 24), - (24, 32), - (32, 24), - (25, 26), - (26, 25), - (25, 33), - (33, 25), - (26, 27), - (27, 26), - (26, 34), - (34, 26), - (27, 28), - (28, 27), - (27, 35), - (35, 27), - (28, 29), - (29, 28), - (28, 36), - (36, 28), - (29, 30), - (30, 29), - (29, 37), - (37, 29), - (30, 38), - (38, 30), - (31, 32), - (32, 31), - (31, 39), - (39, 31), - (32, 33), - (33, 32), - (32, 40), - (40, 32), - (33, 34), - (34, 33), - (33, 41), - (41, 33), - (34, 35), - (35, 34), - (34, 42), - (42, 34), - (35, 36), - (36, 35), - (35, 43), - (43, 35), - (36, 37), - (37, 36), - (36, 44), - (44, 36), - (37, 38), - (38, 37), - (37, 45), - (45, 37), - (39, 40), - (40, 39), - (40, 41), - (41, 40), - (40, 46), - (46, 40), - (41, 42), - (42, 41), - (41, 47), - (47, 41), - (42, 43), - (43, 42), - (42, 48), - (48, 42), - (43, 44), - (44, 43), - (43, 49), - (49, 43), - (44, 45), - (45, 44), - (44, 50), - (50, 44), - (46, 47), - (47, 46), - (47, 48), - (48, 47), - (47, 51), - (51, 47), - (48, 49), - (49, 48), - (48, 52), - (52, 48), - (49, 50), - (50, 49), - (49, 53), - (53, 49), - (51, 52), - (52, 51), - (52, 53), - (53, 52), - } diff --git a/tests/fake_backends/test_fake_aphrodite.py b/tests/fake_backends/test_fake_aphrodite.py index 060488ac8..3030ac8aa 100644 --- a/tests/fake_backends/test_fake_aphrodite.py +++ b/tests/fake_backends/test_fake_aphrodite.py @@ -17,6 +17,7 @@ from qiskit_aer.noise.noise_model import NoiseModel from iqm.qiskit_iqm import IQMFakeAphrodite +from tests.fake_backends.test_fake_apollo import connectivity_to_coupling_map def test_iqm_fake_aphrodite(): @@ -25,8 +26,10 @@ def test_iqm_fake_aphrodite(): assert backend.name == 'IQMFakeAphroditeBackend' -def test_iqm_fake_aphrodite_connectivity(aphrodite_coupling_map): +def test_iqm_fake_aphrodite_connectivity(): backend = IQMFakeAphrodite() + # for current fake backends, cz connectivity is the same as the QPU connectivity + aphrodite_coupling_map = connectivity_to_coupling_map(backend.architecture.gates['cz'].loci) assert set(backend.coupling_map.get_edges()) == aphrodite_coupling_map diff --git a/tests/fake_backends/test_fake_apollo.py b/tests/fake_backends/test_fake_apollo.py index ad9ee1e5c..4c3e0a3b9 100644 --- a/tests/fake_backends/test_fake_apollo.py +++ b/tests/fake_backends/test_fake_apollo.py @@ -19,14 +19,21 @@ from iqm.qiskit_iqm import IQMFakeApollo +def connectivity_to_coupling_map(connectivity: list[list[str]]) -> set[tuple[int, int]]: + """Convert IQMFakeBackend qubit names "QB{i}" to Qiskit indices.""" + return {tuple(int(q[2:]) - 1 for q in pair) for pair in connectivity} + + def test_iqm_fake_apollo(): backend = IQMFakeApollo() assert backend.num_qubits == 20 assert backend.name == 'IQMFakeApolloBackend' -def test_iqm_fake_apollo_connectivity(apollo_coupling_map): +def test_iqm_fake_apollo_connectivity(): backend = IQMFakeApollo() + # for current fake backends, cz connectivity is the same as the QPU connectivity + apollo_coupling_map = connectivity_to_coupling_map(backend.architecture.gates['cz'].loci) assert set(backend.coupling_map.get_edges()) == apollo_coupling_map diff --git a/tests/move_architecture/test_architecture.py b/tests/move_architecture/test_architecture.py index 23539eda2..324cce2e3 100644 --- a/tests/move_architecture/test_architecture.py +++ b/tests/move_architecture/test_architecture.py @@ -36,9 +36,7 @@ def test_backend_configuration_new(move_architecture): check_instruction(backend.instructions, 'r', [(1,), (2,), (3,), (4,), (5,), (6,)]) check_instruction(backend.instructions, 'measure', [(1,), (2,), (3,), (4,), (5,), (6,)]) check_instruction(backend.instructions, 'id', [(0,), (1,), (2,), (3,), (4,), (5,), (6,)]) - check_instruction( - backend.instructions, 'cz', [(1, 0), (0, 1), (2, 0), (0, 2), (3, 0), (0, 3), (4, 0), (0, 4), (5, 0), (0, 5)] - ) + check_instruction(backend.instructions, 'cz', [(i, 0) for i in range(1, 6)]) check_instruction(backend.instructions, 'move', [(6, 0)]) @@ -58,7 +56,7 @@ def test_backend_configuration_adonis(adonis_architecture): check_instruction(backend.instructions, 'r', [(0,), (1,), (2,), (3,), (4,)]) check_instruction(backend.instructions, 'measure', [(0,), (1,), (2,), (3,), (4,)]) check_instruction(backend.instructions, 'id', [(0,), (1,), (2,), (3,), (4,)]) - check_instruction(backend.instructions, 'cz', [(0, 2), (2, 0), (1, 2), (2, 1), (3, 2), (2, 3), (4, 2), (2, 4)]) + check_instruction(backend.instructions, 'cz', [(0, 2), (1, 2), (2, 3), (2, 4)]) def check_instruction( diff --git a/tests/move_architecture/test_move_circuit.py b/tests/move_architecture/test_move_circuit.py index 9a94f8a53..c5dcd80c9 100644 --- a/tests/move_architecture/test_move_circuit.py +++ b/tests/move_architecture/test_move_circuit.py @@ -32,7 +32,7 @@ def test_move_gate_trivial_layout(move_architecture): submitted_circuit = get_transpiled_circuit_json(qc, move_architecture) assert [describe_instruction(i) for i in submitted_circuit.instructions] == [ 'move:6,0', - 'cz:0,3', + 'cz:3,0', 'cz:2,0', 'move:6,0', ] @@ -56,10 +56,10 @@ def test_mapped_move_qubit(move_architecture): """ qc = QuantumCircuit(7) qc.append(MoveGate(), [3, 0]) - qc.cz(0, 2) + qc.cz(2, 0) qc.append(MoveGate(), [3, 0]) submitted_circuit = get_transpiled_circuit_json(qc, move_architecture, create_move_layout=True) - assert [describe_instruction(i) for i in submitted_circuit.instructions] == ['move:6,0', 'cz:0,2', 'move:6,0'] + assert [describe_instruction(i) for i in submitted_circuit.instructions] == ['move:6,0', 'cz:2,0', 'move:6,0'] def test_mapped_move_qubit_and_resonator(move_architecture): @@ -73,10 +73,10 @@ def test_mapped_move_qubit_and_resonator(move_architecture): qc.h(5) submitted_circuit = get_transpiled_circuit_json(qc, move_architecture, create_move_layout=True) assert [describe_instruction(i) for i in submitted_circuit.instructions] == [ - 'cz:0,4', + 'cz:4,0', 'move:6,0', - 'cz:0,1', - 'cz:0,2', + 'cz:1,0', + 'cz:2,0', 'move:6,0', 'prx:6', 'prx:6', @@ -120,7 +120,7 @@ def test_transpiled_circuit(move_architecture): # move(6, 0) 'move:6,0', # cz(0, 3) - 'cz:0,3', + 'cz:3,0', # cz(4, 0) is optimized before h(6) 'cz:4,0', # h(6) diff --git a/tests/test_iqm_provider.py b/tests/test_iqm_provider.py index 69149e6ef..5b9d6af59 100644 --- a/tests/test_iqm_provider.py +++ b/tests/test_iqm_provider.py @@ -55,7 +55,7 @@ def test_get_backend(linear_3q_architecture): assert isinstance(backend, IQMBackend) assert backend.client._api.iqm_server_url == url assert backend.num_qubits == 3 - assert set(backend.coupling_map.get_edges()) == {(0, 1), (1, 0), (1, 2), (2, 1)} + assert set(backend.coupling_map.get_edges()) == {(0, 1), (1, 2)} assert backend._calibration_set_id == linear_3q_architecture.calibration_set_id diff --git a/tests/test_iqm_transpilation.py b/tests/test_iqm_transpilation.py index 9d4e6f740..db0f1ae36 100644 --- a/tests/test_iqm_transpilation.py +++ b/tests/test_iqm_transpilation.py @@ -113,20 +113,20 @@ def test_submitted_circuit(adonis_architecture): instr_names = [f"{instr.name}:{','.join(instr.qubits)}" for instr in submitted_circuit.instructions] assert instr_names == [ - # Hadamard on 0 (= physical 0) + # CX phase 1: Hadamard on target qubit 1 (= physical 2) 'prx:2', 'prx:2', - # CX phase 1: Hadamard on target qubit 1 (= physical 4) + # Hadamard on target qubit 0 (= physical 4) 'prx:4', 'prx:4', # CX phase 2: CZ on 0,1 (= physical 2,4) 'cz:2,4', - # Hadamard again on target qubit 1 (= physical 4) - 'prx:4', - 'prx:4', + # CX phase 3: Hadamard again on target qubit 1 (= physical 2) + 'prx:2', + 'prx:2', # Barrier before measurements - 'barrier:2,4', + 'barrier:4,2', # Measurement on both qubits - 'measure:2', 'measure:4', + 'measure:2', ]