Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix measure sampling bug in BasicAer #2790

Merged
merged 5 commits into from
Jul 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 12 additions & 6 deletions qiskit/providers/basicaer/qasm_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:]
Expand Down
57 changes: 53 additions & 4 deletions test/python/basicaer/test_qasm_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand All @@ -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()
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand Down