diff --git a/CHANGELOG.md b/CHANGELOG.md index fcd16ce6b0..a1ac245919 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,9 +22,12 @@ Added ----- - Added tests for the Fredkin gate (#357) - Added tests for the cu1 gate (#360) +- Added tests for statevector and stabilizer snapshots (\#355) Changed ------- +- Stabilizer snapshot returns stabilizer instead of full Clifford table (\#355) +- Signature of SnapshotStatevector and SnapshotStabilizer (\#355) - Changed all names from tensor_network_state to matrix_product_state (\#356) - Update device noise model to consume asymmetric readout errors from backends (\#354) - Update device noise model to use gate_length (\#352) diff --git a/qiskit/providers/aer/extensions/__init__.py b/qiskit/providers/aer/extensions/__init__.py index a30dec755a..34ad4b0c62 100644 --- a/qiskit/providers/aer/extensions/__init__.py +++ b/qiskit/providers/aer/extensions/__init__.py @@ -14,10 +14,9 @@ Snapshot extensions for Quantum Circuits """ -from . import snapshot -from .snapshot import Snapshot -from . import snapshot_statevector -from . import snapshot_stabilizer -from . import snapshot_density_matrix -from . import snapshot_probabilities -from . import snapshot_expectation_value +from .snapshot import * +from .snapshot_statevector import * +from .snapshot_stabilizer import * +from .snapshot_density_matrix import * +from .snapshot_probabilities import * +from .snapshot_expectation_value import * diff --git a/qiskit/providers/aer/extensions/snapshot.py b/qiskit/providers/aer/extensions/snapshot.py index ec5e061526..23539bacbb 100644 --- a/qiskit/providers/aer/extensions/snapshot.py +++ b/qiskit/providers/aer/extensions/snapshot.py @@ -32,12 +32,14 @@ def __init__(self, num_clbits=0, params=None): """Create new snapshot instruction. + Args: label (str): the snapshot label for result data. snapshot_type (str): the type of the snapshot. num_qubits (int): the number of qubits for the snapshot type [Default: 0]. num_clbits (int): the number of classical bits for the snapshot type [Default: 0]. params (list or None): the parameters for snapshot_type [Default: None]. + Raises: ExtensionError: if snapshot label is invalid. """ diff --git a/qiskit/providers/aer/extensions/snapshot_stabilizer.py b/qiskit/providers/aer/extensions/snapshot_stabilizer.py index 93d433b402..86ce2ca5c8 100644 --- a/qiskit/providers/aer/extensions/snapshot_stabilizer.py +++ b/qiskit/providers/aer/extensions/snapshot_stabilizer.py @@ -21,38 +21,49 @@ class SnapshotStabilizer(Snapshot): """Snapshot instruction for stabilizer method of Qasm simulator.""" - def __init__(self, - label, - num_qubits=0, - num_clbits=0, - params=None): + def __init__(self, label, num_qubits=0): + """Create a stabilizer state snapshot instruction. - super().__init__(label, 'stabilizer', num_qubits, num_clbits, params) + Args: + label (str): the snapshot label. + num_qubits (int): the instruction barrier size [Default: 0]. + Raises: + ExtensionError: if snapshot is invalid. + + Additional Information: + This snapshot is always performed on all qubits in a circuit. + The number of qubits parameter specifies the size of the + instruction as a barrier and should be set to the number of + qubits in the circuit. + """ + super().__init__(label, snapshot_type='stabilizer', num_qubits=num_qubits) + + +def snapshot_stabilizer(self, label): + """Take a stabilizer snapshot of the simulator state. -def snapshot_stabilizer(self, - label, - qubits=None, - params=None): - """Take a snapshot of the internal simulator representation. - Works on all qubits, and prevents reordering (like barrier). Args: - label (str): a snapshot label to report the result - qubits (list or None): the qubits to apply snapshot to [Default: None]. - params (list or None): the parameters for snapshot_type [Default: None]. + label (str): a snapshot label to report the result. + Returns: - QuantumCircuit: with attached command + QuantumCircuit: with attached instruction. + Raises: - ExtensionError: malformed command + ExtensionError: if snapshot is invalid. + + Additional Information: + This snapshot is always performed on all qubits in a circuit. + The number of qubits parameter specifies the size of the + instruction as a barrier and should be set to the number of + qubits in the circuit. """ - snapshot_register = Snapshot.define_snapshot_register(self, label, qubits) + snapshot_register = Snapshot.define_snapshot_register(self, label) return self.append( - SnapshotStabilizer( - label, - num_qubits=len(snapshot_register), - params=params), snapshot_register) + SnapshotStabilizer(label, num_qubits=len(snapshot_register)), + snapshot_register) QuantumCircuit.snapshot_stabilizer = snapshot_stabilizer diff --git a/qiskit/providers/aer/extensions/snapshot_statevector.py b/qiskit/providers/aer/extensions/snapshot_statevector.py index d5a5aa3a9f..19bf1550d1 100644 --- a/qiskit/providers/aer/extensions/snapshot_statevector.py +++ b/qiskit/providers/aer/extensions/snapshot_statevector.py @@ -21,38 +21,50 @@ class SnapshotStatevector(Snapshot): """ Snapshot instruction for statevector snapshot type """ - def __init__(self, - label, - num_qubits=0, - num_clbits=0, - params=None): + def __init__(self, label, num_qubits=0): + """Create a statevector state snapshot instruction. - super().__init__(label, 'statevector', num_qubits, num_clbits, params) + Args: + label (str): the snapshot label. + num_qubits (int): the instruction barrier size [Default: 0]. + Raises: + ExtensionError: if snapshot is invalid. + + Additional Information: + This snapshot is always performed on all qubits in a circuit. + The number of qubits parameter specifies the size of the + instruction as a barrier and should be set to the number of + qubits in the circuit. + """ + super().__init__(label, snapshot_type='statevector', num_qubits=num_qubits) + + +def snapshot_statevector(self, label): + """Take a statevector snapshot of the simulator state. -def snapshot_statevector(self, - label, - qubits=None, - params=None): - """Take a statevector snapshot of the internal simulator representation. - Works on all qubits, and prevents reordering (like barrier). Args: - label (str): a snapshot label to report the result - qubits (list or None): the qubits to apply snapshot to [Default: None]. - params (list or None): the parameters for snapshot_type [Default: None]. + label (str): a snapshot label to report the result. + Returns: - QuantumCircuit: with attached command + QuantumCircuit: with attached instruction. + Raises: - ExtensionError: malformed command - """ + ExtensionError: if snapshot is invalid. - snapshot_register = Snapshot.define_snapshot_register(self, label, qubits) + Additional Information: + This snapshot is always performed on all qubits in a circuit. + The number of qubits parameter specifies the size of the + instruction as a barrier and should be set to the number of + qubits in the circuit. + """ + # Statevector snapshot acts as a barrier across all qubits in the + # circuit + snapshot_register = Snapshot.define_snapshot_register(self, label) return self.append( - SnapshotStatevector( - label, - num_qubits=len(snapshot_register), - params=params), snapshot_register) + SnapshotStatevector(label, num_qubits=len(snapshot_register)), + snapshot_register) QuantumCircuit.snapshot_statevector = snapshot_statevector diff --git a/src/simulators/qasm/qasm_controller.hpp b/src/simulators/qasm/qasm_controller.hpp index d9a2588e69..ba33f3210a 100755 --- a/src/simulators/qasm/qasm_controller.hpp +++ b/src/simulators/qasm/qasm_controller.hpp @@ -589,6 +589,7 @@ void QasmController::set_parallelization_circuit(const Circuit& circ, const auto method = simulation_method(circ, noise_model, false); switch (method) { case Method::statevector: + case Method::stabilizer: case Method::matrix_product_state: { if ((noise_model.is_ideal() || !noise_model.has_quantum_errors()) && check_measure_sampling_opt(circ, Method::statevector).first) { diff --git a/src/simulators/stabilizer/pauli.hpp b/src/simulators/stabilizer/pauli.hpp index 8bc6ffedd6..ecf0ec5673 100644 --- a/src/simulators/stabilizer/pauli.hpp +++ b/src/simulators/stabilizer/pauli.hpp @@ -59,19 +59,22 @@ Pauli::Pauli(const std::string &label) { const auto num_qubits = label.size(); X = BV::BinaryVector(num_qubits); Z = BV::BinaryVector(num_qubits); + // The label corresponds to tensor product order + // So the first element of label is the last qubit and vice versa for (size_t i =0; i < num_qubits; i++) { + const auto qubit_i = num_qubits - 1 - i; switch (label[i]) { case 'I': break; case 'X': - X.set1(i); + X.set1(qubit_i); break; case 'Y': - X.set1(i); - Z.set1(i); + X.set1(qubit_i); + Z.set1(qubit_i); break; case 'Z': - Z.set1(i); + Z.set1(qubit_i); break; default: throw std::invalid_argument("Invalid Pauli label"); @@ -86,13 +89,14 @@ std::string Pauli::str() const { throw std::runtime_error("Pauli::str X and Z vectors are different length."); std::string label; for (size_t i =0; i < num_qubits; i++) { - if (!X[i] && !Z[i]) + const auto qubit_i = num_qubits - 1 - i; + if (!X[qubit_i] && !Z[qubit_i]) label.push_back('I'); - else if (X[i] && !Z[i]) + else if (X[qubit_i] && !Z[qubit_i]) label.push_back('X'); - else if (X[i] && Z[i]) + else if (X[qubit_i] && Z[qubit_i]) label.push_back('Y'); - else if (!X[i] && Z[i]) + else if (!X[qubit_i] && Z[qubit_i]) label.push_back('Z'); } return label; diff --git a/src/simulators/stabilizer/stabilizer_state.hpp b/src/simulators/stabilizer/stabilizer_state.hpp index a256ae478a..6056cc2798 100644 --- a/src/simulators/stabilizer/stabilizer_state.hpp +++ b/src/simulators/stabilizer/stabilizer_state.hpp @@ -150,6 +150,10 @@ class State : public Base::State { // should be left in the pre-snapshot state. //----------------------------------------------------------------------- + // Snapshot the stabilizer state of the simulator. + // This returns a list of stabilizer generators + void snapshot_stabilizer(const Operations::Op &op, OutputData &data); + // Snapshot current qubit probabilities for a measurement (average) void snapshot_probabilities(const Operations::Op &op, OutputData &data, @@ -424,7 +428,7 @@ void State::apply_snapshot(const Operations::Op &op, op.name + "\'."); switch (it->second) { case Snapshots::stabilizer: - BaseState::snapshot_state(op, data, "stabilizer"); + snapshot_stabilizer(op, data); break; case Snapshots::cmemory: BaseState::snapshot_creg_memory(op, data); @@ -446,6 +450,17 @@ void State::apply_snapshot(const Operations::Op &op, } +void State::snapshot_stabilizer(const Operations::Op &op, OutputData &data) { + // We don't want to snapshot the full Clifford table, only the + // stabilizer part. First Convert simulator clifford table to JSON + json_t clifford = BaseState::qreg_; + // Then extract the stabilizer generator list + data.add_singleshot_snapshot("stabilizer", + op.string_params[0], + clifford["stabilizers"]); +} + + void State::snapshot_probabilities(const Operations::Op &op, OutputData &data, bool variance) { diff --git a/test/terra/backends/qasm_simulator/qasm_snapshot.py b/test/terra/backends/qasm_simulator/qasm_snapshot.py new file mode 100644 index 0000000000..a1a413785d --- /dev/null +++ b/test/terra/backends/qasm_simulator/qasm_snapshot.py @@ -0,0 +1,347 @@ +# 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. +""" +QasmSimulator Integration Tests for Snapshot instructions +""" + +import numpy as np + +from qiskit.compiler import assemble +from qiskit.quantum_info.operators import Pauli +from qiskit.providers.aer import QasmSimulator +from qiskit.providers.aer import AerError + +from test.terra.reference.ref_snapshot_state import ( + snapshot_state_circuits_deterministic, snapshot_state_counts_deterministic, + snapshot_state_pre_measure_statevector_deterministic, + snapshot_state_post_measure_statevector_deterministic, + snapshot_state_circuits_nondeterministic, + snapshot_state_counts_nondeterministic, + snapshot_state_pre_measure_statevector_nondeterministic, + snapshot_state_post_measure_statevector_nondeterministic) + + +class QasmSnapshotStatevectorTests: + """QasmSimulator snapshot statevector tests.""" + + SIMULATOR = QasmSimulator() + SUPPORTED_QASM_METHODS = [ + 'automatic', 'statevector', 'matrix_product_state' + ] + BACKEND_OPTS = {} + + def statevector_snapshots(self, data, label): + """Format snapshots as list of Numpy arrays""" + # Check snapshot entry exists in data + self.assertIn("snapshots", data) + self.assertIn("statevector", data["snapshots"]) + self.assertIn(label, data["snapshots"]["statevector"]) + # Format output as list of numpy array + snaps = data["snapshots"]["statevector"][label] + statevecs = [] + for snap in snaps: + svec = np.array(snap) + statevecs.append(svec[:, 0] + 1j * svec[:, 1]) + return statevecs + + def test_snapshot_statevector_pre_measure_det(self): + """Test snapshot statevector before deterministic final measurement""" + shots = 10 + label = "snap" + counts_targets = snapshot_state_counts_deterministic(shots) + statevec_targets = snapshot_state_pre_measure_statevector_deterministic( + ) + circuits = snapshot_state_circuits_deterministic(label, + 'statevector', + post_measure=False) + + qobj = assemble(circuits, self.SIMULATOR, shots=shots) + job = self.SIMULATOR.run(qobj, + backend_options=self.BACKEND_OPTS, + validate=False) + method = self.BACKEND_OPTS.get('method', 'automatic') + if method not in QasmSnapshotStatevectorTests.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) + # Check snapshots + for j, circuit in enumerate(circuits): + data = result.data(circuit) + snaps = self.statevector_snapshots(data, label) + self.assertTrue(len(snaps), 1) + target = statevec_targets[j] + value = snaps[0] + self.assertTrue(np.allclose(value, target)) + + def test_snapshot_statevector_pre_measure_nondet(self): + """Test snapshot statevector before non-deterministic final measurement""" + shots = 100 + label = "snap" + counts_targets = snapshot_state_counts_nondeterministic(shots) + statevec_targets = snapshot_state_pre_measure_statevector_nondeterministic( + ) + circuits = snapshot_state_circuits_nondeterministic(label, + 'statevector', + post_measure=False) + + qobj = assemble(circuits, self.SIMULATOR, shots=shots) + job = self.SIMULATOR.run(qobj, + backend_options=self.BACKEND_OPTS, + validate=False) + method = self.BACKEND_OPTS.get('method', 'automatic') + if method not in QasmSnapshotStatevectorTests.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.2 * shots) + # Check snapshots + for j, circuit in enumerate(circuits): + data = result.data(circuit) + snaps = self.statevector_snapshots(data, label) + self.assertTrue(len(snaps), 1) + target = statevec_targets[j] + value = snaps[0] + self.assertTrue(np.allclose(value, target)) + + def test_snapshot_statevector_post_measure_det(self): + """Test snapshot statevector after deterministic final measurement""" + shots = 10 + label = "snap" + counts_targets = snapshot_state_counts_deterministic(shots) + statevec_targets = snapshot_state_post_measure_statevector_deterministic( + ) + circuits = snapshot_state_circuits_deterministic(label, + 'statevector', + post_measure=True) + + qobj = assemble(circuits, self.SIMULATOR, memory=True, shots=shots) + job = self.SIMULATOR.run(qobj, + backend_options=self.BACKEND_OPTS, + validate=False) + method = self.BACKEND_OPTS.get('method', 'automatic') + if method not in QasmSnapshotStatevectorTests.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) + # Check snapshots + for i, circuit in enumerate(circuits): + data = result.data(circuit) + snaps = self.statevector_snapshots(data, label) + for j, mem in enumerate(data['memory']): + target = statevec_targets[i].get(mem) + self.assertTrue(np.allclose(snaps[j], target)) + + def test_snapshot_statevector_post_measure_nondet(self): + """Test snapshot statevector after non-deterministic final measurement""" + shots = 100 + label = "snap" + counts_targets = snapshot_state_counts_nondeterministic(shots) + statevec_targets = snapshot_state_post_measure_statevector_nondeterministic( + ) + circuits = snapshot_state_circuits_nondeterministic(label, + 'statevector', + post_measure=True) + + qobj = assemble(circuits, self.SIMULATOR, memory=True, shots=shots) + job = self.SIMULATOR.run(qobj, + backend_options=self.BACKEND_OPTS, + validate=False) + method = self.BACKEND_OPTS.get('method', 'automatic') + if method not in QasmSnapshotStatevectorTests.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.2 * shots) + # Check snapshots + for i, circuit in enumerate(circuits): + data = result.data(circuit) + snaps = self.statevector_snapshots(data, label) + for j, mem in enumerate(data['memory']): + target = statevec_targets[i].get(mem) + self.assertTrue(np.allclose(snaps[j], target)) + + +class QasmSnapshotStabilizerTests: + """QasmSimulator method snapshot stabilizer tests.""" + + SIMULATOR = QasmSimulator() + SUPPORTED_QASM_METHODS = ['automatic', 'stabilizer'] + BACKEND_OPTS = {} + + def stabilizer_snapshots(self, data, label): + """Format snapshots as list of Numpy arrays""" + # Check snapshot entry exists in data + self.assertIn("snapshots", data) + self.assertIn("stabilizer", data["snapshots"]) + self.assertIn(label, data["snapshots"]["stabilizer"]) + # Format output as list of numpy array + return data["snapshots"]["stabilizer"][label] + + @staticmethod + def stabilizes_statevector(stabilizer, statevector): + """Return True if two stabilizer states are equal.""" + # Get stabilizer and destabilizers and convert to sets + for stab in stabilizer: + if stab[0] == '-': + pauli_mat = -1 * Pauli.from_label(stab[1:]).to_matrix() + else: + pauli_mat = Pauli.from_label(stab).to_matrix() + val = statevector.conj().dot(pauli_mat.dot(statevector)) + if not np.isclose(val, 1): + return False + return True + + def test_snapshot_stabilizer_pre_measure_det(self): + """Test snapshot stabilizer before deterministic final measurement""" + shots = 10 + label = "snap" + counts_targets = snapshot_state_counts_deterministic(shots) + statevec_targets = snapshot_state_pre_measure_statevector_deterministic( + ) + circuits = snapshot_state_circuits_deterministic(label, + 'stabilizer', + post_measure=False) + + qobj = assemble(circuits, self.SIMULATOR, shots=shots) + job = self.SIMULATOR.run(qobj, + backend_options=self.BACKEND_OPTS, + validate=False) + method = self.BACKEND_OPTS.get('method', 'automatic') + if method not in QasmSnapshotStabilizerTests.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) + # Check snapshots + for j, circuit in enumerate(circuits): + data = result.data(circuit) + snaps = self.stabilizer_snapshots(data, label) + self.assertEqual(len(snaps), 1) + statevec = statevec_targets[j] + stabilizer = snaps[0] + self.assertTrue( + self.stabilizes_statevector(stabilizer, statevec)) + + def test_snapshot_stabilizer_pre_measure_nondet(self): + """Test snapshot stabilizer before non-deterministic final measurement""" + shots = 100 + label = "snap" + counts_targets = snapshot_state_counts_nondeterministic(shots) + statevec_targets = snapshot_state_pre_measure_statevector_nondeterministic( + ) + circuits = snapshot_state_circuits_nondeterministic(label, + 'stabilizer', + post_measure=False) + + qobj = assemble(circuits, self.SIMULATOR, shots=shots) + job = self.SIMULATOR.run(qobj, + backend_options=self.BACKEND_OPTS, + validate=False) + method = self.BACKEND_OPTS.get('method', 'automatic') + if method not in QasmSnapshotStabilizerTests.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.2 * shots) + # Check snapshots + for j, circuit in enumerate(circuits): + data = result.data(circuit) + snaps = self.stabilizer_snapshots(data, label) + self.assertEqual(len(snaps), 1) + statevec = statevec_targets[j] + stabilizer = snaps[0] + self.assertTrue( + self.stabilizes_statevector(stabilizer, statevec)) + + def test_snapshot_stabilizer_post_measure_det(self): + """Test snapshot stabilizer after deterministic final measurement""" + shots = 10 + label = "snap" + counts_targets = snapshot_state_counts_deterministic(shots) + statevec_targets = snapshot_state_post_measure_statevector_deterministic( + ) + circuits = snapshot_state_circuits_deterministic(label, + 'stabilizer', + post_measure=True) + + qobj = assemble(circuits, self.SIMULATOR, memory=True, shots=shots) + job = self.SIMULATOR.run(qobj, + backend_options=self.BACKEND_OPTS, + validate=False) + method = self.BACKEND_OPTS.get('method', 'automatic') + if method not in QasmSnapshotStabilizerTests.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) + # Check snapshots + for i, circuit in enumerate(circuits): + data = result.data(circuit) + snaps = self.stabilizer_snapshots(data, label) + for j, mem in enumerate(data['memory']): + statevec = statevec_targets[i].get(mem) + stabilizer = snaps[j] + self.assertTrue( + self.stabilizes_statevector(stabilizer, statevec)) + + def test_snapshot_stabilizer_post_measure_nondet(self): + """Test snapshot stabilizer after non-deterministic final measurement""" + shots = 100 + label = "snap" + counts_targets = snapshot_state_counts_nondeterministic(shots) + statevec_targets = snapshot_state_post_measure_statevector_nondeterministic( + ) + circuits = snapshot_state_circuits_nondeterministic(label, + 'stabilizer', + post_measure=True) + + qobj = assemble(circuits, self.SIMULATOR, memory=True, shots=shots) + job = self.SIMULATOR.run(qobj, + backend_options=self.BACKEND_OPTS, + validate=False) + method = self.BACKEND_OPTS.get('method', 'automatic') + if method not in QasmSnapshotStabilizerTests.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.2 * shots) + # Check snapshots + for i, circuit in enumerate(circuits): + data = result.data(circuit) + snaps = self.stabilizer_snapshots(data, label) + for j, mem in enumerate(data['memory']): + statevec = statevec_targets[i].get(mem) + stabilizer = snaps[j] + self.assertTrue( + self.stabilizes_statevector(stabilizer, statevec)) diff --git a/test/terra/backends/test_qasm_density_matrix_simulator.py b/test/terra/backends/test_qasm_density_matrix_simulator.py index d2579b47d4..ed12e99afe 100644 --- a/test/terra/backends/test_qasm_density_matrix_simulator.py +++ b/test/terra/backends/test_qasm_density_matrix_simulator.py @@ -44,6 +44,8 @@ from test.terra.backends.qasm_simulator.qasm_noise import QasmKrausNoiseTests # Other tests from test.terra.backends.qasm_simulator.qasm_method import QasmMethodTests +# Snapshot tests +from test.terra.backends.qasm_simulator.qasm_snapshot import QasmSnapshotStatevectorTests class TestQasmDensityMatrixSimulator(common.QiskitAerTestCase, @@ -68,7 +70,8 @@ class TestQasmDensityMatrixSimulator(common.QiskitAerTestCase, QasmReadoutNoiseTests, QasmPauliNoiseTests, QasmResetNoiseTests, - QasmKrausNoiseTests): + QasmKrausNoiseTests, + QasmSnapshotStatevectorTests): """QasmSimulator density_matrix method tests.""" BACKEND_OPTS = { diff --git a/test/terra/backends/test_qasm_matrix_product_state.py b/test/terra/backends/test_qasm_matrix_product_state.py index 459c3fef51..d7b658be62 100644 --- a/test/terra/backends/test_qasm_matrix_product_state.py +++ b/test/terra/backends/test_qasm_matrix_product_state.py @@ -18,16 +18,20 @@ from test.terra import common from test.terra.backends.qasm_simulator.matrix_product_state_method import QasmMatrixProductStateMethodTests from test.terra.backends.qasm_simulator.matrix_product_state_measure import QasmMatrixProductStateMeasureTests - +# Snapshot tests +from test.terra.backends.qasm_simulator.qasm_snapshot import QasmSnapshotStatevectorTests class TestQasmMatrixProductStateSimulator(common.QiskitAerTestCase, QasmMatrixProductStateMethodTests, - QasmMatrixProductStateMeasureTests): + QasmMatrixProductStateMeasureTests, + #QasmSnapshotStatevectorTests, # FAILING + ): BACKEND_OPTS = { "seed_simulator": 314159, "method": "matrix_product_state" } + if __name__ == '__main__': unittest.main() diff --git a/test/terra/backends/test_qasm_simulator.py b/test/terra/backends/test_qasm_simulator.py index 267fb8704a..391d2ee149 100644 --- a/test/terra/backends/test_qasm_simulator.py +++ b/test/terra/backends/test_qasm_simulator.py @@ -40,6 +40,9 @@ # Noise model simulation tests from test.terra.backends.qasm_simulator.qasm_noise import QasmReadoutNoiseTests from test.terra.backends.qasm_simulator.qasm_noise import QasmPauliNoiseTests +# Snapshot tests +from test.terra.backends.qasm_simulator.qasm_snapshot import QasmSnapshotStatevectorTests +from test.terra.backends.qasm_simulator.qasm_snapshot import QasmSnapshotStabilizerTests # Other tests from test.terra.backends.qasm_simulator.qasm_method import QasmMethodTests from test.terra.backends.qasm_simulator.qasm_thread_management import QasmThreadManagementTests @@ -77,7 +80,9 @@ class TestQasmSimulator(common.QiskitAerTestCase, QasmQubitsTruncateTests, QasmResetNoiseTests, QasmKrausNoiseTests, - QasmBasicsTests): + QasmBasicsTests, + QasmSnapshotStatevectorTests, + QasmSnapshotStabilizerTests): """QasmSimulator automatic method tests.""" BACKEND_OPTS = { diff --git a/test/terra/backends/test_qasm_stabilizer_simulator.py b/test/terra/backends/test_qasm_stabilizer_simulator.py index 76b3ed1033..d13a054d86 100644 --- a/test/terra/backends/test_qasm_stabilizer_simulator.py +++ b/test/terra/backends/test_qasm_stabilizer_simulator.py @@ -30,6 +30,9 @@ from test.terra.backends.qasm_simulator.qasm_noise import QasmReadoutNoiseTests from test.terra.backends.qasm_simulator.qasm_noise import QasmPauliNoiseTests from test.terra.backends.qasm_simulator.qasm_noise import QasmResetNoiseTests +# Snapshot tests +from test.terra.backends.qasm_simulator.qasm_snapshot import QasmSnapshotStatevectorTests +from test.terra.backends.qasm_simulator.qasm_snapshot import QasmSnapshotStabilizerTests # Other tests from test.terra.backends.qasm_simulator.qasm_method import QasmMethodTests @@ -44,7 +47,9 @@ class TestQasmStabilizerSimulator(common.QiskitAerTestCase, QasmAlgorithmTests, QasmReadoutNoiseTests, QasmResetNoiseTests, - QasmPauliNoiseTests): + QasmPauliNoiseTests, + QasmSnapshotStatevectorTests, + QasmSnapshotStabilizerTests): """QasmSimulator stabilizer method tests.""" BACKEND_OPTS = { diff --git a/test/terra/backends/test_qasm_statevector_simulator.py b/test/terra/backends/test_qasm_statevector_simulator.py index 7e9de2b32e..d633f5db08 100644 --- a/test/terra/backends/test_qasm_statevector_simulator.py +++ b/test/terra/backends/test_qasm_statevector_simulator.py @@ -38,11 +38,13 @@ from test.terra.backends.qasm_simulator.qasm_algorithms import QasmAlgorithmTestsWaltzBasis from test.terra.backends.qasm_simulator.qasm_algorithms import QasmAlgorithmTestsMinimalBasis # Noise model simulation tests - from test.terra.backends.qasm_simulator.qasm_noise import QasmReadoutNoiseTests from test.terra.backends.qasm_simulator.qasm_noise import QasmPauliNoiseTests from test.terra.backends.qasm_simulator.qasm_noise import QasmResetNoiseTests from test.terra.backends.qasm_simulator.qasm_noise import QasmKrausNoiseTests +# Snapshot tests +from test.terra.backends.qasm_simulator.qasm_snapshot import QasmSnapshotStatevectorTests +from test.terra.backends.qasm_simulator.qasm_snapshot import QasmSnapshotStabilizerTests # Other tests from test.terra.backends.qasm_simulator.qasm_method import QasmMethodTests @@ -68,7 +70,9 @@ class TestQasmStatevectorSimulator(common.QiskitAerTestCase, QasmReadoutNoiseTests, QasmPauliNoiseTests, QasmResetNoiseTests, - QasmKrausNoiseTests): + QasmKrausNoiseTests, + QasmSnapshotStatevectorTests, + QasmSnapshotStabilizerTests): """QasmSimulator statevector method tests.""" BACKEND_OPTS = { diff --git a/test/terra/extensions/__init__.py b/test/terra/extensions/__init__.py new file mode 100644 index 0000000000..0e1a4c3082 --- /dev/null +++ b/test/terra/extensions/__init__.py @@ -0,0 +1,32 @@ +# 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. + +''' +Terra tests +''' + +import os + + +def load_tests(loader, standard_tests, pattern): + """ + test suite for unittest discovery + """ + this_dir = os.path.dirname(__file__) + if pattern in ['test*.py', '*_test.py']: + package_tests = loader.discover(start_dir=this_dir, pattern=pattern) + standard_tests.addTests(package_tests) + elif pattern in ['profile*.py', '*_profile.py']: + loader.testMethodPrefix = 'profile' + package_tests = loader.discover(start_dir=this_dir, pattern='test*.py') + standard_tests.addTests(package_tests) + return standard_tests diff --git a/test/terra/extensions/test_snapshot_stabilizer.py b/test/terra/extensions/test_snapshot_stabilizer.py new file mode 100644 index 0000000000..77e5d134a3 --- /dev/null +++ b/test/terra/extensions/test_snapshot_stabilizer.py @@ -0,0 +1,81 @@ +# 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. + + +import unittest + +from qiskit import QuantumCircuit, assemble +from qiskit.extensions.exceptions import ExtensionError +from qiskit.providers.aer.extensions.snapshot_stabilizer import SnapshotStabilizer + + +class TestSnapshotStabilizerExtension(unittest.TestCase): + """SnapshotStbilizer extension tests""" + + @staticmethod + def snapshot_circuit_instr(circ_qubits, label): + """Return QobjInstruction for circuit monkey patch method.""" + circuit = QuantumCircuit(circ_qubits) + circuit.snapshot_stabilizer(label) + qobj = assemble(circuit) + instr = qobj.experiments[0].instructions[0] + return instr + + def test_snapshot_label_raises(self): + """Test snapshot label must be str""" + self.assertRaises(ExtensionError, lambda: SnapshotStabilizer(1, 1)) + + def test_snapshot_name(self): + """Test snapshot instruction has correct name""" + instrs = [ + SnapshotStabilizer('snap', 1).assemble(), + self.snapshot_circuit_instr(1, 'snap') + ] + for instr in instrs: + self.assertTrue(hasattr(instr, 'name')) + self.assertEqual(instr.name, 'snapshot') + + def test_snapshot_type(self): + """Test snapshot instruction has correct type.""" + instrs = [ + SnapshotStabilizer('snap', 1).assemble(), + self.snapshot_circuit_instr(1, 'snap') + ] + for instr in instrs: + self.assertTrue(hasattr(instr, 'snapshot_type')) + self.assertEqual(instr.snapshot_type, 'stabilizer') + + def test_snapshot_label(self): + """Test snapshot instruction has correct label""" + for label in ['snap0', 'snap1', 'snap2']: + instrs = [ + SnapshotStabilizer(label, 1).assemble(), + self.snapshot_circuit_instr(1, label) + ] + for instr in instrs: + self.assertTrue(hasattr(instr, 'label')) + self.assertEqual(instr.label, label) + + def test_snapshot_qubits(self): + """Test snapshot instruction has correct qubits.""" + for j in range(1, 5): + instrs = [ + SnapshotStabilizer('snap', j).assemble(), + self.snapshot_circuit_instr(j, 'snap') + ] + for instr in instrs: + self.assertTrue(hasattr(instr, 'qubits')) + self.assertEqual(instr.qubits, list(range(j))) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/terra/extensions/test_snapshot_statevector.py b/test/terra/extensions/test_snapshot_statevector.py new file mode 100644 index 0000000000..29cada05e8 --- /dev/null +++ b/test/terra/extensions/test_snapshot_statevector.py @@ -0,0 +1,81 @@ +# 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. + + +import unittest + +from qiskit import QuantumCircuit, assemble +from qiskit.extensions.exceptions import ExtensionError +from qiskit.providers.aer.extensions.snapshot_statevector import SnapshotStatevector + + +class TestSnapshotStatevectorExtension(unittest.TestCase): + """SnapshotStatevector extension tests""" + + @staticmethod + def snapshot_circuit_instr(circ_qubits, label): + """Return QobjInstruction for circuit monkey patch method.""" + circuit = QuantumCircuit(circ_qubits) + circuit.snapshot_statevector(label) + qobj = assemble(circuit) + instr = qobj.experiments[0].instructions[0] + return instr + + def test_snapshot_label_raises(self): + """Test snapshot label must be str""" + self.assertRaises(ExtensionError, lambda: SnapshotStatevector(1, 1)) + + def test_snapshot_name(self): + """Test snapshot instruction has correct name""" + instrs = [ + SnapshotStatevector('snap', 1).assemble(), + self.snapshot_circuit_instr(1, 'snap') + ] + for instr in instrs: + self.assertTrue(hasattr(instr, 'name')) + self.assertEqual(instr.name, 'snapshot') + + def test_snapshot_type(self): + """Test snapshot instruction has correct type.""" + instrs = [ + SnapshotStatevector('snap', 1).assemble(), + self.snapshot_circuit_instr(1, 'snap') + ] + for instr in instrs: + self.assertTrue(hasattr(instr, 'snapshot_type')) + self.assertEqual(instr.snapshot_type, 'statevector') + + def test_snapshot_label(self): + """Test snapshot instruction has correct label""" + for label in ['snap0', 'snap1', 'snap2']: + instrs = [ + SnapshotStatevector(label, 1).assemble(), + self.snapshot_circuit_instr(1, label) + ] + for instr in instrs: + self.assertTrue(hasattr(instr, 'label')) + self.assertEqual(instr.label, label) + + def test_snapshot_qubits(self): + """Test snapshot instruction has correct qubits.""" + for j in range(1, 5): + instrs = [ + SnapshotStatevector('snap', j).assemble(), + self.snapshot_circuit_instr(j, 'snap') + ] + for instr in instrs: + self.assertTrue(hasattr(instr, 'qubits')) + self.assertEqual(instr.qubits, list(range(j))) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/terra/reference/ref_snapshot_state.py b/test/terra/reference/ref_snapshot_state.py new file mode 100644 index 0000000000..f54f02d055 --- /dev/null +++ b/test/terra/reference/ref_snapshot_state.py @@ -0,0 +1,179 @@ +# 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, sqrt +from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit +from qiskit.providers.aer.extensions.snapshot import Snapshot +from qiskit.providers.aer.extensions.snapshot_statevector import * + + +def snapshot_state_circuits_deterministic(snapshot_label='snap', + snapshot_type='statevector', + post_measure=False): + """Snapshot Statevector test circuits""" + + circuits = [] + num_qubits = 3 + qr = QuantumRegister(num_qubits) + cr = ClassicalRegister(num_qubits) + regs = (qr, cr) + + # State snapshot instruction acting on all qubits + snapshot = Snapshot(snapshot_label, snapshot_type, num_qubits) + + # Snapshot |000> + circuit = QuantumCircuit(*regs) + if not post_measure: + circuit.append(snapshot, qr) + circuit.barrier(qr) + circuit.measure(qr, cr) + if post_measure: + circuit.append(snapshot, qr) + circuits.append(circuit) + + # Snapshot |111> + circuit = QuantumCircuit(*regs) + circuit.x(qr) + if not post_measure: + circuit.append(snapshot, qr) + circuit.barrier(qr) + circuit.measure(qr, cr) + if post_measure: + circuit.append(snapshot, qr) + circuits.append(circuit) + + return circuits + + +def snapshot_state_counts_deterministic(shots): + """Snapshot Statevector test circuits reference counts.""" + targets = [] + # Snapshot |000> + targets.append({'0x0': shots}) + # Snapshot |111> + targets.append({'0x7': shots}) + return targets + + +def snapshot_state_pre_measure_statevector_deterministic(): + """Snapshot Statevector test circuits reference final statevector""" + targets = [] + # Snapshot |000> + targets.append(array([1, 0, 0, 0, 0, 0, 0, 0], dtype=complex)) + # Snapshot |111> + targets.append(array([0, 0, 0, 0, 0, 0, 0, 1], dtype=complex)) + return targets + + +def snapshot_state_post_measure_statevector_deterministic(): + """Snapshot Statevector test circuits reference final statevector""" + + targets = [] + # Snapshot |000> + targets.append({'0x0': array([1, 0, 0, 0, 0, 0, 0, 0], dtype=complex)}) + # Snapshot |111> + targets.append({'0x7': array([0, 0, 0, 0, 0, 0, 0, 1], dtype=complex)}) + return targets + + +def snapshot_state_circuits_nondeterministic(snapshot_label='snap', + snapshot_type='statevector', + post_measure=False): + """Snapshot Statevector test circuits""" + + circuits = [] + num_qubits = 3 + qr = QuantumRegister(num_qubits) + cr = ClassicalRegister(num_qubits) + regs = (qr, cr) + + # State snapshot instruction acting on all qubits + snapshot = Snapshot(snapshot_label, snapshot_type, num_qubits) + + # Snapshot |000> + i|111> + circuit = QuantumCircuit(*regs) + circuit.h(qr[0]) + circuit.s(qr[0]) + circuit.cx(qr[0], qr[1]) + circuit.cx(qr[0], qr[2]) + if not post_measure: + circuit.append(snapshot, qr) + circuit.barrier(qr) + circuit.measure(qr, cr) + if post_measure: + circuit.append(snapshot, qr) + circuits.append(circuit) + + # Snapshot |+++> + circuit = QuantumCircuit(*regs) + circuit.h(qr) + if not post_measure: + circuit.append(snapshot, qr) + circuit.barrier(qr) + circuit.measure(qr, cr) + if post_measure: + circuit.append(snapshot, qr) + circuits.append(circuit) + + return circuits + + +def snapshot_state_counts_nondeterministic(shots): + """Snapshot Statevector test circuits reference counts.""" + targets = [] + # Snapshot |000> + i|111> + targets.append({'0x0': shots/2, + '0x7': shots/2}) + # Snapshot |+++> + targets.append({'0x0': shots/8, + '0x1': shots/8, + '0x2': shots/8, + '0x3': shots/8, + '0x4': shots/8, + '0x5': shots/8, + '0x6': shots/8, + '0x7': shots/8}) + return targets + + +def snapshot_state_pre_measure_statevector_nondeterministic(): + """Snapshot Statevector test circuits reference final statevector""" + targets = [] + # Snapshot |000> + i|111> + targets.append(array([1, 0, 0, 0, 0, 0, 0, 1j], dtype=complex) / sqrt(2)) + # Snapshot |+++> + targets.append(array([1, 1, 1, 1, 1, 1, 1, 1], dtype=complex) / sqrt(8)) + return targets + + +def snapshot_state_post_measure_statevector_nondeterministic(): + """Snapshot Statevector test circuits reference final statevector""" + + targets = [] + # Snapshot |000> + i|111> + targets.append({'0x0': array([1, 0, 0, 0, 0, 0, 0, 0], dtype=complex), + '0x7': array([0, 0, 0, 0, 0, 0, 0, 1j], dtype=complex)}) + # Snapshot |+++> + targets.append({'0x0': array([1, 0, 0, 0, 0, 0, 0, 0], dtype=complex), + '0x1': array([0, 1, 0, 0, 0, 0, 0, 0], dtype=complex), + '0x2': array([0, 0, 1, 0, 0, 0, 0, 0], dtype=complex), + '0x3': array([0, 0, 0, 1, 0, 0, 0, 0], dtype=complex), + '0x4': array([0, 0, 0, 0, 1, 0, 0, 0], dtype=complex), + '0x5': array([0, 0, 0, 0, 0, 1, 0, 0], dtype=complex), + '0x6': array([0, 0, 0, 0, 0, 0, 1, 0], dtype=complex), + '0x7': array([0, 0, 0, 0, 0, 0, 0, 1], dtype=complex)}) + return targets +