mirror of https://github.com/Qiskit/qiskit-aer.git
moved dmat to be special case of matrix
This commit is contained in:
parent
0235bcadce
commit
7102e519aa
|
@ -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": "<X>_1_gate",
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -558,6 +558,15 @@ template <class T>
|
|||
bool is_unitary(const matrix<T> &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;
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
#ifndef _aer_noise_model_hpp_
|
||||
#define _aer_noise_model_hpp_
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
#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 <class DerivedError>
|
||||
void add_error(const DerivedError &error,
|
||||
const std::vector<std::string> &op_labels,
|
||||
const std::unordered_set<std::string> &op_labels,
|
||||
const std::vector<reg_t> &op_qubits = {},
|
||||
const std::vector<reg_t> &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<std::string> &waltz_gates) {
|
||||
inline void set_waltz_gates(const std::unordered_set<std::string> &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 <class DerivedError>
|
||||
void add_local_error(const DerivedError &error,
|
||||
const std::vector<std::string> &op_labels,
|
||||
const std::unordered_set<std::string> &op_labels,
|
||||
const std::vector<reg_t> &op_qubits);
|
||||
|
||||
// Add a non-local Error type to the model for specific qubits
|
||||
template <class DerivedError>
|
||||
void add_nonlocal_error(const DerivedError &error,
|
||||
const std::vector<std::string> &op_labels,
|
||||
const std::unordered_set<std::string> &op_labels,
|
||||
const std::vector<reg_t> &op_qubits,
|
||||
const std::vector<reg_t> &noise_qubits);
|
||||
|
||||
|
@ -121,7 +143,7 @@ private:
|
|||
std::unordered_map<std::string, nonlocal_qubit_map_t> nonlocal_error_table_;
|
||||
|
||||
// Table of single-qubit gates to use a Waltz X90 based error model
|
||||
std::set<std::string> waltz_gates_;
|
||||
std::unordered_set<std::string> 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 <class DerivedError>
|
||||
void NoiseModel::add_error(const DerivedError &error,
|
||||
const std::vector<std::string> &op_labels,
|
||||
const std::unordered_set<std::string> &op_labels,
|
||||
const std::vector<reg_t> &op_qubits,
|
||||
const std::vector<reg_t> &noise_qubits) {
|
||||
|
||||
|
@ -232,7 +235,7 @@ void NoiseModel::add_error(const DerivedError &error,
|
|||
|
||||
template <class DerivedError>
|
||||
void NoiseModel::add_local_error(const DerivedError &error,
|
||||
const std::vector<std::string> &op_labels,
|
||||
const std::unordered_set<std::string> &op_labels,
|
||||
const std::vector<reg_t> &op_qubits) {
|
||||
// Turn on local error flag
|
||||
if (!op_labels.empty()) {
|
||||
|
@ -251,7 +254,7 @@ void NoiseModel::add_local_error(const DerivedError &error,
|
|||
|
||||
template <class DerivedError>
|
||||
void NoiseModel::add_nonlocal_error(const DerivedError &error,
|
||||
const std::vector<std::string> &op_labels,
|
||||
const std::unordered_set<std::string> &op_labels,
|
||||
const std::vector<reg_t> &op_qubits,
|
||||
const std::vector<reg_t> &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<reg_t> 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<std::string> ops;
|
||||
std::unordered_set<std::string> ops; // want set so ops are unique, and we can pull out measure
|
||||
JSON::get_value(ops, "operations", gate_js);
|
||||
std::vector<reg_t> gate_qubits;
|
||||
JSON::get_value(ops, "gate_qubits", gate_js);
|
||||
std::vector<reg_t> 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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
|
|
@ -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<std::string> 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<std::string> 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<std::string, State::Gates> 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:
|
||||
|
|
Loading…
Reference in New Issue