diff --git a/examples/qobj/qobj_amplitude_damping.json b/examples/qobj/qobj_amplitude_damping.json index a288d1cc1..06b3885bc 100644 --- a/examples/qobj/qobj_amplitude_damping.json +++ b/examples/qobj/qobj_amplitude_damping.json @@ -17,8 +17,10 @@ }, "experiments": [ { - "config": {"shots": 10}, + "config": {"shots": 1}, "instructions": [ + {"name": "mat", "qubits": [0], "params": [[[1, 0], [1, 0]]]}, + {"name": "snapshot", "label": "post-mat", "type": "state"}, {"name": "h", "qubits": [0]}, {"name": "snapshot", "type": "pauli_observable", "label": "_1_gate", diff --git a/src/framework/operations.hpp b/src/framework/operations.hpp index 59f5b80d7..fe2d33763 100755 --- a/src/framework/operations.hpp +++ b/src/framework/operations.hpp @@ -178,7 +178,6 @@ Op json_to_op_snapshot_probs(const json_t &js); // Matrices Op json_to_op_mat(const json_t &js); -Op json_to_op_dmat(const json_t &js); Op json_to_op_kraus(const json_t &js); Op json_to_op_noise_switch(const json_t &js); @@ -204,8 +203,6 @@ Op json_to_op(const json_t &js) { // Arbitrary matrix gates if (name == "mat") return json_to_op_mat(js); - if (name == "dmat") - return json_to_op_dmat(js); if (name == "kraus") return json_to_op_kraus(js); // Snapshot @@ -342,18 +339,10 @@ Op json_to_op_mat(const json_t &js) { } // Validation check_qubits(op.qubits); - return op; -} - -// TODO: Remove and treat as special case of mat. -Op json_to_op_dmat(const json_t &js) { - Op op; - op.name = "dmat"; - JSON::get_value(op.qubits, "qubits", js); - JSON::get_value(op.params, "params", js); // store diagonal as complex vector - - // Validation - check_qubits(op.qubits); + // Check unitary + if (!Utils::is_unitary(op.mats[0], 1e-10)) { + throw std::invalid_argument("\"mat\" matrix is not unitary."); + } return op; } diff --git a/src/framework/qobj.hpp b/src/framework/qobj.hpp index 664ea7680..57302615e 100755 --- a/src/framework/qobj.hpp +++ b/src/framework/qobj.hpp @@ -46,7 +46,7 @@ public: std::vector circuits; // List of circuits json_t header; // (optional) passed through to result; json_t config; // (optional) not currently used? - + //---------------------------------------------------------------- // Loading Functions //---------------------------------------------------------------- diff --git a/src/framework/utils.hpp b/src/framework/utils.hpp index 5611c426e..26a704520 100755 --- a/src/framework/utils.hpp +++ b/src/framework/utils.hpp @@ -558,6 +558,15 @@ template bool is_unitary(const matrix &mat, double threshold) { size_t nrows = mat.GetRows(); size_t ncols = mat.GetColumns(); + // Check if diagonal row-matrix + if (nrows == 1) { + for (size_t j=0; j < ncols; j++) { + bool delta = std::abs(1.0 - std::real(std::abs(mat(0, j)))); + if (delta > threshold) + return false; + } + return true; + } // Check U matrix is square if (nrows != ncols) return false; diff --git a/src/noise/noise_model.hpp b/src/noise/noise_model.hpp index 1f313de81..c4b2a733f 100644 --- a/src/noise/noise_model.hpp +++ b/src/noise/noise_model.hpp @@ -14,6 +14,8 @@ #ifndef _aer_noise_model_hpp_ #define _aer_noise_model_hpp_ +#include + #include "framework/operations.hpp" #include "framework/types.hpp" #include "framework/rng.hpp" @@ -46,11 +48,8 @@ public: NoiseModel() = default; NoiseModel(const json_t &js) {load_from_json(js);} - // Sample noise for the current operation - virtual NoiseOps sample_noise(const Operations::Op &op); - // Sample a noisy implementation of a full circuit - virtual Circuit sample_noise(const Circuit &circ); + Circuit sample_noise(const Circuit &circ); // Load a noise model from JSON void load_from_json(const json_t &js); @@ -61,7 +60,7 @@ public: // Add a non-local Error type to the model for specific qubits template void add_error(const DerivedError &error, - const std::vector &op_labels, + const std::unordered_set &op_labels, const std::vector &op_qubits = {}, const std::vector &noise_qubits = {}); @@ -72,7 +71,7 @@ public: } // Set which single qubit gates should use the X90 waltz error model - inline void set_waltz_gates(const std::set &waltz_gates) { + inline void set_waltz_gates(const std::unordered_set &waltz_gates) { waltz_gates_ = waltz_gates; } @@ -84,16 +83,39 @@ public: private: + // Sample noise for the current operation + NoiseOps sample_noise(const Operations::Op &op); + + // Sample noise for the current operation + void sample_noise_local(const Operations::Op &op, + NoiseOps &noise_before, + NoiseOps &noise_after); + + void sample_noise_nonlocal(const Operations::Op &op, + NoiseOps &noise_before, + NoiseOps &noise_after); + + // Sample noise for the current operation + NoiseOps sample_noise_helper(const Operations::Op &op); + + // Sample a noisy implementation of a two-X90 pulse u3 gate + NoiseOps sample_noise_waltz_u3(uint_t qubit, complex_t theta, + complex_t phi, complex_t lam); + + // Sample a noisy implementation of a single-X90 pulse u2 gate + NoiseOps sample_noise_waltz_u2(uint_t qubit, complex_t phi, complex_t lam); + + // Add a local error to the noise model for specific qubits template void add_local_error(const DerivedError &error, - const std::vector &op_labels, + const std::unordered_set &op_labels, const std::vector &op_qubits); // Add a non-local Error type to the model for specific qubits template void add_nonlocal_error(const DerivedError &error, - const std::vector &op_labels, + const std::unordered_set &op_labels, const std::vector &op_qubits, const std::vector &noise_qubits); @@ -121,7 +143,7 @@ private: std::unordered_map nonlocal_error_table_; // Table of single-qubit gates to use a Waltz X90 based error model - std::set waltz_gates_; + std::unordered_set waltz_gates_; // Lookup table for gate strings to enum enum class Gate {id, x, y, z, h, s, sdg, t, tdg, u0, u1, u2, u3}; @@ -132,25 +154,6 @@ private: // Rng engine RngEngine rng_; // initialized with random seed - - // Sample noise for the current operation - void sample_noise_local(const Operations::Op &op, - NoiseOps &noise_before, - NoiseOps &noise_after); - - void sample_noise_nonlocal(const Operations::Op &op, - NoiseOps &noise_before, - NoiseOps &noise_after); - - // Sample noise for the current operation - NoiseOps sample_noise_helper(const Operations::Op &op); - - // Sample a noisy implementation of a two-X90 pulse u3 gate - NoiseOps sample_noise_waltz_u3(uint_t qubit, complex_t theta, - complex_t phi, complex_t lam); - - // Sample a noisy implementation of a single-X90 pulse u2 gate - NoiseOps sample_noise_waltz_u2(uint_t qubit, complex_t phi, complex_t lam); }; @@ -213,7 +216,7 @@ Circuit NoiseModel::sample_noise(const Circuit &circ) { template void NoiseModel::add_error(const DerivedError &error, - const std::vector &op_labels, + const std::unordered_set &op_labels, const std::vector &op_qubits, const std::vector &noise_qubits) { @@ -232,7 +235,7 @@ void NoiseModel::add_error(const DerivedError &error, template void NoiseModel::add_local_error(const DerivedError &error, - const std::vector &op_labels, + const std::unordered_set &op_labels, const std::vector &op_qubits) { // Turn on local error flag if (!op_labels.empty()) { @@ -251,7 +254,7 @@ void NoiseModel::add_local_error(const DerivedError &error, template void NoiseModel::add_nonlocal_error(const DerivedError &error, - const std::vector &op_labels, + const std::unordered_set &op_labels, const std::vector &op_qubits, const std::vector &noise_qubits) { @@ -338,19 +341,21 @@ void NoiseModel::sample_noise_nonlocal(const Operations::Op &op, // Get the inner error map for gate name auto iter = nonlocal_error_table_.find(op.name); if (iter != nonlocal_error_table_.end()) { + const auto qubit_map = iter->second; // Format qubit sets std::vector qubit_sets; - if (op.name == "measure" || op.name == "reset") { + if ((op.name == "measure" || op.name == "reset") + && qubit_map.find(op.qubits) == qubit_map.end()) { // since measure and reset ops can be defined on multiple qubits - // but error model is single qubit we add each one separately + // but error model may be specified only on single qubits we add + // each one separately. If a multi-qubit model is found for specified + // qubits however, that will be used instead. for (const auto &q : op.qubits) qubit_sets.push_back({q}); } else { // for gate operations we use the qubits as specified qubit_sets.push_back(op.qubits); } - // Check if the qubits are listed in the inner model - const auto qubit_map = iter->second; for (const auto &qubits: qubit_sets) { // Check if the qubits are listed in the inner model auto iter_qubits = qubit_map.find(qubits); @@ -464,6 +469,12 @@ NoiseModel::NoiseOps NoiseModel::sample_noise_waltz_u2(uint_t qubit, "default_probabilities": probs, "qubit_probabilities": [[q0, probs0], [q1, probs1]] } + + Readout + { + "type": "readout", + "assignment_probabilities": [[P(0|0), P(0|1)], [P(1|0), P(1|1)]] + } */ // Allowed types: "unitary_error", "kraus_error", "reset_error" @@ -490,13 +501,38 @@ void NoiseModel::load_from_json(const json_t &js) { for (const auto &gate_js : js["errors"]) { std::string type; JSON::get_value(type, "type", gate_js); - std::vector ops; + std::unordered_set ops; // want set so ops are unique, and we can pull out measure JSON::get_value(ops, "operations", gate_js); std::vector gate_qubits; JSON::get_value(ops, "gate_qubits", gate_js); std::vector noise_qubits; JSON::get_value(ops, "noise_qubits", gate_js); + // We treat measure as a separate error op so that it can be applied before + // the measure operation, rather than after like the other gates + if (ops.find("measure") != ops.end() && type != "readout") { + ops.erase("measure"); // remove measure from set of ops + if (type == "unitary") { + UnitaryError error; + error.load_from_json(gate_js); + error.set_errors_before(); // set errors before the op + add_error(error, {"measure"}, gate_qubits, noise_qubits); + } else if (type == "kraus") { + KrausError error; + error.load_from_json(gate_js); + error.set_errors_before(); // set errors before the op + add_error(error, {"measure"}, gate_qubits, noise_qubits); + } else if (type == "reset") { + ResetError error; + error.load_from_json(gate_js); + error.set_errors_before(); // set errors before the op + add_error(error, {"measure"}, gate_qubits, noise_qubits); + } + else { + throw std::invalid_argument("NoiseModel: Invalid noise type (" + type + ")"); + } + } + // Load the remaining non-measure ops as a separate error if (type == "unitary") { UnitaryError error; error.load_from_json(gate_js); diff --git a/src/noise/unitary_error.hpp b/src/noise/unitary_error.hpp index f2603a520..a716dd96d 100644 --- a/src/noise/unitary_error.hpp +++ b/src/noise/unitary_error.hpp @@ -148,6 +148,11 @@ void UnitaryError::load_from_json(const json_t &js) { JSON::get_value(mats, "matrices", js); if (!mats.empty()) set_unitaries(mats); + + // Check input is valid unitary error + auto valid = validate(); + if (valid.first == false) + throw std::invalid_argument(valid.second); } //------------------------------------------------------------------------- diff --git a/src/simulators/qubitvector/qv_state.hpp b/src/simulators/qubitvector/qv_state.hpp index 2e4ec0c55..5a59d73c1 100755 --- a/src/simulators/qubitvector/qv_state.hpp +++ b/src/simulators/qubitvector/qv_state.hpp @@ -69,7 +69,7 @@ public: // Allowed operations are: // {"snapshot_state", "snapshot_probs", "snapshot_pauli", "snapshot_matrix", - // "barrier", "measure", "reset", "mat", "dmat", "kraus", + // "barrier", "measure", "reset", "mat", "kraus", // "u0", "u1", "u2", "u3", "cx", "cz", // "id", "x", "y", "z", "h", "s", "sdg", "t", "tdg"} virtual std::set allowed_ops() const override; @@ -125,7 +125,7 @@ protected: // Enum class and gateset map for switching based on gate name enum class Gates { - mat, dmat, kraus, // special + mat, kraus, // special measure, reset, barrier, u0, u1, u2, u3, id, x, y, z, h, s, sdg, t, tdg, // single qubit cx, cz, rzz // two qubit @@ -205,7 +205,7 @@ protected: std::set State::allowed_ops() const { return { "barrier", "measure", "reset", "snapshot_state", "snapshot_probs", "snapshot_pauli", "snapshot_matrix", - "mat", "dmat", "kraus", + "mat", "kraus", "u0", "u1", "u2", "u3", "cx", "cz", "id", "x", "y", "z", "h", "s", "sdg", "t", "tdg"}; } @@ -215,7 +215,6 @@ const std::unordered_map State::gateset({ {"barrier", Gates::barrier}, // barrier does nothing // Matrix multiplication {"mat", Gates::mat}, // matrix multiplication - {"dmat", Gates::dmat}, // Diagonal matrix multiplication // Single qubit gates {"id", Gates::id}, // Pauli-Identity gate {"x", Gates::x}, // Pauli-X gate @@ -394,8 +393,6 @@ void State::apply_op(const Operations::Op &op) { case Gates::mat: apply_matrix(op.qubits, op.mats[0]); break; - case Gates::dmat: - apply_matrix(op.qubits, op.params); break; // Special Noise operations case Gates::kraus: