From bd8b2216b6c4504d86aec5ac63c66079417c2021 Mon Sep 17 00:00:00 2001 From: Richard Chen Date: Thu, 19 Mar 2020 10:39:24 -0400 Subject: [PATCH 1/4] support diff qubit order in error mitigation, transform the calibration matrix if order is different. --- qiskit/aqua/quantum_instance.py | 23 ++++++-- .../utils/measurement_error_mitigation.py | 53 ++++++++++++------- 2 files changed, 53 insertions(+), 23 deletions(-) diff --git a/qiskit/aqua/quantum_instance.py b/qiskit/aqua/quantum_instance.py index 23ff4f3b21..f6d22ad2e8 100644 --- a/qiskit/aqua/quantum_instance.py +++ b/qiskit/aqua/quantum_instance.py @@ -271,7 +271,7 @@ def execute(self, circuits, had_transpiled=False): qobj = compiler.assemble(circuits, **self._run_config.to_dict()) if self._meas_error_mitigation_cls is not None: - qubit_index = get_measured_qubits_from_qobj(qobj) + qubit_index, qubit_mappings = get_measured_qubits_from_qobj(qobj) qubit_index_str = '_'.join([str(x) for x in qubit_index]) + \ "_{}".format(self._meas_error_mitigation_shots or self._run_config.shots) meas_error_mitigation_fitter, timestamp = \ @@ -346,9 +346,24 @@ def execute(self, circuits, had_transpiled=False): if meas_error_mitigation_fitter is not None: logger.info("Performing measurement error mitigation.") - result = \ - meas_error_mitigation_fitter.filter.apply(result, - self._meas_error_mitigation_method) + skip_num_circuits = len(result.results) - len(circuits) + # remove the calibration counts from result object to assure the length of + # ExperimentalResult is equal length to input circuits + result.results = result.results[skip_num_circuits:] + tmp_result = copy.deepcopy(result) + for qubit_index_str, c_idx in qubit_mappings.items(): + curr_qubit_index = [int(x) for x in qubit_index_str.split("_")] + tmp_result.results = [result.results[i] for i in c_idx] + if curr_qubit_index == qubit_index: + tmp_fitter = meas_error_mitigation_fitter + else: + tmp_fitter = meas_error_mitigation_fitter.subset_fitter(curr_qubit_index) + tmp_result = tmp_fitter.filter.apply( + tmp_result, self._meas_error_mitigation_method + ) + for i, n in enumerate(c_idx): + result.results[n] = tmp_result.results[i] + else: result = run_qobj(qobj, self._backend, self._qjob_config, self._backend_options, self._noise_config, diff --git a/qiskit/aqua/utils/measurement_error_mitigation.py b/qiskit/aqua/utils/measurement_error_mitigation.py index aa290b1297..c10193017f 100644 --- a/qiskit/aqua/utils/measurement_error_mitigation.py +++ b/qiskit/aqua/utils/measurement_error_mitigation.py @@ -34,25 +34,32 @@ def get_measured_qubits(transpiled_circuits): transpiled_circuits ([QuantumCircuit]): a list of transpiled circuits Returns: - list[int]: the qubit mapping to-be-used for measure error mitigation + list[int]: the used and sorted qubit index + dict: key is qubit index str connected by '_', + value is the experiment index. {str: list[int]} Raises: AquaError: invalid qubit mapping """ - - qubit_mapping = None - for qc in transpiled_circuits: + qubit_index = None + qubit_mappings = {} + for idx, qc in enumerate(transpiled_circuits): measured_qubits = [] for inst, qargs, _ in qc.data: if inst.name != 'measure': continue measured_qubits.append(qargs[0][1]) - if qubit_mapping is None: - qubit_mapping = measured_qubits - elif qubit_mapping != measured_qubits: - raise AquaError("The qubit mapping of circuits are different." - "Currently, we only support single mapping.") + measured_qubits_str = '_'.join([str(x) for x in measured_qubits]) + if measured_qubits_str not in qubit_mappings: + qubit_mappings[measured_qubits_str] = [] + qubit_mappings[measured_qubits_str].append(idx) + if qubit_index is None: + qubit_index = measured_qubits + elif set(qubit_index) != set(measured_qubits): + raise AquaError("The used qubit index are different. ({}) vs ({}).\nCurrently, " + "we only support all circuits using the same set of qubits " + "regardless qubit order.".format(qubit_index, measured_qubits)) - return qubit_mapping + return sorted(qubit_index), qubit_mappings def get_measured_qubits_from_qobj(qobj): @@ -63,27 +70,35 @@ def get_measured_qubits_from_qobj(qobj): qobj (QasmObj): qobj Returns: - list[int]: the qubit mapping to-be-used for measure error mitigation + list[int]: the used and sorted qubit index + dict: key is qubit index str connected by '_', + value is the experiment index. {str: list[int]} Raises: AquaError: invalid qubit mapping """ - qubit_mapping = None + qubit_index = None + qubit_mappings = {} - for exp in qobj.experiments: + for idx, exp in enumerate(qobj.experiments): measured_qubits = [] for instr in exp.instructions: if instr.name != 'measure': continue measured_qubits.append(instr.qubits[0]) - if qubit_mapping is None: - qubit_mapping = measured_qubits + measured_qubits_str = '_'.join([str(x) for x in measured_qubits]) + if measured_qubits_str not in qubit_mappings: + qubit_mappings[measured_qubits_str] = [] + qubit_mappings[measured_qubits_str].append(idx) + if qubit_index is None: + qubit_index = measured_qubits else: - if qubit_mapping != measured_qubits: - raise AquaError("The qubit mapping of circuits are different." - "Currently, we only support single mapping.") + if set(qubit_index) != set(measured_qubits): + raise AquaError("The used qubit index are different. ({}) vs ({}).\nCurrently, " + "we only support all circuits using the same set of qubits " + "regardless qubit order.".format(qubit_index, measured_qubits)) - return qubit_mapping + return sorted(qubit_index), qubit_mappings # pylint: disable=invalid-name From 126a02f2f599bf1ec609f4ce6be46998d411bb48 Mon Sep 17 00:00:00 2001 From: Richard Chen Date: Fri, 20 Mar 2020 13:50:39 -0400 Subject: [PATCH 2/4] add test --- test/aqua/test_measure_error_mitigation.py | 53 +++++++++++++++++++++- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/test/aqua/test_measure_error_mitigation.py b/test/aqua/test_measure_error_mitigation.py index 326e597f3b..f0078cb8e6 100644 --- a/test/aqua/test_measure_error_mitigation.py +++ b/test/aqua/test_measure_error_mitigation.py @@ -16,12 +16,15 @@ import unittest import time -from test.aqua import QiskitAquaTestCase + import numpy as np from qiskit.ignis.mitigation.measurement import CompleteMeasFitter +from qiskit import QuantumCircuit + from qiskit.aqua.components.oracles import LogicalExpressionOracle -from qiskit.aqua import QuantumInstance, aqua_globals +from qiskit.aqua import QuantumInstance, aqua_globals, AquaError from qiskit.aqua.algorithms import Grover +from test.aqua import QiskitAquaTestCase class TestMeasurementErrorMitigation(QiskitAquaTestCase): @@ -153,5 +156,51 @@ def test_measurement_error_mitigation_with_dedicated_shots(self): self.assertGreater(timestamp_2, timestamp_1) + def test_measurement_error_mitigation_with_diff_qubit_order(self): + """ measurement error mitigation with dedicated shots test """ + # pylint: disable=import-outside-toplevel + from qiskit import Aer + from qiskit.providers.aer import noise + + aqua_globals.random_seed = 0 + + # build noise model + noise_model = noise.NoiseModel() + read_err = noise.errors.readout_error.ReadoutError([[0.9, 0.1], [0.25, 0.75]]) + noise_model.add_all_qubit_readout_error(read_err) + + backend = Aer.get_backend('qasm_simulator') + quantum_instance = QuantumInstance(backend=backend, + seed_simulator=1679, + seed_transpiler=167, + shots=1000, + noise_model=noise_model, + measurement_error_mitigation_cls=CompleteMeasFitter, + cals_matrix_refresh_period=0) + # circuit + qc1 = QuantumCircuit(2, 2) + qc1.h(0) + qc1.cx(0, 1) + qc1.measure(0, 0) + qc1.measure(1, 1) + qc2 = QuantumCircuit(2, 2) + qc2.h(0) + qc2.cx(0, 1) + qc2.measure(1, 0) + qc2.measure(0, 1) + + # this should run smoothly + quantum_instance.execute([qc1, qc2]) + + # failure case + qc3 = QuantumCircuit(3, 3) + qc3.h(2) + qc3.cx(1, 2) + qc3.measure(2, 1) + qc3.measure(1, 2) + + self.assertRaises(AquaError, quantum_instance.execute, [qc1, qc3]) + + if __name__ == '__main__': unittest.main() From 6efdba30e1b4db8ea465a179ed1654c65617b9a7 Mon Sep 17 00:00:00 2001 From: Manoel Marques Date: Fri, 20 Mar 2020 14:35:40 -0400 Subject: [PATCH 3/4] fix lint and style --- test/aqua/test_measure_error_mitigation.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/aqua/test_measure_error_mitigation.py b/test/aqua/test_measure_error_mitigation.py index f0078cb8e6..a1fc51cdb0 100644 --- a/test/aqua/test_measure_error_mitigation.py +++ b/test/aqua/test_measure_error_mitigation.py @@ -17,6 +17,7 @@ import unittest import time +from test.aqua import QiskitAquaTestCase import numpy as np from qiskit.ignis.mitigation.measurement import CompleteMeasFitter from qiskit import QuantumCircuit @@ -24,7 +25,6 @@ from qiskit.aqua.components.oracles import LogicalExpressionOracle from qiskit.aqua import QuantumInstance, aqua_globals, AquaError from qiskit.aqua.algorithms import Grover -from test.aqua import QiskitAquaTestCase class TestMeasurementErrorMitigation(QiskitAquaTestCase): @@ -155,7 +155,6 @@ def test_measurement_error_mitigation_with_dedicated_shots(self): self.assertGreater(total_diff, 0.0) self.assertGreater(timestamp_2, timestamp_1) - def test_measurement_error_mitigation_with_diff_qubit_order(self): """ measurement error mitigation with dedicated shots test """ # pylint: disable=import-outside-toplevel @@ -168,7 +167,7 @@ def test_measurement_error_mitigation_with_diff_qubit_order(self): noise_model = noise.NoiseModel() read_err = noise.errors.readout_error.ReadoutError([[0.9, 0.1], [0.25, 0.75]]) noise_model.add_all_qubit_readout_error(read_err) - + backend = Aer.get_backend('qasm_simulator') quantum_instance = QuantumInstance(backend=backend, seed_simulator=1679, From d8c83fbd557a3dc53a0d40da87571a6878faf538 Mon Sep 17 00:00:00 2001 From: Manoel Marques Date: Fri, 20 Mar 2020 14:47:25 -0400 Subject: [PATCH 4/4] fix copyright --- qiskit/aqua/utils/measurement_error_mitigation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/aqua/utils/measurement_error_mitigation.py b/qiskit/aqua/utils/measurement_error_mitigation.py index c10193017f..489219fed9 100644 --- a/qiskit/aqua/utils/measurement_error_mitigation.py +++ b/qiskit/aqua/utils/measurement_error_mitigation.py @@ -2,7 +2,7 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2019. +# (C) Copyright IBM 2019, 2020. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory