diff --git a/CHANGELOG.md b/CHANGELOG.md index 439a4307e2ae..319aa55ac1b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -108,6 +108,8 @@ The format is based on [Keep a Changelog]. - Fixed bug in CommutationAnalysis pass affecting conditional gates (\#2669) - Fixed bug in measure sampling for BasicAer Qasm simulator if a qubit was measured into more than one memory cbit (\#2735) +- Fixed bug in measure sampling for BasicAer Qasm simulator if only a + subset of qubits are measured (\#2790) ## [0.8.2] - 2019-06-14 diff --git a/qiskit/providers/basicaer/qasm_simulator.py b/qiskit/providers/basicaer/qasm_simulator.py index d3e9257bc0b9..500f7ed4ef1d 100644 --- a/qiskit/providers/basicaer/qasm_simulator.py +++ b/qiskit/providers/basicaer/qasm_simulator.py @@ -183,10 +183,13 @@ def _add_sample_measure(self, measure_params, num_samples): Returns: list: A list of memory values in hex format. """ - # Get unique qubits that are actually measured - measured_qubits = list({qubit for qubit, cmembit in measure_params}) + # Get unique qubits that are actually measured and sort in + # ascending order + measured_qubits = sorted(list({qubit for qubit, cmembit in measure_params})) num_measured = len(measured_qubits) - # Axis for numpy.sum to compute probabilities + # We use the axis kwarg for numpy.sum to compute probabilities + # this sums over all non-measured qubits to return a vector + # of measure probabilities for the measured qubits axis = list(range(self._number_of_qubits)) for qubit in reversed(measured_qubits): # Remove from largest qubit to smallest so list position is correct @@ -195,15 +198,18 @@ def _add_sample_measure(self, measure_params, num_samples): probabilities = np.reshape(np.sum(np.abs(self._statevector) ** 2, axis=tuple(axis)), 2 ** num_measured) - # Generate samples on measured qubits + # Generate samples on measured qubits as ints with qubit + # position in the bit-string for each int given by the qubit + # position in the sorted measured_qubits list samples = self._local_random.choice(range(2 ** num_measured), num_samples, p=probabilities) - # Convert to bit-strings + # Convert the ints to bitstrings memory = [] for sample in samples: classical_memory = self._classical_memory for qubit, cmembit in measure_params: - qubit_outcome = int((sample & (1 << qubit)) >> qubit) + pos = measured_qubits.index(qubit) + qubit_outcome = int((sample & (1 << pos)) >> pos) membit = 1 << cmembit classical_memory = (classical_memory & (~membit)) | (qubit_outcome << cmembit) value = bin(classical_memory)[2:] diff --git a/test/python/basicaer/test_qasm_simulator.py b/test/python/basicaer/test_qasm_simulator.py index 077fdb9b18fc..47677c3d7694 100644 --- a/test/python/basicaer/test_qasm_simulator.py +++ b/test/python/basicaer/test_qasm_simulator.py @@ -48,7 +48,7 @@ def test_qasm_simulator_single_shot(self): result = self.backend.run(self.qobj).result() self.assertEqual(result.success, True) - def test_qasm_simulator_measure_sampler(self): + def test_measure_sampler_repeated_qubits(self): """Test measure sampler if qubits measured more than once.""" shots = 100 qr = QuantumRegister(2, 'qr') @@ -69,6 +69,55 @@ def test_qasm_simulator_measure_sampler(self): counts = result.get_counts(0) self.assertEqual(counts, target) + def test_measure_sampler_single_qubit(self): + """Test measure sampler if single-qubit is measured.""" + shots = 100 + num_qubits = 5 + qr = QuantumRegister(num_qubits, 'qr') + cr = ClassicalRegister(1, 'cr') + + for qubit in range(num_qubits): + circuit = QuantumCircuit(qr, cr) + circuit.x(qr[qubit]) + circuit.measure(qr[qubit], cr[0]) + target = {'1': shots} + job = execute( + circuit, + backend=self.backend, + shots=shots, + seed_simulator=self.seed) + result = job.result() + counts = result.get_counts(0) + self.assertEqual(counts, target) + + def test_measure_sampler_partial_qubit(self): + """Test measure sampler if single-qubit is measured.""" + shots = 100 + num_qubits = 5 + qr = QuantumRegister(num_qubits, 'qr') + cr = ClassicalRegister(4, 'cr') + + circuit = QuantumCircuit(qr, cr) + circuit.x(qr[3]) + circuit.x(qr[1]) + circuit.barrier(qr) + circuit.measure(qr[3], cr[1]) + circuit.barrier(qr) + circuit.measure(qr[1], cr[0]) + circuit.barrier(qr) + circuit.measure(qr[0], cr[2]) + circuit.barrier(qr) + circuit.measure(qr[3], cr[3]) + target = {'1011': shots} + job = execute( + circuit, + backend=self.backend, + shots=shots, + seed_simulator=self.seed) + result = job.result() + counts = result.get_counts(0) + self.assertEqual(counts, target) + def test_qasm_simulator(self): """Test data counts output for single circuit run against reference.""" result = self.backend.run(self.qobj).result() @@ -126,7 +175,7 @@ def test_teleport(self): circuit = QuantumCircuit(qr, cr0, cr1, cr2, name='teleport') circuit.h(qr[1]) circuit.cx(qr[1], qr[2]) - circuit.ry(pi/4, qr[0]) + circuit.ry(pi / 4, qr[0]) circuit.cx(qr[0], qr[1]) circuit.h(qr[0]) circuit.barrier(qr) @@ -153,8 +202,8 @@ def test_teleport(self): self.log.info('test_teleport: data %s', data) self.log.info('test_teleport: alice %s', alice) self.log.info('test_teleport: bob %s', bob) - alice_ratio = 1/np.tan(pi/8)**2 - bob_ratio = bob['0']/float(bob['1']) + alice_ratio = 1 / np.tan(pi / 8) ** 2 + bob_ratio = bob['0'] / float(bob['1']) error = abs(alice_ratio - bob_ratio) / alice_ratio self.log.info('test_teleport: relative error = %s', error) self.assertLess(error, 0.05)