diff --git a/src/noise/abstract_error.hpp b/src/noise/abstract_error.hpp deleted file mode 100644 index 5e88848f29..0000000000 --- a/src/noise/abstract_error.hpp +++ /dev/null @@ -1,76 +0,0 @@ -/** - * 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. - */ - -#ifndef _aer_noise_abstract_error_hpp_ -#define _aer_noise_abstract_error_hpp_ - -#include "framework/operations.hpp" -#include "framework/types.hpp" -#include "framework/rng.hpp" - -namespace AER { -namespace Noise { - -//========================================================================= -// Error abstract base class -//========================================================================= - -class AbstractError { -public: - - // Constructors - AbstractError() = default; - virtual ~AbstractError() = default; - - // Alias for return type - using NoiseOps = std::vector; - - // Sample an realization of the error from the error model using the passed - // in RNG engine. - virtual NoiseOps sample_noise(const reg_t &qubits, - RngEngine &rng) const = 0; - - // Load from a JSON file - virtual void load_from_json(const json_t &js) = 0; - - // Set number of qubits or memory bits for error - inline void set_num_qubits(uint_t num_qubits) {num_qubits_ = num_qubits;} - - // Get number of qubits or memory bits for error - inline uint_t get_num_qubits() const {return num_qubits_;} - - // Set the sampled errors to be applied after the original operation - inline void set_errors_after() {errors_after_op_ = true;} - - // Set the sampled errors to be applied before the original operation - inline void set_errors_before() {errors_after_op_ = false;} - - // Returns true if the errors are to be applied after the operation - inline bool errors_after() const {return errors_after_op_;} - -private: - // flag for where errors should be applied relative to the sampled op - bool errors_after_op_ = true; - - // Number of qubits / memory bits the error applies to - uint_t num_qubits_ = 0; -}; - - -//------------------------------------------------------------------------- -} // end namespace Noise -//------------------------------------------------------------------------- -} // end namespace AER -//------------------------------------------------------------------------- -#endif \ No newline at end of file diff --git a/src/noise/noise_model.hpp b/src/noise/noise_model.hpp index 0ad76c3c65..2df15ee58b 100644 --- a/src/noise/noise_model.hpp +++ b/src/noise/noise_model.hpp @@ -22,9 +22,6 @@ #include "framework/types.hpp" #include "framework/rng.hpp" #include "framework/circuit.hpp" -#include "noise/abstract_error.hpp" - -// For JSON parsing of specific error types #include "noise/quantum_error.hpp" #include "noise/readout_error.hpp" diff --git a/src/noise/quantum_error.hpp b/src/noise/quantum_error.hpp index a06be42870..95056470a5 100644 --- a/src/noise/quantum_error.hpp +++ b/src/noise/quantum_error.hpp @@ -15,7 +15,7 @@ #ifndef _aer_noise_quantum_error_hpp_ #define _aer_noise_quantum_error_hpp_ -#include "noise/abstract_error.hpp" +#include "simulators/superoperator/superoperator_state.hpp" namespace AER { namespace Noise { @@ -27,27 +27,39 @@ namespace Noise { // Quantum error class that can model any error that is expressed as a // qobj instruction acting on qubits. -class QuantumError : public AbstractError { +class QuantumError { public: + // Methods for sampling + enum class Method {standard, superop}; + // Alias for return type using NoiseOps = std::vector; //----------------------------------------------------------------------- - // Error base required methods + // Sampling method //----------------------------------------------------------------------- // Sample a noisy implementation of op NoiseOps sample_noise(const reg_t &qubits, - RngEngine &rng) const override; + RngEngine &rng, + Method method = Method::standard) const; - // Load a QuantumError object from a JSON Error object - void load_from_json(const json_t &js) override; + // Return the opset for the quantum error + const Operations::OpSet& opset() const {return opset_;} + + // Return the superoperator matrix representation of the error. + // If the error cannot be converted to a superoperator and error + // will be raised. + const cmatrix_t& superoperator() const; //----------------------------------------------------------------------- - // Additional class methods + // Initialization //----------------------------------------------------------------------- + // Load a QuantumError object from a JSON Error object + void load_from_json(const json_t &js); + // Sets the sub-circuits and probabilities to be sampled from. // The length of the circuits vector and probability vector must be equal. void set_circuits(const std::vector &circuits, @@ -58,12 +70,35 @@ class QuantumError : public AbstractError { // non-kraus subcircuits. void set_from_kraus(const std::vector &mats); + // Compute the superoperator representation of the quantum error + void compute_superoperator(); + + //----------------------------------------------------------------------- + // Utility + //----------------------------------------------------------------------- + + // Set number of qubits or memory bits for error + inline void set_num_qubits(uint_t num_qubits) {num_qubits_ = num_qubits;} + + // Get number of qubits or memory bits for error + inline uint_t get_num_qubits() const {return num_qubits_;} + + // Set the sampled errors to be applied after the original operation + inline void set_errors_after() {errors_after_op_ = true;} + + // Set the sampled errors to be applied before the original operation + inline void set_errors_before() {errors_after_op_ = false;} + + // Returns true if the errors are to be applied after the operation + inline bool errors_after() const {return errors_after_op_;} + // Set threshold for checking probabilities and matrices void set_threshold(double); - const Operations::OpSet& opset() const {return opset_;} - protected: + // Number of qubits sthe error applies to + uint_t num_qubits_ = 0; + // Probabilities, first entry is no-error (identity) rvector_t probabilities_; //std::discrete_distribution probabilities_; @@ -76,37 +111,56 @@ class QuantumError : public AbstractError { // threshold for validating if matrices are unitary double threshold_ = 1e-10; + + // Superoperator matrix representation of the error + cmatrix_t superoperator_; + + // flag for where errors should be applied relative to the sampled op + bool errors_after_op_ = true; }; //------------------------------------------------------------------------- // Implementation: Mixed unitary error subclass //------------------------------------------------------------------------- + QuantumError::NoiseOps QuantumError::sample_noise(const reg_t &qubits, - RngEngine &rng) const { + RngEngine &rng, + Method method) const { if (qubits.size() < get_num_qubits()) { std::stringstream msg; msg << "QuantumError: qubits size (" << qubits.size() << ")"; msg << " < error qubits (" << get_num_qubits() << ")."; throw std::invalid_argument(msg.str()); } - auto r = rng.rand_int(probabilities_); - // Check for invalid arguments - if (r + 1 > circuits_.size()) { - std::stringstream msg; - msg << "QuantumError: probability outcome (" << r << ")"; - msg << " is greater than number of circuits (" << circuits_.size() << ")."; - throw std::invalid_argument(msg.str()); - } - NoiseOps noise_ops = circuits_[r]; - // Add qubits to noise op commands; - for (auto &op : noise_ops) { - // Update qubits based on position in qubits list - for (size_t q=0; q < op.qubits.size(); q++) { - op.qubits[q] = qubits[op.qubits[q]]; + switch (method) { + case Method::superop: { + // Truncate qubits to size of the actual error + reg_t op_qubits = qubits; + op_qubits.resize(get_num_qubits()); + auto op = Operations::make_superop(op_qubits, superoperator()); + return NoiseOps({op}); + } + default: { + auto r = rng.rand_int(probabilities_); + // Check for invalid arguments + if (r + 1 > circuits_.size()) { + std::stringstream msg; + msg << "QuantumError: probability outcome (" << r << ")"; + msg << " is greater than number of circuits (" << circuits_.size() << ")."; + throw std::invalid_argument(msg.str()); + } + NoiseOps noise_ops = circuits_[r]; + // Add qubits to noise op commands; + for (auto &op : noise_ops) { + // Update qubits based on position in qubits list + for (size_t q=0; q < op.qubits.size(); q++) { + op.qubits[q] = qubits[op.qubits[q]]; + } + } + return noise_ops; } } - return noise_ops; } void QuantumError::set_threshold(double threshold) { @@ -259,6 +313,35 @@ void QuantumError::set_from_kraus(const std::vector &mats) { } +const cmatrix_t& QuantumError::superoperator() const { + // Check the superoperator is actually computed + // If not raise an exception + if (superoperator_.empty()) { + throw std::runtime_error("QuantumError: superoperator is empty."); + } + return superoperator_; +} + + +void QuantumError::compute_superoperator() { + // Initialize superoperator matrix to correct size + size_t dim = 1 << (2 * get_num_qubits()); + superoperator_.initialize(dim, dim); + // We use the superoperator simulator state to do this + QubitSuperoperator::State<> superop; + for (size_t j=0; j; + //----------------------------------------------------------------------- - // Error base required methods + // Error sampling //----------------------------------------------------------------------- // Sample a noisy implementation of op NoiseOps sample_noise(const reg_t &memory, - RngEngine &rng) const override; - - // Load a ReadoutError object from a JSON Error object - void load_from_json(const json_t &js) override; + RngEngine &rng) const; //----------------------------------------------------------------------- - // Additional class methods + // Initialization //----------------------------------------------------------------------- + // Load a ReadoutError object from a JSON Error object + void load_from_json(const json_t &js); + // Set the default assignment probabilities for measurement of each qubit // Each vector is a list of probabilities {P(0|q), P(1|q), ... P(n-1|q)} // For the no-error case this list viewed as a matrix should be an // identity matrix void set_probabilities(const std::vector &probs); + //----------------------------------------------------------------------- + // Utility + //----------------------------------------------------------------------- + + // Set number of qubits or memory bits for error + inline void set_num_qubits(uint_t num_qubits) {num_qubits_ = num_qubits;} + + // Get number of qubits or memory bits for error + inline uint_t get_num_qubits() const {return num_qubits_;} + protected: + + // Number of qubits/memory bits the error applies to + uint_t num_qubits_ = 0; + + // Vector of assignment probability vectors std::vector assignment_probabilities_; // threshold for checking probabilities