diff --git a/test/terra/backends/qasm_simulator/qasm_snapshot.py b/test/terra/backends/qasm_simulator/qasm_snapshot.py index 656ea0230e..42bba4c80f 100644 --- a/test/terra/backends/qasm_simulator/qasm_snapshot.py +++ b/test/terra/backends/qasm_simulator/qasm_snapshot.py @@ -28,10 +28,12 @@ snapshot_state_counts_nondeterministic, snapshot_state_pre_measure_statevector_nondeterministic, snapshot_state_post_measure_statevector_nondeterministic) - -def get_snapshots(data, label, snapshot_type): - """Format snapshots as list of Numpy arrays""" - return data.get("snapshots", {}).get(snapshot_type, {}).get(label, []) +from test.terra.reference.ref_snapshot_probabilities import ( + snapshot_probabilities_circuits, + snapshot_probabilities_counts, + snapshot_probabilities_labels_qubits, + snapshot_probabilities_post_meas_probs, + snapshot_probabilities_pre_meas_probs) class QasmSnapshotStatevectorTests: @@ -489,3 +491,84 @@ def test_snapshot_density_matrix_post_measure_nondet(self): target = np.outer(target, target.conj()) value = snaps.get(mem) self.assertTrue(np.allclose(value, target)) + + +class QasmSnapshotProbabilitiesTests: + """QasmSimulator snapshot probabilities tests.""" + + SIMULATOR = QasmSimulator() + SUPPORTED_QASM_METHODS = [ + 'automatic', 'statevector', 'density_matrix', 'matrix_product_state' + ] + BACKEND_OPTS = {} + + @staticmethod + def probabilitiy_snapshots(data, labels): + """Format snapshots as nested dicts""" + # Check snapshot entry exists in data + output = {} + for label in labels: + snaps = data.get("snapshots", {}).get("probabilities", {}).get(label, []) + # Convert list into dict + inner = {} + for snap_dict in snaps: + inner[snap_dict['memory']] = snap_dict['value'] + output[label] = inner + return output + + def test_snapshot_probabilities_pre_measure(self): + """Test snapshot probabilities before final measurement""" + shots = 1000 + labels = list(snapshot_probabilities_labels_qubits().keys()) + counts_targets = snapshot_probabilities_counts(shots) + prob_targets = snapshot_probabilities_pre_meas_probs() + + circuits = snapshot_probabilities_circuits(post_measure=False) + + qobj = assemble(circuits, self.SIMULATOR, shots=shots) + job = self.SIMULATOR.run(qobj, backend_options=self.BACKEND_OPTS) + method = self.BACKEND_OPTS.get('method', 'automatic') + if method not in QasmSnapshotProbabilitiesTests.SUPPORTED_QASM_METHODS: + self.assertRaises(AerError, job.result) + else: + result = job.result() + self.is_completed(result) + self.compare_counts(result, circuits, counts_targets, delta=0.1 * shots) + # Check snapshots + for j, circuit in enumerate(circuits): + data = result.data(circuit) + all_snapshots = self.probabilitiy_snapshots(data, labels) + for label in labels: + snaps = all_snapshots.get(label, {}) + self.assertTrue(len(snaps), 1) + for memory, value in snaps.items(): + target = prob_targets[j].get(label, {}).get(memory, {}) + self.assertDictAlmostEqual(value, target, delta=1e-7) + + def test_snapshot_probabilities_post_measure(self): + """Test snapshot probabilities after final measurement""" + shots = 1000 + labels = list(snapshot_probabilities_labels_qubits().keys()) + counts_targets = snapshot_probabilities_counts(shots) + prob_targets = snapshot_probabilities_post_meas_probs() + + circuits = snapshot_probabilities_circuits(post_measure=True) + + qobj = assemble(circuits, self.SIMULATOR, shots=shots) + job = self.SIMULATOR.run(qobj, backend_options=self.BACKEND_OPTS) + method = self.BACKEND_OPTS.get('method', 'automatic') + if method not in QasmSnapshotProbabilitiesTests.SUPPORTED_QASM_METHODS: + self.assertRaises(AerError, job.result) + else: + result = job.result() + self.is_completed(result) + self.compare_counts(result, circuits, counts_targets, delta=0.1 * shots) + # Check snapshots + for j, circuit in enumerate(circuits): + data = result.data(circuit) + all_snapshots = self.probabilitiy_snapshots(data, labels) + for label in labels: + snaps = all_snapshots.get(label, {}) + for memory, value in snaps.items(): + target = prob_targets[j].get(label, {}).get(memory, {}) + self.assertDictAlmostEqual(value, target, delta=1e-7) diff --git a/test/terra/backends/test_qasm_density_matrix_simulator.py b/test/terra/backends/test_qasm_density_matrix_simulator.py index e9d9b0a34f..4902bc0d96 100644 --- a/test/terra/backends/test_qasm_density_matrix_simulator.py +++ b/test/terra/backends/test_qasm_density_matrix_simulator.py @@ -48,6 +48,7 @@ from test.terra.backends.qasm_simulator.qasm_snapshot import QasmSnapshotStatevectorTests from test.terra.backends.qasm_simulator.qasm_snapshot import QasmSnapshotDensityMatrixTests from test.terra.backends.qasm_simulator.qasm_snapshot import QasmSnapshotStabilizerTests +from test.terra.backends.qasm_simulator.qasm_snapshot import QasmSnapshotProbabilitiesTests class TestQasmDensityMatrixSimulator(common.QiskitAerTestCase, @@ -75,6 +76,7 @@ class TestQasmDensityMatrixSimulator(common.QiskitAerTestCase, QasmKrausNoiseTests, QasmSnapshotStatevectorTests, QasmSnapshotDensityMatrixTests, + QasmSnapshotProbabilitiesTests, QasmSnapshotStabilizerTests): """QasmSimulator density_matrix method tests.""" diff --git a/test/terra/backends/test_qasm_simulator.py b/test/terra/backends/test_qasm_simulator.py index 2cf006642c..20ff751b13 100644 --- a/test/terra/backends/test_qasm_simulator.py +++ b/test/terra/backends/test_qasm_simulator.py @@ -44,6 +44,7 @@ from test.terra.backends.qasm_simulator.qasm_snapshot import QasmSnapshotStatevectorTests from test.terra.backends.qasm_simulator.qasm_snapshot import QasmSnapshotDensityMatrixTests from test.terra.backends.qasm_simulator.qasm_snapshot import QasmSnapshotStabilizerTests +from test.terra.backends.qasm_simulator.qasm_snapshot import QasmSnapshotProbabilitiesTests # Other tests from test.terra.backends.qasm_simulator.qasm_method import QasmMethodTests from test.terra.backends.qasm_simulator.qasm_thread_management import QasmThreadManagementTests @@ -84,6 +85,7 @@ class TestQasmSimulator(common.QiskitAerTestCase, QasmBasicsTests, QasmSnapshotStatevectorTests, QasmSnapshotDensityMatrixTests, + QasmSnapshotProbabilitiesTests, QasmSnapshotStabilizerTests): """QasmSimulator automatic method tests.""" diff --git a/test/terra/backends/test_qasm_stabilizer_simulator.py b/test/terra/backends/test_qasm_stabilizer_simulator.py index bbb2734f28..07550f02c0 100644 --- a/test/terra/backends/test_qasm_stabilizer_simulator.py +++ b/test/terra/backends/test_qasm_stabilizer_simulator.py @@ -34,6 +34,7 @@ from test.terra.backends.qasm_simulator.qasm_snapshot import QasmSnapshotStatevectorTests from test.terra.backends.qasm_simulator.qasm_snapshot import QasmSnapshotDensityMatrixTests from test.terra.backends.qasm_simulator.qasm_snapshot import QasmSnapshotStabilizerTests +from test.terra.backends.qasm_simulator.qasm_snapshot import QasmSnapshotProbabilitiesTests # Other tests from test.terra.backends.qasm_simulator.qasm_method import QasmMethodTests @@ -51,6 +52,7 @@ class TestQasmStabilizerSimulator(common.QiskitAerTestCase, QasmPauliNoiseTests, QasmSnapshotStatevectorTests, QasmSnapshotDensityMatrixTests, + QasmSnapshotProbabilitiesTests, QasmSnapshotStabilizerTests): """QasmSimulator stabilizer method tests.""" diff --git a/test/terra/backends/test_qasm_statevector_simulator.py b/test/terra/backends/test_qasm_statevector_simulator.py index 65278d6368..c0298c3c6d 100644 --- a/test/terra/backends/test_qasm_statevector_simulator.py +++ b/test/terra/backends/test_qasm_statevector_simulator.py @@ -46,6 +46,7 @@ from test.terra.backends.qasm_simulator.qasm_snapshot import QasmSnapshotStatevectorTests from test.terra.backends.qasm_simulator.qasm_snapshot import QasmSnapshotDensityMatrixTests from test.terra.backends.qasm_simulator.qasm_snapshot import QasmSnapshotStabilizerTests +from test.terra.backends.qasm_simulator.qasm_snapshot import QasmSnapshotProbabilitiesTests # Other tests from test.terra.backends.qasm_simulator.qasm_method import QasmMethodTests @@ -74,6 +75,7 @@ class TestQasmStatevectorSimulator(common.QiskitAerTestCase, QasmKrausNoiseTests, QasmSnapshotStatevectorTests, QasmSnapshotDensityMatrixTests, + QasmSnapshotProbabilitiesTests, QasmSnapshotStabilizerTests): """QasmSimulator statevector method tests.""" diff --git a/test/terra/reference/ref_snapshot_probabilities.py b/test/terra/reference/ref_snapshot_probabilities.py new file mode 100644 index 0000000000..5950ab305e --- /dev/null +++ b/test/terra/reference/ref_snapshot_probabilities.py @@ -0,0 +1,173 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2018, 2019. +# +# 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 +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" +Test circuits and reference outputs for snapshot state instructions. +""" + +from numpy import array +from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit +from qiskit.providers.aer.extensions.snapshot_probabilities import * + + +def snapshot_probabilities_labels_qubits(): + """Dictionary of labels and qubits for 3-qubit probability snapshots""" + return { + "[0]": [0], + "[1]": [1], + "[2]": [2], + "[0, 1]": [0, 1], + "[1, 0]": [1, 0], + "[0, 2]": [0, 2], + "[2, 0]": [2, 0], + "[1, 2]": [1, 2], + "[2, 1]": [2, 1], + "[0, 1, 2]": [0, 1, 2], + "[1, 2, 0]": [1, 2, 0], + "[2, 0, 1]": [2, 0, 1] + } + + +def snapshot_probabilities_circuits(post_measure=False): + """Snapshot Probabilities test circuits with deterministic counts""" + + circuits = [] + num_qubits = 3 + qr = QuantumRegister(num_qubits) + cr = ClassicalRegister(num_qubits) + regs = (qr, cr) + + # State |01+> + circuit = QuantumCircuit(*regs) + circuit.h(0) + circuit.x(1) + if not post_measure: + for label, qubits in snapshot_probabilities_labels_qubits().items(): + circuit.snapshot_probabilities(label, qubits) + circuit.barrier(qr) + circuit.measure(qr, cr) + circuit.barrier(qr) + if post_measure: + for label, qubits in snapshot_probabilities_labels_qubits().items(): + circuit.snapshot_probabilities(label, qubits) + circuits.append(circuit) + + # State |010> -i|101> + circuit = QuantumCircuit(*regs) + circuit.h(0) + circuit.sdg(0) + circuit.cx(0, 1) + circuit.cx(0, 2) + circuit.x(1) + if not post_measure: + for label, qubits in snapshot_probabilities_labels_qubits().items(): + circuit.snapshot_probabilities(label, qubits) + circuit.barrier(qr) + circuit.measure(qr, cr) + circuit.barrier(qr) + if post_measure: + for label, qubits in snapshot_probabilities_labels_qubits().items(): + circuit.snapshot_probabilities(label, qubits) + circuits.append(circuit) + + return circuits + + +def snapshot_probabilities_counts(shots): + """Snapshot Probabilities test circuits reference counts.""" + targets = [] + # State |01+> + targets.append({'0x2': shots / 2, '0x3': shots / 2}) + + # State |010> -i|101> + targets.append({'0x2': shots / 2, '0x5': shots / 2}) + return targets + + +def snapshot_probabilities_pre_meas_probs(): + """Snapshot Probabilities test circuits reference final probs""" + targets = [] + + # State |01+> + probs = { + "[0]": {'0x0': {'0x0': 0.5, '0x1': 0.5}}, + "[1]": {'0x0': {'0x1': 1.0}}, + "[2]": {'0x0': {'0x0': 1.0}}, + "[0, 1]": {'0x0': {'0x2': 0.5, '0x3': 0.5}}, + "[1, 0]": {'0x0': {'0x1': 0.5, '0x3': 0.5}}, + "[0, 2]": {'0x0': {'0x0': 0.5, '0x1': 0.5}}, + "[2, 0]": {'0x0': {'0x0': 0.5, '0x2': 0.5}}, + "[1, 2]": {'0x0': {'0x1': 1.0}}, + "[2, 1]": {'0x0': {'0x2': 1.0}}, + "[0, 1, 2]": {'0x0': {'0x2': 0.5, '0x3': 0.5}}, + "[1, 2, 0]": {'0x0': {'0x1': 0.5, '0x5': 0.5}}, + "[2, 0, 1]": {'0x0': {'0x4': 0.5, '0x6': 0.5}}, + } + targets.append(probs) + + # State |010> -i|101> + probs = { + "[0]": {'0x0': {'0x0': 0.5, '0x1': 0.5}}, + "[1]": {'0x0': {'0x0': 0.5, '0x1': 0.5}}, + "[2]": {'0x0': {'0x0': 0.5, '0x1': 0.5}}, + "[0, 1]": {'0x0': {'0x1': 0.5, '0x2': 0.5}}, + "[1, 0]": {'0x0': {'0x1': 0.5, '0x2': 0.5}}, + "[0, 2]": {'0x0': {'0x0': 0.5, '0x3': 0.5}}, + "[2, 0]": {'0x0': {'0x0': 0.5, '0x3': 0.5}}, + "[1, 2]": {'0x0': {'0x1': 0.5, '0x2': 0.5}}, + "[2, 1]": {'0x0': {'0x1': 0.5, '0x2': 0.5}}, + "[0, 1, 2]": {'0x0': {'0x2': 0.5, '0x5': 0.5}}, + "[1, 2, 0]": {'0x0': {'0x1': 0.5, '0x6': 0.5}}, + "[2, 0, 1]": {'0x0': {'0x3': 0.5, '0x4': 0.5}}, + } + targets.append(probs) + return targets + + +def snapshot_probabilities_post_meas_probs(): + """Snapshot Probabilities test circuits reference final statevector""" + targets = [] + + # State |01+> + probs = { + "[0]": {'0x2': {'0x0': 1.0}, '0x3': {'0x1': 1.0}}, + "[1]": {'0x2': {'0x1': 1.0}, '0x3': {'0x1': 1.0}}, + "[2]": {'0x2': {'0x0': 1.0}, '0x3': {'0x0': 1.0}}, + "[0, 1]": {'0x2': {'0x2': 1.0}, '0x3': {'0x3': 1.0}}, + "[1, 0]": {'0x2': {'0x1': 1.0}, '0x3': {'0x3': 1.0}}, + "[0, 2]": {'0x2': {'0x0': 1.0}, '0x3': {'0x1': 1.0}}, + "[2, 0]": {'0x2': {'0x0': 1.0}, '0x3': {'0x2': 1.0}}, + "[1, 2]": {'0x2': {'0x1': 1.0}, '0x3': {'0x1': 1.0}}, + "[2, 1]": {'0x2': {'0x2': 1.0}, '0x3': {'0x2': 1.0}}, + "[0, 1, 2]": {'0x2': {'0x2': 1.0}, '0x3': {'0x3': 1.0}}, + "[1, 2, 0]": {'0x2': {'0x1': 1.0}, '0x3': {'0x5': 1.0}}, + "[2, 0, 1]": {'0x2': {'0x4': 1.0}, '0x3': {'0x6': 1.0}}, + } + targets.append(probs) + + # State |010> -i|101> + probs = { + "[0]": {'0x2': {'0x0': 1.0}, '0x5': {'0x1': 1.0}}, + "[1]": {'0x2': {'0x1': 1.0}, '0x5': {'0x0': 1.0}}, + "[2]": {'0x2': {'0x0': 1.0}, '0x5': {'0x1': 1.0}}, + "[0, 1]": {'0x2': {'0x2': 1.0}, '0x5': {'0x1': 1.0}}, + "[1, 0]": {'0x2': {'0x1': 1.0}, '0x5': {'0x2': 1.0}}, + "[0, 2]": {'0x2': {'0x0': 1.0}, '0x5': {'0x3': 1.0}}, + "[2, 0]": {'0x2': {'0x0': 1.0}, '0x5': {'0x3': 1.0}}, + "[1, 2]": {'0x2': {'0x1': 1.0}, '0x5': {'0x2': 1.0}}, + "[2, 1]": {'0x2': {'0x2': 1.0}, '0x5': {'0x1': 1.0}}, + "[0, 1, 2]": {'0x2': {'0x2': 1.0}, '0x5': {'0x5': 1.0}}, + "[1, 2, 0]": {'0x2': {'0x1': 1.0}, '0x5': {'0x6': 1.0}}, + "[2, 0, 1]": {'0x2': {'0x4': 1.0}, '0x5': {'0x3': 1.0}}, + } + targets.append(probs) + return targets