diff --git a/qiskit/providers/aer/backends/qasm_simulator.py b/qiskit/providers/aer/backends/qasm_simulator.py index e9f0eefdab..adf49d8f7b 100644 --- a/qiskit/providers/aer/backends/qasm_simulator.py +++ b/qiskit/providers/aer/backends/qasm_simulator.py @@ -295,14 +295,14 @@ class QasmSimulator(AerBackend): 'initialize', 'delay', 'pauli', 'mcx_gray', # Custom instructions 'kraus', 'roerror', 'snapshot', 'save_expval', 'save_expval_var', - 'save_probabilities', 'save_probabilities_dict', + 'save_probabilities', 'save_probabilities_dict', 'save_state', 'save_density_matrix', 'save_statevector', 'save_amplitudes', 'save_amplitudes_sq', 'save_stabilizer' ]), 'custom_instructions': sorted([ 'roerror', 'kraus', 'snapshot', 'save_expval', 'save_expval_var', 'save_probabilities', 'save_probabilities_dict', - 'save_density_matrix', 'save_statevector', + 'save_state', 'save_density_matrix', 'save_statevector', 'save_amplitudes', 'save_amplitudes_sq', 'save_stabilizer']), 'gates': [] } @@ -481,7 +481,7 @@ def _method_configuration(method=None): config.custom_instructions = sorted([ 'roerror', 'snapshot', 'kraus', 'superop', 'save_expval', 'save_expval_var', 'save_probabilities', 'save_probabilities_dict', 'save_density_matrix', - 'save_amplitudes_sq']) + 'save_amplitudes_sq', 'save_state']) config.basis_gates = sorted([ 'u1', 'u2', 'u3', 'u', 'p', 'r', 'rx', 'ry', 'rz', 'id', 'x', 'y', 'z', 'h', 's', 'sdg', 'sx', 't', 'tdg', 'swap', 'cx', @@ -511,7 +511,7 @@ def _method_configuration(method=None): config.custom_instructions = sorted([ 'roerror', 'snapshot', 'save_expval', 'save_expval_var', 'save_probabilities', 'save_probabilities_dict', - 'save_amplitudes_sq', 'save_stabilizer']) + 'save_amplitudes_sq', 'save_stabilizer', 'save_state']) config.basis_gates = sorted([ 'id', 'x', 'y', 'z', 'h', 's', 'sdg', 'sx', 'cx', 'cy', 'cz', 'swap', 'delay', diff --git a/qiskit/providers/aer/backends/statevector_simulator.py b/qiskit/providers/aer/backends/statevector_simulator.py index f83788bc77..7791db02b8 100644 --- a/qiskit/providers/aer/backends/statevector_simulator.py +++ b/qiskit/providers/aer/backends/statevector_simulator.py @@ -130,7 +130,7 @@ class StatevectorSimulator(AerBackend): 'initialize', 'kraus', 'roerror', 'delay', 'pauli', 'save_expval', 'save_density_matrix', 'save_statevector', 'save_probs', 'save_probs_ket', 'save_amplitudes', - 'save_amplitudes_sq' + 'save_amplitudes_sq', 'save_state' ], 'gates': [] } diff --git a/qiskit/providers/aer/backends/unitary_simulator.py b/qiskit/providers/aer/backends/unitary_simulator.py index 7f141c1291..abed526b3c 100644 --- a/qiskit/providers/aer/backends/unitary_simulator.py +++ b/qiskit/providers/aer/backends/unitary_simulator.py @@ -133,7 +133,7 @@ class UnitarySimulator(AerBackend): 'rzz', 'rzx', 'ccx', 'cswap', 'mcx', 'mcy', 'mcz', 'mcsx', 'mcp', 'mcu1', 'mcu2', 'mcu3', 'mcrx', 'mcry', 'mcrz', 'mcr', 'mcswap', 'unitary', 'diagonal', 'multiplexer', 'delay', 'pauli', - 'save_unitary' + 'save_unitary', 'save_state' ], 'gates': [] } diff --git a/qiskit/providers/aer/library/__init__.py b/qiskit/providers/aer/library/__init__.py index 6eb2848230..61742e881a 100644 --- a/qiskit/providers/aer/library/__init__.py +++ b/qiskit/providers/aer/library/__init__.py @@ -33,6 +33,7 @@ .. autosummary:: :toctree: ../stubs/ + SaveState SaveExpectationValue SaveExpectationValueVariance SaveProbabilities @@ -53,6 +54,7 @@ .. autosummary:: :toctree: ../stubs/ + save_state save_expectation_value save_expectation_value_variance save_probabilities @@ -95,12 +97,14 @@ state. """ -__all__ = ['SaveExpectationValue', 'SaveExpectationValueVariance', +__all__ = ['SaveState', + 'SaveExpectationValue', 'SaveExpectationValueVariance', 'SaveProbabilities', 'SaveProbabilitiesDict', 'SaveStatevector', 'SaveStatevectorDict', 'SaveDensityMatrix', 'SaveAmplitudes', 'SaveAmplitudesSquared', 'SaveUnitary', 'SaveStabilizer'] +from .save_state import SaveState, save_state from .save_expectation_value import ( SaveExpectationValue, save_expectation_value, SaveExpectationValueVariance, save_expectation_value_variance) @@ -111,5 +115,5 @@ from .save_density_matrix import SaveDensityMatrix, save_density_matrix from .save_amplitudes import (SaveAmplitudes, save_amplitudes, SaveAmplitudesSquared, save_amplitudes_squared) -from .save_stabilizer import (SaveStabilizer, save_stabilizer) -from .save_unitary import (SaveUnitary, save_unitary) +from .save_stabilizer import SaveStabilizer, save_stabilizer +from .save_unitary import SaveUnitary, save_unitary diff --git a/qiskit/providers/aer/library/save_state.py b/qiskit/providers/aer/library/save_state.py new file mode 100644 index 0000000000..ce22b6208b --- /dev/null +++ b/qiskit/providers/aer/library/save_state.py @@ -0,0 +1,85 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2018, 2021. +# +# 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. +""" +Simulator instruction to save simulator state. +""" + +from qiskit.circuit import QuantumCircuit +from .save_data import SaveSingleData, default_qubits + + +class SaveState(SaveSingleData): + """Save simulator state + + The format of the saved state depends on the simulation method used. + """ + def __init__(self, num_qubits, + label=None, + pershot=False, + conditional=False): + """Create new instruction to save the simualtor state. + + The format of the saved state depends on the simulation method used. + + Args: + num_qubits (int): the number of qubits of the + label (str or None): Optional, the key for retrieving saved data + from results. If None the key will be the + state type of the simulator. + pershot (bool): if True save a list of states for each + shot of the simulation rather than a single + state [Default: False]. + conditional (bool): if True save data conditional on the current + classical register values [Default: False]. + + .. note:: + + This save instruction must always be performed on the full width of + qubits in a circuit, otherwise an exception will be raised during + simulation. + """ + if label is None: + label = '_method_' + super().__init__('save_state', num_qubits, label, + pershot=pershot, + conditional=conditional) + + +def save_state(self, label=None, pershot=False, conditional=False): + """Save the current simulator quantum state. + + Args: + label (str or None): Optional, the key for retrieving saved data + from results. If None the key will be the + state type of the simulator. + pershot (bool): if True save a list of statevectors for each + shot of the simulation [Default: False]. + conditional (bool): if True save pershot data conditional on the + current classical register values + [Default: False]. + + Returns: + QuantumCircuit: with attached instruction. + + .. note: + + This instruction is always defined across all qubits in a circuit. + """ + qubits = default_qubits(self) + instr = SaveState(len(qubits), + label=label, + pershot=pershot, + conditional=conditional) + return self.append(instr, qubits) + + +QuantumCircuit.save_state = save_state diff --git a/releasenotes/notes/save-data-instructions-24b127612c9f6502.yaml b/releasenotes/notes/save-data-instructions-24b127612c9f6502.yaml index bd5bd83796..2261807c93 100644 --- a/releasenotes/notes/save-data-instructions-24b127612c9f6502.yaml +++ b/releasenotes/notes/save-data-instructions-24b127612c9f6502.yaml @@ -95,3 +95,13 @@ features: This instruction can also be appended to a quantum circuit by using the :class:`~qiskit.providers.aer.library.save_unitary`` circuit methods which is added to ``QuantumCircuit`` when importing Aer. + - | + Adds a :class:`qiskit.providers.aer.library.SaveState` + circuit instruction for saving the state of the + :class:`~qiskit.providers.aer.QasmSimulator`. The format of the saved + state depends on the simulation method + (eg. statevector, density matrix, etc). + + This instruction can also be appended to a quantum circuit by using the + :class:`~qiskit.providers.aer.library.save_state`` circuit + methods which is added to ``QuantumCircuit`` when importing Aer. diff --git a/src/framework/operations.hpp b/src/framework/operations.hpp index 0c22520ceb..63f1115859 100755 --- a/src/framework/operations.hpp +++ b/src/framework/operations.hpp @@ -41,7 +41,7 @@ enum class OpType { // Noise instructions kraus, superop, roerror, noise_switch, // Save instructions - save_expval, save_expval_var, save_statevec, save_statevec_ket, + save_state, save_expval, save_expval_var, save_statevec, save_statevec_ket, save_densmat, save_probs, save_probs_ket, save_amps, save_amps_sq, save_stabilizer, save_unitary }; @@ -51,7 +51,7 @@ enum class DataSubType { }; static const std::unordered_set SAVE_TYPES = { - OpType::save_expval, OpType::save_expval_var, + OpType::save_state, OpType::save_expval, OpType::save_expval_var, OpType::save_statevec, OpType::save_statevec_ket, OpType::save_densmat, OpType::save_probs, OpType::save_probs_ket, OpType::save_amps, OpType::save_amps_sq, OpType::save_stabilizer, @@ -75,6 +75,9 @@ inline std::ostream& operator<<(std::ostream& stream, const OpType& type) { case OpType::barrier: stream << "barrier"; break; + case OpType::save_state: + stream << "save_state"; + break; case OpType::save_expval: stream << "save_expval"; break; @@ -515,6 +518,8 @@ Op json_to_op(const json_t &js) { if (name == "superop") return json_to_op_superop(js); // Save + if (name == "save_state") + return json_to_op_save_default(js, OpType::save_state); if (name == "save_expval") return json_to_op_save_expval(js, false); if (name == "save_expval_var") diff --git a/src/simulators/density_matrix/densitymatrix_state.hpp b/src/simulators/density_matrix/densitymatrix_state.hpp index 25c6b80322..faa59014f1 100644 --- a/src/simulators/density_matrix/densitymatrix_state.hpp +++ b/src/simulators/density_matrix/densitymatrix_state.hpp @@ -50,7 +50,7 @@ const Operations::OpSet StateOpSet( OpType::superop, OpType::save_expval, OpType::save_expval_var, OpType::save_densmat, OpType::save_probs, OpType::save_probs_ket, - OpType::save_amps_sq + OpType::save_amps_sq, OpType::save_state }, // Gates {"U", "CX", "u1", "u2", "u3", "u", "cx", "cy", "cz", @@ -189,6 +189,11 @@ class State : public Base::State { // Save data instructions //----------------------------------------------------------------------- + // Save the current full density matrix + void apply_save_state(const Operations::Op &op, + ExperimentResult &result, + bool last_op = false); + // Save the current density matrix or reduced density matrix void apply_save_density_matrix(const Operations::Op &op, ExperimentResult &result, @@ -489,6 +494,9 @@ void State::apply_ops(const std::vector &ops, case OpType::save_expval_var: BaseState::apply_save_expval(op, result); break; + case OpType::save_state: + apply_save_state(op, result, final_ops && ops.size() == i + 1); + break; case OpType::save_densmat: apply_save_density_matrix(op, result, final_ops && ops.size() == i + 1); break; @@ -558,6 +566,44 @@ void State::apply_save_density_matrix(const Operations::Op &op, op.save_type); } +template +void State::apply_save_state(const Operations::Op &op, + ExperimentResult &result, + bool last_op) { + if (op.qubits.size() != BaseState::qreg_.num_qubits()) { + throw std::invalid_argument( + op.name + " was not applied to all qubits." + " Only the full state can be saved."); + } + // Renamp single data type to average + Operations::DataSubType save_type; + switch (op.save_type) { + case Operations::DataSubType::single: + save_type = Operations::DataSubType::average; + break; + case Operations::DataSubType::c_single: + save_type = Operations::DataSubType::c_average; + break; + default: + save_type = op.save_type; + } + + // Default key + std::string key = (op.string_params[0] == "_method_") + ? "density_matrix" + : op.string_params[0]; + if (last_op) { + BaseState::save_data_average(result, key, + BaseState::qreg_.move_to_matrix(), + save_type); + } else { + BaseState::save_data_average(result, key, + BaseState::qreg_.copy_to_matrix(), + save_type); + } +} + + template cmatrix_t State::reduced_density_matrix(const reg_t& qubits, bool last_op) { cmatrix_t reduced_state; diff --git a/src/simulators/density_matrix/densitymatrix_state_chunk.hpp b/src/simulators/density_matrix/densitymatrix_state_chunk.hpp index 31128fc989..f0ce811d05 100644 --- a/src/simulators/density_matrix/densitymatrix_state_chunk.hpp +++ b/src/simulators/density_matrix/densitymatrix_state_chunk.hpp @@ -42,6 +42,7 @@ const Operations::OpSet StateOpSet( OpType::diagonal_matrix, OpType::kraus, OpType::superop, OpType::save_expval, OpType::save_expval_var, OpType::save_densmat, + OpType::save_state, OpType::save_probs, OpType::save_probs_ket, OpType::save_amps_sq }, @@ -169,6 +170,11 @@ class State : public Base::StateChunk { // Save data instructions //----------------------------------------------------------------------- + // Save the current density matrix + void apply_save_state(const Operations::Op &op, + ExperimentResult &result, + bool last_op = false); + // Save the current density matrix or reduced density matrix void apply_save_density_matrix(const Operations::Op &op, ExperimentResult &result, @@ -615,6 +621,9 @@ void State::apply_op(const int_t iChunk,const Operations::Op &op, case Operations::OpType::save_expval_var: BaseState::apply_save_expval(op, result); break; + case Operations::OpType::save_state: + apply_save_state(op, result, final_ops); + break; case Operations::OpType::save_densmat: apply_save_density_matrix(op, result, final_ops); break; @@ -812,6 +821,30 @@ void State::apply_save_density_matrix(const Operations::Op &op, op.save_type); } +template +void State::apply_save_state(const Operations::Op &op, + ExperimentResult &result, + bool last_op) +{ + // Renamp single data type to average + Operations::Op op_cpy = op; + switch (op.save_type) { + case Operations::DataSubType::single: + op_cpy.save_type = Operations::DataSubType::average; + break; + case Operations::DataSubType::c_single: + op_cpy.save_type = Operations::DataSubType::c_average; + break; + default: + break; + } + // Default key + op_cpy.string_params[0] = (op.string_params[0] == "_method_") + ? "density_matrix" + : op.string_params[0]; + apply_save_density_matrix(op_cpy, result, last_op); +} + //========================================================================= // Implementation: Snapshots //========================================================================= diff --git a/src/simulators/stabilizer/stabilizer_state.hpp b/src/simulators/stabilizer/stabilizer_state.hpp index beaf8d028c..6cde7600d8 100644 --- a/src/simulators/stabilizer/stabilizer_state.hpp +++ b/src/simulators/stabilizer/stabilizer_state.hpp @@ -37,7 +37,7 @@ const Operations::OpSet StateOpSet( OpType::roerror, OpType::save_expval, OpType::save_expval_var, OpType::save_probs, OpType::save_probs_ket, OpType::save_amps_sq, - OpType::save_stabilizer}, + OpType::save_stabilizer, OpType::save_state}, // Gates {"CX", "cx", "cy", "cz", "swap", "id", "x", "y", "z", "h", "s", "sdg", "sx", "delay"}, @@ -344,6 +344,7 @@ void State::apply_ops(const std::vector &ops, case OpType::save_amps_sq: apply_save_amplitudes_sq(op, result); break; + case OpType::save_state: case OpType::save_stabilizer: apply_save_stabilizer(op, result); break; @@ -483,8 +484,9 @@ std::vector State::sample_measure(const reg_t &qubits, void State::apply_save_stabilizer(const Operations::Op &op, ExperimentResult &result) { + std::string key = (op.string_params[0] == "_method_") ? "stabilizer" : op.string_params[0]; json_t clifford = BaseState::qreg_; - BaseState::save_data_pershot(result, op.string_params[0], + BaseState::save_data_pershot(result, key, std::move(clifford), op.save_type); } diff --git a/src/simulators/state.hpp b/src/simulators/state.hpp index f7485e56df..8a6566058a 100644 --- a/src/simulators/state.hpp +++ b/src/simulators/state.hpp @@ -233,7 +233,7 @@ class State { //----------------------------------------------------------------------- // Common instructions //----------------------------------------------------------------------- - + // Apply a save expectation value instruction void apply_save_expval(const Operations::Op &op, ExperimentResult &result); diff --git a/src/simulators/statevector/statevector_state.hpp b/src/simulators/statevector/statevector_state.hpp index 24da46f3ce..a2b5ac1f88 100755 --- a/src/simulators/statevector/statevector_state.hpp +++ b/src/simulators/statevector/statevector_state.hpp @@ -52,7 +52,7 @@ const Operations::OpSet StateOpSet( OpType::save_expval_var, OpType::save_densmat, OpType::save_probs, OpType::save_probs_ket, OpType::save_amps, OpType::save_amps_sq, - OpType::save_statevec + OpType::save_state, OpType::save_statevec // OpType::save_statevec_ket // TODO }, // Gates @@ -587,6 +587,7 @@ void State::apply_ops(const std::vector &ops, case OpType::save_densmat: apply_save_density_matrix(op, result); break; + case OpType::save_state: case OpType::save_statevec: apply_save_statevector(op, result, final_ops && ops.size() == i + 1); break; @@ -645,12 +646,15 @@ void State::apply_save_statevector(const Operations::Op &op, op.name + " was not applied to all qubits." " Only the full statevector can be saved."); } + std::string key = (op.string_params[0] == "_method_") + ? "statevector" + : op.string_params[0]; if (last_op) { - BaseState::save_data_pershot(result, op.string_params[0], + BaseState::save_data_pershot(result, key, BaseState::qreg_.move_to_vector(), op.save_type); } else { - BaseState::save_data_pershot(result, op.string_params[0], + BaseState::save_data_pershot(result, key, BaseState::qreg_.copy_to_vector(), op.save_type); } diff --git a/src/simulators/statevector/statevector_state_chunk.hpp b/src/simulators/statevector/statevector_state_chunk.hpp index 2ba9ce0855..395ddeb8df 100644 --- a/src/simulators/statevector/statevector_state_chunk.hpp +++ b/src/simulators/statevector/statevector_state_chunk.hpp @@ -48,7 +48,7 @@ const Operations::OpSet StateOpSet( OpType::save_expval_var, OpType::save_densmat, OpType::save_probs, OpType::save_probs_ket, OpType::save_amps, OpType::save_amps_sq, - OpType::save_statevec + OpType::save_statevec, OpType::save_state // OpType::save_statevec_ket // TODO }, // Gates @@ -594,6 +594,7 @@ void State::apply_op(const int_t iChunk,const Operations::Op &op, case Operations::OpType::save_densmat: apply_save_density_matrix(op, result); break; + case Operations::OpType::save_state: case Operations::OpType::save_statevec: apply_save_statevector(op, result, final_ops); break; @@ -754,14 +755,13 @@ void State::apply_save_statevector(const Operations::Op &op, op.name + " was not applied to all qubits." " Only the full statevector can be saved."); } + std::string key = (op.string_params[0] == "_method_") + ? "statevector" + : op.string_params[0]; if (last_op) { - BaseState::save_data_pershot(result, op.string_params[0], - move_to_vector(), - op.save_type); + BaseState::save_data_pershot(result, key, move_to_vector(), op.save_type); } else { - BaseState::save_data_pershot(result, op.string_params[0], - copy_to_vector(), - op.save_type); + BaseState::save_data_pershot(result, key, copy_to_vector(), op.save_type); } } diff --git a/src/simulators/superoperator/superoperator_state.hpp b/src/simulators/superoperator/superoperator_state.hpp index 5744c0e30e..ab0b4b5edb 100755 --- a/src/simulators/superoperator/superoperator_state.hpp +++ b/src/simulators/superoperator/superoperator_state.hpp @@ -33,7 +33,8 @@ const Operations::OpSet StateOpSet( {Operations::OpType::gate, Operations::OpType::reset, Operations::OpType::snapshot, Operations::OpType::barrier, Operations::OpType::matrix, Operations::OpType::diagonal_matrix, - Operations::OpType::kraus, Operations::OpType::superop}, + Operations::OpType::kraus, Operations::OpType::superop, + Operations::OpType::save_state}, // Gates {"U", "CX", "u1", "u2", "u3", "u", "cx", "cy", "cz", "swap", "id", "x", "y", "z", "h", "s", "sdg", "t", @@ -152,6 +153,11 @@ class State : public Base::State { // Save data instructions //----------------------------------------------------------------------- + // Save the current superop matrix + void apply_save_state(const Operations::Op &op, + ExperimentResult &result, + bool last_op = false); + // Helper function for computing expectation value virtual double expval_pauli(const reg_t &qubits, const std::string& pauli) override; @@ -226,7 +232,8 @@ void State::apply_ops(const std::vector &ops, RngEngine &rng, bool final_ops) { // Simple loop over vector of input operations - for (const auto &op: ops) { + for (size_t i = 0; i < ops.size(); ++i) { + const auto& op = ops[i]; switch (op.type) { case Operations::OpType::barrier: break; @@ -254,6 +261,9 @@ void State::apply_ops(const std::vector &ops, case Operations::OpType::snapshot: apply_snapshot(op, result); break; + case Operations::OpType::save_state: + apply_save_state(op, result, final_ops && ops.size() == i + 1); + break; default: throw std::invalid_argument( "QubitSuperoperator::State::invalid instruction \'" + op.name + @@ -489,6 +499,26 @@ void State::apply_snapshot(const Operations::Op &op, } } +template +void State::apply_save_state(const Operations::Op &op, + ExperimentResult &result, + bool last_op) { + if (op.qubits.size() != BaseState::qreg_.num_qubits()) { + throw std::invalid_argument( + op.name + " was not applied to all qubits." + " Only the full state can be saved."); + } + if (last_op) { + BaseState::save_data_average(result, op.string_params[0], + BaseState::qreg_.move_to_matrix(), + op.save_type); + } else { + BaseState::save_data_average(result, op.string_params[0], + BaseState::qreg_.copy_to_matrix(), + op.save_type); + } +} + template double State::expval_pauli(const reg_t &qubits, const std::string& pauli) { diff --git a/src/simulators/unitary/unitary_state.hpp b/src/simulators/unitary/unitary_state.hpp index 17bdd91c4b..566eaceee1 100755 --- a/src/simulators/unitary/unitary_state.hpp +++ b/src/simulators/unitary/unitary_state.hpp @@ -41,7 +41,8 @@ const Operations::OpSet StateOpSet( // Op types {Operations::OpType::gate, Operations::OpType::barrier, Operations::OpType::matrix, Operations::OpType::diagonal_matrix, - Operations::OpType::snapshot, Operations::OpType::save_unitary}, + Operations::OpType::snapshot, Operations::OpType::save_unitary, + Operations::OpType::save_state}, // Gates {"u1", "u2", "u3", "u", "U", "CX", "cx", "cz", "cy", "cp", "cu1", "cu2", "cu3", "swap", "id", "p", @@ -280,6 +281,7 @@ void State::apply_ops( if (BaseState::creg_.check_conditional(op)) apply_gate(op); break; + case Operations::OpType::save_state: case Operations::OpType::save_unitary: apply_save_unitary(op, result, final_ops && ops.size() == i + 1); break; @@ -536,12 +538,14 @@ void State::apply_save_unitary(const Operations::Op &op, op.name + " was not applied to all qubits." " Only the full unitary can be saved."); } + std::string key = (op.string_params[0] == "_method_") ? "unitary" : op.string_params[0]; + if (last_op) { - BaseState::save_data_pershot(result, op.string_params[0], + BaseState::save_data_pershot(result, key, BaseState::qreg_.move_to_matrix(), op.save_type); } else { - BaseState::save_data_pershot(result, op.string_params[0], + BaseState::save_data_pershot(result, key, BaseState::qreg_.copy_to_matrix(), op.save_type); } diff --git a/test/terra/backends/qasm_simulator/qasm_save.py b/test/terra/backends/qasm_simulator/qasm_save.py index c6c1d86f1b..f0042e2e96 100644 --- a/test/terra/backends/qasm_simulator/qasm_save.py +++ b/test/terra/backends/qasm_simulator/qasm_save.py @@ -19,6 +19,7 @@ from .qasm_save_stabilizer import QasmSaveStabilizerTests from .qasm_save_probabilities import QasmSaveProbabilitiesTests from .qasm_save_amplitudes import QasmSaveAmplitudesTests +from .qasm_save_state import QasmSaveStateTests class QasmSaveDataTests(QasmSaveExpectationValueTests, @@ -26,5 +27,6 @@ class QasmSaveDataTests(QasmSaveExpectationValueTests, QasmSaveDensityMatrixTests, QasmSaveStabilizerTests, QasmSaveProbabilitiesTests, - QasmSaveAmplitudesTests): + QasmSaveAmplitudesTests, + QasmSaveStateTests): """QasmSimulator SaveData instruction tests.""" diff --git a/test/terra/backends/qasm_simulator/qasm_save_state.py b/test/terra/backends/qasm_simulator/qasm_save_state.py new file mode 100644 index 0000000000..bbabb40c81 --- /dev/null +++ b/test/terra/backends/qasm_simulator/qasm_save_state.py @@ -0,0 +1,79 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2018, 2021. +# +# 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 SaveState instruction +""" + +import numpy as np + +from qiskit import QuantumCircuit, assemble +from qiskit.providers.aer import QasmSimulator +from qiskit.providers.aer.library import ( + SaveStatevector, SaveDensityMatrix, SaveStabilizer) + + +class QasmSaveStateTests: + """QasmSimulator SaveState instruction tests.""" + + SIMULATOR = QasmSimulator() + BACKEND_OPTS = {} + + def test_save_state(self): + """Test save_amplitudes instruction""" + + REFERENCE_SAVE = { + 'automatic': SaveStabilizer, + 'stabilizer': SaveStabilizer, + 'statevector': SaveStatevector, + 'statevector_gpu': SaveStatevector, + 'statevector_thrust': SaveStatevector, + 'density_matrix': SaveDensityMatrix, + 'density_matrix_gpu': SaveDensityMatrix, + 'density_matrix_thrust': SaveDensityMatrix + } + REFERENCE_LABEL = { + 'automatic': 'stabilizer', + 'stabilizer': 'stabilizer', + 'statevector': 'statevector', + 'statevector_gpu': 'statevector', + 'statevector_thrust': 'statevector', + 'density_matrix': 'density_matrix', + 'density_matrix_gpu': 'density_matrix', + 'density_matrix_thrust': 'density_matrix' + } + + opts = self.BACKEND_OPTS.copy() + method = opts.get('method', 'automatic') + + if method in REFERENCE_SAVE: + + # Stabilizer test circuit + num_qubits = 4 + target_instr = REFERENCE_SAVE[method](num_qubits, label='target') + circ = QuantumCircuit(num_qubits) + circ.h(0) + for i in range(1, num_qubits): + circ.cx(i - 1, i) + circ.save_state() + circ.append(target_instr, range(num_qubits)) + label = REFERENCE_LABEL[method] + + # Run + qobj = assemble(circ, self.SIMULATOR) + result = self.SIMULATOR.run(qobj, **opts).result() + self.assertTrue(result.success) + data = result.data(0) + self.assertIn(label, data) + self.assertIn('target', data) + value = data[label] + target = data['target'] + self.assertTrue(np.all(value == target))