diff --git a/Dockerfile b/Dockerfile index 92cb243d2..e247ad26d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # The dockerfile for OpenQAOA braket jobs -FROM 292282985366.dkr.ecr.us-east-1.amazonaws.com/Braket-Base:1.0.0-cpu-py39-ubuntu22.04 +FROM 292282985366.dkr.ecr.us-east-1.amazonaws.com/amazon-braket-base-jobs:1.0-cpu-py39-ubuntu22.04 RUN mkdir -p /openqaoa diff --git a/src/openqaoa-braket/openqaoa_braket/backends/devices.py b/src/openqaoa-braket/openqaoa_braket/backends/devices.py index 004fc0c3b..6a8914951 100644 --- a/src/openqaoa-braket/openqaoa_braket/backends/devices.py +++ b/src/openqaoa-braket/openqaoa_braket/backends/devices.py @@ -63,6 +63,7 @@ def __init__( self.provider_connected = None self.qpu_connected = None + self._connectivity = None def check_connection(self) -> bool: self.provider_connected = self._check_provider_connection() @@ -144,7 +145,22 @@ def _check_provider_connection(self) -> bool: ) return False + @property def connectivity(self) -> List[List[int]]: - aws_connectivity = self.backend_device.topology_graph.edges - connectivity_list = [list(edge) for edge in aws_connectivity] - return connectivity_list + aws_device_properties = self.backend_device.properties + if hasattr(aws_device_properties, "topology_graph"): + aws_device_topology = aws_device_properties.topology_gprah + if aws_device_topology is not None: + self._connectivity = [ + list(edge) for edge in aws_device_topology.edges + ] + # else: + # return None + return self._connectivity + + @connectivity.setter + def connectivity(self, value: List[List[int]]): + if isinstance(value, list): + self._connectivity = value + else: + raise TypeError("Connectivity must be a list of lists") diff --git a/src/openqaoa-braket/openqaoa_braket/backends/qaoa_braket_qpu.py b/src/openqaoa-braket/openqaoa_braket/backends/qaoa_braket_qpu.py index e5db0e66a..829d76bd8 100644 --- a/src/openqaoa-braket/openqaoa_braket/backends/qaoa_braket_qpu.py +++ b/src/openqaoa-braket/openqaoa_braket/backends/qaoa_braket_qpu.py @@ -20,6 +20,7 @@ from openqaoa.qaoa_components.variational_parameters.variational_baseparams import ( QAOAVariationalBaseParams, ) +from openqaoa.utilities import permute_counts_dictionary class QAOAAWSQPUBackend( @@ -85,9 +86,12 @@ def __init__( else list(range(self.n_qubits)) ) else: - warnings.warn( - "Ignoring the initial_qubit_mapping since the routing algorithm chose one" - ) + if isinstance(initial_qubit_mapping, list): + warnings.warn( + "Ignoring the initial_qubit_mapping since the routing algorithm chose one" + ) + else: + pass self.disable_qubit_rewiring = disable_qubit_rewiring if self.prepend_state: @@ -138,7 +142,6 @@ def qaoa_circuit(self, params: QAOAVariationalBaseParams) -> Circuit: if self.append_state: parametric_circuit += self.append_state - # TODO: needs to be fixed --> measurement operations on problem qubits parametric_circuit += Probability.probability() angles_list = self.obtain_angles_for_pauli_list(self.abstract_circuit, params) @@ -252,11 +255,14 @@ def get_counts(self, params: QAOAVariationalBaseParams, n_shots=None) -> dict: "An Error Occurred with the Task(s) sent to AWS." ) - final_counts = counts - # if self.final_mapping is not None: - # final_counts = permute_counts_dictionary(final_counts, - # self.final_mapping) # # Expose counts + if self.final_mapping is not None: + counts = permute_counts_dictionary(counts, self.final_mapping) + + final_counts = {key[: self.problem_qubits]: 0 for key in counts.keys()} + for key, value in counts.items(): + final_counts[key[: self.problem_qubits]] += value + self.measurement_outcomes = final_counts return final_counts diff --git a/src/openqaoa-braket/tests/test_qpu_braket.py b/src/openqaoa-braket/tests/test_qpu_braket.py index 320b94802..4f99c9534 100644 --- a/src/openqaoa-braket/tests/test_qpu_braket.py +++ b/src/openqaoa-braket/tests/test_qpu_braket.py @@ -1,5 +1,5 @@ import unittest -from unittest.mock import Mock +from unittest.mock import Mock, MagicMock import numpy as np import pytest @@ -439,6 +439,143 @@ def test_remote_integration_qpu_run(self): ) braket_backend.expectation(variate_params) + # Test to be improved, to be run when OQ starts supporting LocalSimulators from braket + # @pytest.mark.sim + # def test_braket_measurement_instructions(self): + # """ + # When using the routing functionality within OpenQAOA, the + # final order of qubits is changed. This function tests for + # the functionality in the braket backend to reset the order + # of qubits in the measurement instructions. + + # The goal is compare the output of a routed circuit to that of + # a non-routed circuit on the SV1 simulator to test the correctness + # of the output. + # """ + # routing_function = MagicMock( + # return_value=( + # [ + # [5, 7], + # [0, 4], + # [2, 6], + # [1, 6], + # [3, 7], + # [1, 3], + # [0, 5], + # [2, 4], + # [1, 6], + # [1, 3], + # [2, 6], + # [2, 4], + # [0, 4], + # [2, 6], + # [3, 7], + # [5, 7], + # [1, 3], + # [0, 5], + # [5, 7], + # [0, 4], + # [0, 4], + # [2, 4], + # [5, 7], + # [0, 5], + # [3, 7], + # [1, 6], + # [1, 3], + # [2, 6], + # [2, 4], + # [2, 6], + # [3, 7], + # [1, 3], + # [1, 6], + # [1, 3], + # [2, 6], + # [0, 5], + # [5, 7], + # [0, 4], + # [5, 7], + # [0, 4], + # [0, 5], + # ], + # [ + # False, + # False, + # False, + # False, + # False, + # False, + # False, + # False, + # True, + # False, + # False, + # True, + # False, + # False, + # True, + # False, + # False, + # True, + # False, + # False, + # True, + # False, + # True, + # False, + # False, + # True, + # False, + # False, + # True, + # False, + # True, + # False, + # True, + # False, + # False, + # True, + # False, + # False, + # True, + # True, + # False, + # ], + # {1: 0, 5: 1, 3: 2, 6: 3, 2: 4, 0: 5, 4: 6, 7: 7}, + # [3, 6, 7, 4, 0, 2, 1, 5], + # ) + # ) + # n_shots = 100 + # # generate a random set of 8 integers + # set_of_numbers = [9, 4, 4, 7, 3, 1, 1, 9] + # qubo = NumberPartition(set_of_numbers).qubo + + # mixer_hamil = X_mixer_hamiltonian(n_qubits=8) + # aws_device = DeviceAWS("arn:aws:braket:::device/quantum-simulator/amazon/sv1") + + # qaoa_descriptor_routed = QAOADescriptor( + # qubo.hamiltonian, mixer_hamil, p=1, routing_function=routing_function + # ) + # variate_params = create_qaoa_variational_params( + # qaoa_descriptor_routed, "standard", "rand" + # ) + # braket_backend_routed = QAOAAWSQPUBackend( + # qaoa_descriptor_routed, aws_device, n_shots, None, None, True, 1.0 + # ) + + # qaoa_descriptor = QAOADescriptor(qubo.hamiltonian, mixer_hamil, p=1) + # braket_backend = QAOAAWSQPUBackend( + # qaoa_descriptor, aws_device, n_shots, None, None, True, 1.0 + # ) + + # list_angles = [[1.0, 0.0]]#, [0.8, 0.2], [0.6, 0.4], [0.4, 0.6]] + # for angles in list_angles: + # variate_params.update_from_raw(angles) + # # Run the circuit without routing + # non_routed_exp = braket_backend.expectation(variate_params) + # # Run the circuit with routing + # routed_exp = braket_backend_routed.expectation(variate_params) + # self.assertAlmostEqual(non_routed_exp, routed_exp, places=2) + if __name__ == "__main__": unittest.main() diff --git a/src/openqaoa-core/openqaoa/utilities.py b/src/openqaoa-core/openqaoa/utilities.py index b5f773846..262bee490 100644 --- a/src/openqaoa-core/openqaoa/utilities.py +++ b/src/openqaoa-core/openqaoa/utilities.py @@ -1719,7 +1719,8 @@ def is_valid_uuid(uuid_to_test: str) -> bool: def permute_counts_dictionary( - counts_dictionary: dict, permutation_order: List[int] + counts_dictionary: dict, + permutation_order: List[int], ) -> dict: """Permutes the order of the qubits in the counts dictionary to the original order if SWAP gates were used leading to modified qubit layout. @@ -1735,6 +1736,11 @@ def permute_counts_dictionary( `dict` The permuted counts dictionary with qubits in the original place """ + counts_dict_nqubits = len(list(counts_dictionary.keys())[0]) + assert len(permutation_order) == counts_dict_nqubits, ( + "The number of qubits in the permutation order should be equal" + "to the number of qubits in the counts dictionary." + ) # Create a mapping of original positions to final positions # original order always goes from 0 -> n-1 diff --git a/src/openqaoa-pyquil/openqaoa_pyquil/backends/qaoa_pyquil_qpu.py b/src/openqaoa-pyquil/openqaoa_pyquil/backends/qaoa_pyquil_qpu.py index ab2828d4d..1860917d8 100644 --- a/src/openqaoa-pyquil/openqaoa_pyquil/backends/qaoa_pyquil_qpu.py +++ b/src/openqaoa-pyquil/openqaoa_pyquil/backends/qaoa_pyquil_qpu.py @@ -127,6 +127,11 @@ def __init__( if initial_qubit_mapping is not None else list(range(self.n_qubits)) ) + else: + if isinstance(initial_qubit_mapping, list): + warnings.warn( + "Ignoring the initial_qubit_mapping since the routing algorithm chose one" + ) # self.qureg_placeholders = QubitPlaceholder.register(self.n_qubits) self.qubit_mapping = dict(zip(self.qureg, self.initial_qubit_mapping)) diff --git a/src/openqaoa-qiskit/openqaoa_qiskit/backends/qaoa_qiskit_qpu.py b/src/openqaoa-qiskit/openqaoa_qiskit/backends/qaoa_qiskit_qpu.py index 66131dd41..0a2e27cdb 100644 --- a/src/openqaoa-qiskit/openqaoa_qiskit/backends/qaoa_qiskit_qpu.py +++ b/src/openqaoa-qiskit/openqaoa_qiskit/backends/qaoa_qiskit_qpu.py @@ -94,6 +94,11 @@ def __init__( if initial_qubit_mapping is not None else list(range(self.n_qubits)) ) + else: + if isinstance(initial_qubit_mapping, list): + warnings.warn( + "Ignoring the initial_qubit_mapping since the routing algorithm chose one" + ) if self.prepend_state: assert self.n_qubits >= len(prepend_state.qubits), (