Updated noise model class for local and nonlocal noise, added reset error error type

This commit is contained in:
cjwood 2018-08-16 16:19:37 -04:00
parent 4474ad058b
commit 46c120104a
13 changed files with 698 additions and 186 deletions

View File

@ -22,14 +22,11 @@
#include "base/engine.hpp" #include "base/engine.hpp"
#include "simulators/qubitvector/qubitvector.hpp" #include "simulators/qubitvector/qubitvector.hpp"
#include "simulators/qubitvector/qv_state.hpp" #include "simulators/qubitvector/qv_state.hpp"
#include "framework/interface.hpp"
// Noise // Noise
#include "base/noise.hpp" #include "base/noise_model.hpp"
#include "noise/simple_model.hpp"
#include "noise/unitary_error.hpp"
#include "noise/gate_error.hpp"
#include "framework/interface.hpp"
/******************************************************************************* /*******************************************************************************
* *
* Main * Main
@ -75,33 +72,21 @@ int main(int argc, char **argv) {
using namespace AER; using namespace AER;
using State = QubitVector::State; // State class using State = QubitVector::State; // State class
using Engine = Base::Engine<QV::QubitVector>; // Optimized Engine class using Engine = Base::Engine<QV::QubitVector>; // Optimized Engine class
using NoiseModel = Noise::SimpleModel; using Base::NoiseModel;
// Initialize simulator // Initialize simulator
Base::Controller<Engine, State> sim; Base::Controller<Engine, State> sim;
// Check for noise_params // Check for noise_params
if (JSON::check_key("config", qobj) && if (JSON::check_key("config", qobj) &&
JSON::check_key("noise_params", qobj["config"])) { JSON::check_key("noise_params", qobj["config"])) {
NoiseModel noise(qobj["config"]["noise_params"]); NoiseModel noise1(qobj["config"]["noise_params"]);
out << sim.execute(qobj, &noise).dump(4) << std::endl; out << sim.execute(qobj, &noise1).dump(4) << std::endl;
} else { } else {
// execute without noise // execute without noise
out << sim.execute(qobj).dump(4) << std::endl; out << sim.execute(qobj).dump(4) << std::endl;
} }
// Amplitude damping channel
/*
NoiseModel kraus_noise;
std::vector<cmatrix_t> amp_damp(2);
double gamma = 0.4;
amp_damp[0] = Utils::make_matrix<complex_t>({{{1, 0}, {0, 0}},
{{0, 0}, {std::sqrt(gamma), 0}}});
amp_damp[1] = Utils::make_matrix<complex_t>({{{0, 0}, {std::sqrt(1-gamma), 0}},
{{0, 0}, {0, 0}}});
kraus_noise.add_error(Noise::GateError(amp_damp), {"x", "y", "z", "s", "sdg", "h", "t", "tdg", "u1", "u2", "u3"});
out << sim.execute(qobj, &kraus_noise).dump(4) << std::endl;
*/
return 0; return 0;
} catch (std::exception &e) { } catch (std::exception &e) {
std::stringstream msg; std::stringstream msg;

View File

@ -29,7 +29,7 @@
// Base Controller // Base Controller
#include "framework/qobj.hpp" #include "framework/qobj.hpp"
#include "base/noise.hpp" #include "base/noise_model.hpp"
namespace AER { namespace AER {
namespace Base { namespace Base {
@ -72,12 +72,12 @@ public:
// Load and execute a qobj // Load and execute a qobj
inline json_t execute(const json_t &qobj) {return execute(qobj, nullptr);} inline json_t execute(const json_t &qobj) {return execute(qobj, nullptr);}
json_t execute(const json_t &qobj, Noise::Model *noise_ptr); json_t execute(const json_t &qobj, Base::NoiseModel *noise_ptr);
// Execute a single circuit // Execute a single circuit
json_t execute_circuit(Circuit &circ, json_t execute_circuit(Circuit &circ,
int max_shot_threads, int max_shot_threads,
Noise::Model *noise_ptr); Base::NoiseModel *noise_ptr);
// Check if measurement sampling can be performed for a circuit // Check if measurement sampling can be performed for a circuit
// and set circuit flag if it is compatible // and set circuit flag if it is compatible
@ -134,7 +134,7 @@ void Controller<Engine_t, State_t>::load_config(const json_t &config) {
template < class Engine_t, class State_t> template < class Engine_t, class State_t>
json_t Controller<Engine_t, State_t>::execute(const json_t &qobj_js, json_t Controller<Engine_t, State_t>::execute(const json_t &qobj_js,
Noise::Model *noise_ptr) { Base::NoiseModel *noise_ptr) {
auto timer_start = myclock_t::now(); // start timer auto timer_start = myclock_t::now(); // start timer
Qobj qobj; // Load QOBJ from json Qobj qobj; // Load QOBJ from json
@ -209,7 +209,7 @@ json_t Controller<Engine_t, State_t>::execute(const json_t &qobj_js,
template <class Engine_t, class State_t> template <class Engine_t, class State_t>
json_t Controller<Engine_t, State_t>::execute_circuit(Circuit &circ, json_t Controller<Engine_t, State_t>::execute_circuit(Circuit &circ,
int max_shot_threads, int max_shot_threads,
Noise::Model *noise_ptr) { Base::NoiseModel *noise_ptr) {
// Initialize Return // Initialize Return
auto timer_start = myclock_t::now(); // state circuit timer auto timer_start = myclock_t::now(); // state circuit timer

View File

@ -20,7 +20,7 @@
#include "framework/snapshot.hpp" #include "framework/snapshot.hpp"
#include "framework/utils.hpp" #include "framework/utils.hpp"
#include "base/state.hpp" #include "base/state.hpp"
#include "base/noise.hpp" #include "base/noise_model.hpp"
namespace AER { namespace AER {
namespace Base { namespace Base {
@ -56,7 +56,7 @@ public:
void execute(const Circuit &circ, void execute(const Circuit &circ,
uint_t shots, uint_t shots,
State<state_t> *state_ptr, State<state_t> *state_ptr,
Noise::Model *noise_ptr = nullptr); Base::NoiseModel *noise_ptr = nullptr);
// This method performs the same function as 'execute', except // This method performs the same function as 'execute', except
// that it only simulates a single shot and then generates samples // that it only simulates a single shot and then generates samples
@ -104,7 +104,7 @@ protected:
// Apply an operation to the state // Apply an operation to the state
void apply_op(const Operations::Op &op, void apply_op(const Operations::Op &op,
State<state_t> *state_ptr, State<state_t> *state_ptr,
Noise::Model *noise_ptr); Base::NoiseModel *noise_ptr);
// Initialize an engine and circuit // Initialize an engine and circuit
void initialize(State<state_t> *state, const Circuit &circ); void initialize(State<state_t> *state, const Circuit &circ);
@ -212,7 +212,7 @@ template <class state_t>
void Engine<state_t>::execute(const Circuit &circ, void Engine<state_t>::execute(const Circuit &circ,
uint_t shots, uint_t shots,
State<state_t> *state_ptr, State<state_t> *state_ptr,
Noise::Model *noise_ptr) { Base::NoiseModel *noise_ptr) {
// Check if ideal simulation check if sampling is possible // Check if ideal simulation check if sampling is possible
if (noise_ptr == nullptr && circ.measure_sampling_flag) { if (noise_ptr == nullptr && circ.measure_sampling_flag) {
execute_with_measure_sampling(circ, shots, state_ptr); execute_with_measure_sampling(circ, shots, state_ptr);
@ -233,7 +233,7 @@ void Engine<state_t>::execute(const Circuit &circ,
template <class state_t> template <class state_t>
void Engine<state_t>::apply_op(const Operations::Op &op, void Engine<state_t>::apply_op(const Operations::Op &op,
State<state_t> *state_ptr, State<state_t> *state_ptr,
Noise::Model *noise_ptr) { Base::NoiseModel *noise_ptr) {
auto it = engine_ops_.find(op.name); auto it = engine_ops_.find(op.name);
if (it == engine_ops_.end()) { if (it == engine_ops_.end()) {
// check if op passes conditional // check if op passes conditional

View File

@ -1,86 +0,0 @@
/**
* Copyright 2018, IBM.
*
* This source code is licensed under the Apache License, Version 2.0 found in
* the LICENSE.txt file in the root directory of this source tree.
*/
/**
* @file noise.hpp
* @brief Noise model base class for qiskit-aer simulator engines
* @author Christopher J. Wood <cjwood@us.ibm.com>
*/
#ifndef _aer_base_noise_hpp_
#define _aer_base_noise_hpp_
#include "framework/operations.hpp"
#include "framework/types.hpp"
#include "framework/rng.hpp"
// Work in progress
/* Schema idea?
{
"type": "gate_error", // string
"operations": ["x", "y", "z"], // list string
"qubits": [[0], [1], ...], // if null or missing gate applies to all qubits
"targets": [[qs,..], ...], // if null or missing gate targets are gate qubits
"params": [mat1, mat2, mat3,...] // kraus CPTP ops
}
*/
namespace AER {
namespace Noise {
using NoiseOps = std::vector<Operations::Op>;
using NoisePair = std::pair<double, NoiseOps>;
//=========================================================================
// Error abstract base class
//=========================================================================
class Error {
public:
// Sample an realization of the error from the error model using the passed
// in RNG engine.
virtual NoiseOps sample_noise(const Operations::Op &op,
const reg_t &qubits,
RngEngine &rng) = 0;
// Return the number of error terms
inline size_t num_qubits() const {return num_qubits_;}
// Return the number of error terms
inline void set_num_qubits(size_t nq) {num_qubits_ = nq;}
protected:
size_t num_qubits_;
};
//=========================================================================
// Noise Model abstract base class
//=========================================================================
class Model {
public:
// Sample noise for the current operation
// Base class returns the input operation with no noise (ideal)
// derived classes should implement this method
virtual NoiseOps sample_noise(const Operations::Op &op) {return {op};}
// Set the RngEngine seed to a fixed value
inline void set_rng_seed(uint_t seed) { rng_ = RngEngine(seed);}
protected:
RngEngine rng_; // initialized with random seed
};
//-------------------------------------------------------------------------
} // end namespace Noise
//-------------------------------------------------------------------------
} // end namespace AER
//-------------------------------------------------------------------------
#endif

297
src/base/noise_model.hpp Normal file
View File

@ -0,0 +1,297 @@
/**
* Copyright 2018, IBM.
*
* This source code is licensed under the Apache License, Version 2.0 found in
* the LICENSE.txt file in the root directory of this source tree.
*/
/**
* @file noise_model.hpp
* @brief Noise model base class for qiskit-aer simulator engines
* @author Christopher J. Wood <cjwood@us.ibm.com>
*/
#ifndef _aer_base_noise_model_hpp_
#define _aer_base_noise_model_hpp_
#include "framework/operations.hpp"
#include "framework/types.hpp"
#include "framework/rng.hpp"
#include "framework/circuit.hpp"
#include "noise/abstract_error.hpp"
// move JSON parsing
#include "noise/gate_error.hpp"
namespace AER {
namespace Base {
//=========================================================================
// Noise Model class
//=========================================================================
// This allows specification of default and individual noise parameters for
// each circuit operation that may either be local (applied to the qubits
// in the operation) or nonlocal (applied to different qubits). The errors
// may each also be applied either before or after the operation as per
// the specification of the AbstractError subclass.
class NoiseModel {
public:
using NoiseOps = std::vector<Operations::Op>;
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);
// Load a noise model from JSON
void load_from_json(const json_t &js);
// Set the RngEngine seed to a fixed value
inline void set_rng_seed(uint_t seed) { rng_ = RngEngine(seed);}
// 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::vector<reg_t> &op_qubit_sets);
// Add a local Error type to the model default for all qubits
// without a specific error present
template <class DerivedError>
void add_local_error(const DerivedError &error,
const std::vector<std::string> &op_labels) {
add_local_error(error, op_labels, {reg_t()});
}
// 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::vector<reg_t> &op_qubit_sets,
const std::vector<reg_t> &noise_qubit_sets);
// Set which single qubit gates should use the X90 waltz error model
inline void set_waltz_gates(const std::set<std::string> &waltz_gates) {
waltz_gates_ = waltz_gates;
}
// Set threshold for applying u1 rotation angles.
// an Op for u1(theta) will only be added if |theta| > 0 and |theta - 2*pi| > 0
inline void set_u1_threshold(double threshold) {
u1_threshold_ = threshold;
}
private:
// Flags which say whether the local or nonlocal error tables are used
bool local_errors_ = false;
bool nonlocal_errors_ = false;
// Table of errors
std::vector<std::unique_ptr<Noise::AbstractError>> error_ptrs_;
// Table indexes a name with a vector of the position of noise operations
// Cant make inner map and unordered map due to lack of hashing for vector<uint_t>
using qubit_map_t = std::map<reg_t, std::vector<size_t>>;
std::unordered_map<std::string, qubit_map_t> local_error_table_;
// Nonlocal noise lookup table. Things get messy here...
// the outer map is a table from gate strings to gate qubit maps
// the gate qubit map is a map from qubits to another map of target qubits
// which is then the final qubit map from target qubits to error_ptr positions
using nonlocal_qubit_map_t = std::map<reg_t, qubit_map_t>;
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_;
// Lookup table for gate strings to enum
enum class Gate {id, x, y, z, h, s, sdg, t, tdg, u0, u1, u2, u3};
const static std::unordered_map<std::string, Gate> waltz_gate_table_;
// waltz threshold for applying u1 rotations if |theta - 2n*pi | > threshold
double u1_threshold_ = 1e-10;
// Rng engine
RngEngine rng_; // initialized with random seed
// Sample a noisy implementation of a two-X90 pulse u3 gate
NoiseOps sample_u3(uint_t qubit, complex_t theta,
complex_t phi, complex_t lam); // TODO
// Sample a noisy implementation of a single-X90 pulse u2 gate
NoiseOps sample_u2(uint_t qubit, complex_t phi, complex_t lam); // TODO
};
//-------------------------------------------------------------------------
// Implementation
//-------------------------------------------------------------------------
NoiseModel::NoiseOps NoiseModel::sample_noise(const Operations::Op &op) {
// Return operator set
NoiseOps noise_before;
NoiseOps noise_after;
// Apply local errors first
if (local_errors_) {
// Get the qubit error map for gate name
auto iter = local_error_table_.find(op.name);
if (iter != local_error_table_.end()) {
// Check if the qubits are listed in the inner model
const auto qubit_map = iter->second;
auto iter_qubits = qubit_map.find(op.qubits);
auto iter_default = qubit_map.find({});
if (iter_qubits != qubit_map.end() ||
iter_default != qubit_map.end()) {
auto &error_positions = (iter_qubits != qubit_map.end())
? iter_qubits->second
: iter_default->second;
for (auto &pos : error_positions) {
auto ops = error_ptrs_[pos]->sample_noise(op.qubits, rng_);
if (error_ptrs_[pos]->errors_after())
noise_after.insert(noise_after.end(), ops.begin(), ops.end());
else
noise_before.insert(noise_before.end(), ops.begin(), ops.end());
}
}
}
}
// Apply nonlocal errors second
if (nonlocal_errors_) {
// Get the inner error map for gate name
auto iter = nonlocal_error_table_.find(op.name);
if (iter != nonlocal_error_table_.end()) {
// Check if the qubits are listed in the inner model
const auto qubit_map = iter->second;
auto iter_qubits = qubit_map.find(op.qubits);
if (iter_qubits != qubit_map.end()) {
for (auto &target_pair : iter_qubits->second) {
auto &target_qubits = target_pair.first;
auto &error_positions = target_pair.second;
for (auto &pos : error_positions) {
auto ops = error_ptrs_[pos]->sample_noise(target_qubits, rng_);
if (error_ptrs_[pos]->errors_after())
noise_after.insert(noise_after.end(), ops.begin(), ops.end());
else
noise_before.insert(noise_before.end(), ops.begin(), ops.end());
}
}
}
}
}
// combine the original op with the noise ops before and after
noise_before.reserve(noise_before.size() + noise_after.size() + 1);
noise_before.push_back(op);
noise_before.insert(noise_before.begin(), noise_after.begin(), noise_after.end());
return noise_before;
}
Circuit NoiseModel::sample_noise(const Circuit &circ) {
Circuit noisy_circ = circ; // copy input circuit
noisy_circ.measure_sampling_flag = false; // disable measurement opt flag
noisy_circ.ops.clear(); // delete ops
// Sample a noisy realization of the circuit
for (const auto &op: circ.ops) {
NoiseOps noisy_op = sample_noise(op);
// insert noisy op sequence into the circuit
noisy_circ.ops.insert(noisy_circ.ops.begin(), noisy_op.begin(), noisy_op.end());
}
return noisy_circ;
}
template <class DerivedError>
void NoiseModel::add_local_error(const DerivedError &error,
const std::vector<std::string> &op_labels,
const std::vector<reg_t> &qubit_sets) {
// Turn on local error flag
if (!op_labels.empty()) {
local_errors_ = true;
}
// Add error term as unique pointer
error_ptrs_.push_back(std::unique_ptr<Noise::AbstractError>(std::make_unique<DerivedError>(error)));
// position is error vector is length - 1
const auto error_pos = error_ptrs_.size() - 1;
// Add error index to the error table
for (const auto &gate: op_labels)
for (const auto &qubits : qubit_sets)
local_error_table_[gate][qubits].push_back(error_pos);
}
template <class DerivedError>
void NoiseModel::add_nonlocal_error(const DerivedError &error,
const std::vector<std::string> &op_labels,
const std::vector<reg_t> &qubit_sets,
const std::vector<reg_t> &noise_qubit_sets) {
// Turn on nonlocal error flag
if (!op_labels.empty() && !qubit_sets.empty() && !noise_qubit_sets.empty()) {
nonlocal_errors_ = true;
}
// Add error term as unique pointer
error_ptrs_.push_back(std::unique_ptr<Noise::AbstractError>(std::make_unique<DerivedError>(error)));
// position is error vector is length - 1
const auto error_pos = error_ptrs_.size() - 1;
// Add error index to the error table
for (const auto &gate: op_labels)
for (const auto &qubits_gate : qubit_sets)
for (const auto &qubits_noise : noise_qubit_sets)
nonlocal_error_table_[gate][qubits_gate][qubits_noise].push_back(error_pos);
}
// Currently we only support a Kraus and unitary gate errors
// the noise config should be an array of gate_error objects:
// [gate_error1, gate_error2, ...]
// where each gate_error object is of the form
// {
// "type": "gate_error", // string
// "operations": ["x", "y", "z"], // list string
// "params": [mat1, mat2, mat3]
// }
// TODO add reset errors and X90 based errors
void NoiseModel::load_from_json(const json_t &js) {
// Check json is an array
if (!js.is_array()) {
throw std::invalid_argument("Noise params JSON is not an array");
}
for (const auto &gate_js : js) {
std::string type;
JSON::get_value(type, "type", gate_js);
std::vector<std::string> ops;
JSON::get_value(ops, "operations", gate_js);
std::vector<cmatrix_t> mats;
JSON::get_value(mats, "params", gate_js);
if (type == "gate_error") {
Noise::GateError error(mats);
add_local_error(error, ops);
} else {
throw std::invalid_argument("Invalid noise type (" + type + ")");
}
}
}
void from_json(const json_t &js, NoiseModel &model) {
model = NoiseModel(js);
}
//-------------------------------------------------------------------------
} // end namespace Noise
//-------------------------------------------------------------------------
} // end namespace AER
//-------------------------------------------------------------------------
#endif

View File

@ -63,10 +63,10 @@ struct Op {
using qubit_set_t = std::set<uint_t, std::greater<uint_t>>; using qubit_set_t = std::set<uint_t, std::greater<uint_t>>;
using pauli_component_t = std::tuple<complex_t, // component coefficient using pauli_component_t = std::tuple<complex_t, // component coefficient
qubit_set_t, // component qubits qubit_set_t, // component qubits
std::string>; // component pauli string std::string>; // component Pauli string
using matrix_component_t = std::tuple<complex_t, // component coefficient using matrix_component_t = std::tuple<complex_t, // component coefficient
std::vector<reg_t>, // qubit subregisters Ex: [[2], [1, 0]] std::vector<reg_t>, // qubit sub-registers Ex: [[2], [1, 0]]
std::vector<cmatrix_t>>; // submatrices Ex: [M2, M10] std::vector<cmatrix_t>>; // sub-matrices Ex: [M2, M10]
std::vector<pauli_component_t> params_pauli_obs; std::vector<pauli_component_t> params_pauli_obs;
std::vector<matrix_component_t> params_mat_obs; // not that diagonal matrices are stored as std::vector<matrix_component_t> params_mat_obs; // not that diagonal matrices are stored as
// 1 x M row-matrices // 1 x M row-matrices
@ -97,6 +97,55 @@ inline void check_qubits(const reg_t &qubits) {
} }
} }
//------------------------------------------------------------------------------
// Generator functions
//------------------------------------------------------------------------------
Op make_mat(const reg_t &qubits, const cmatrix_t &mat, std::string label = "") {
Op op;
op.name = "mat";
op.qubits = qubits;
op.mats = {mat};
if (label != "")
op.string_params = {label};
return op;
};
template <typename T> // real or complex numeric type
Op make_u1(uint_t qubit, T lam) {
Op op;
op.name = "u1";
op.qubits = {qubit};
op.params = {lam};
return op;
};
template <typename T> // real or complex numeric type
Op make_u2(uint_t qubit, T phi, T lam) {
Op op;
op.name = "u2";
op.qubits = {qubit};
op.params = {phi, lam};
return op;
};
template <typename T> // real or complex numeric type
Op make_u3(uint_t qubit, T theta, T phi, T lam) {
Op op;
op.name = "u3";
op.qubits = {qubit};
op.params = {theta, phi, lam};
return op;
};
Op make_reset(uint_t qubit, uint_t state = 0) {
Op op;
op.name = "reset";
op.qubits = {qubit};
op.params = {state};
return op;
};
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// JSON conversion // JSON conversion
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -208,10 +257,9 @@ Op json_to_op_reset(const json_t &js) {
Op op; Op op;
op.name = "reset"; op.name = "reset";
JSON::get_value(op.qubits, "qubits", js); JSON::get_value(op.qubits, "qubits", js);
JSON::get_value(op.params, "params", js); op.params = {0}; // default reset to 0 state
// If params is missing default reset to 0 if (JSON::check_key("params", js)) {
if (op.params.empty()) { op.params[0] = js["params"].get<rvector_t>()[0];
op.params = cvector_t(op.qubits.size(), 0.);
} }
// Validation // Validation
check_qubits(op.qubits); check_qubits(op.qubits);
@ -277,12 +325,18 @@ Op json_to_op_mat(const json_t &js) {
cmatrix_t mat; cmatrix_t mat;
JSON::get_value(mat, "params", js); JSON::get_value(mat, "params", js);
op.mats.push_back(mat); op.mats.push_back(mat);
// Check for a label
std::string label;
JSON::get_value(label, "label", js);
if (!label.empty()) {
op.string_params.push_back(label);
}
// Validation // Validation
check_qubits(op.qubits); check_qubits(op.qubits);
return op; return op;
} }
// TODO: Remove and treat as special case of mat.
Op json_to_op_dmat(const json_t &js) { Op json_to_op_dmat(const json_t &js) {
Op op; Op op;
op.name = "dmat"; op.name = "dmat";

View File

@ -28,21 +28,23 @@ namespace Utils {
class Matrix { class Matrix {
public: public:
// Single-qubit gates // Single-qubit gates
const static cmatrix_t I; const static cmatrix_t I; // name: "id"
const static cmatrix_t X; const static cmatrix_t X; // name: "x"
const static cmatrix_t Y; const static cmatrix_t Y; // name: "y"
const static cmatrix_t Z; const static cmatrix_t Z; // name: "z"
const static cmatrix_t H; const static cmatrix_t H; // name: "h"
const static cmatrix_t S; const static cmatrix_t S; // name: "s"
const static cmatrix_t T; const static cmatrix_t Sdg; // name: "sdg"
const static cmatrix_t X90; const static cmatrix_t T; // name: "t"
const static cmatrix_t Tdg; // name: "tdg"
const static cmatrix_t X90; // name: "x90"
// Two-qubit gates // Two-qubit gates
const static cmatrix_t CX; const static cmatrix_t CX; // name: "cx"
const static cmatrix_t CZ; const static cmatrix_t CZ; // name: "cz"
const static cmatrix_t SWAP; const static cmatrix_t SWAP; // name: "swap"
const static cmatrix_t CR; // TODO const static cmatrix_t CR; // TODO
const static cmatrix_t CR90; // TODO const static cmatrix_t CR90; // TODO
// Identity Matrix // Identity Matrix
static cmatrix_t Identity(size_t dim); static cmatrix_t Identity(size_t dim);
@ -51,6 +53,33 @@ public:
static cmatrix_t U1(double lam); static cmatrix_t U1(double lam);
static cmatrix_t U2(double phi, double lam); static cmatrix_t U2(double phi, double lam);
static cmatrix_t U3(double theta, double phi, double lam); static cmatrix_t U3(double theta, double phi, double lam);
// Complex arguments are implemented by taking std::real
// of the input
inline static cmatrix_t U1(complex_t lam) {return U1(std::real(lam));}
inline static cmatrix_t U2(complex_t phi, complex_t lam) {
return U2(std::real(phi), std::real(lam));
}
inline static cmatrix_t U3(complex_t theta, complex_t phi, complex_t lam) {
return U3(std::real(theta), std::real(phi), std::real(lam));
};
// Return the matrix for a named matrix string
// Allowed names correspond to all the const static single-qubit
// and two-qubit gate members
inline static const cmatrix_t from_name(const std::string &name) {
return *label_map_.at(name);
}
// Check if the input name string is allowed
inline static bool allowed_name(const std::string &name) {
return (label_map_.find(name) != label_map_.end());
}
private:
// Lookup table that returns a pointer to the static data member
const static std::unordered_map<std::string, const cmatrix_t*> label_map_;
}; };
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -202,9 +231,15 @@ const cmatrix_t Matrix::Z = make_matrix<complex_t>({{{1, 0}, {0, 0}},
const cmatrix_t Matrix::S = make_matrix<complex_t>({{{1, 0}, {0, 0}}, const cmatrix_t Matrix::S = make_matrix<complex_t>({{{1, 0}, {0, 0}},
{{0, 0}, {0, 1}}}); {{0, 0}, {0, 1}}});
const cmatrix_t Matrix::Sdg = make_matrix<complex_t>({{{1, 0}, {0, 0}},
{{0, 0}, {0, -1}}});
const cmatrix_t Matrix::T = make_matrix<complex_t>({{{1, 0}, {0, 0}}, const cmatrix_t Matrix::T = make_matrix<complex_t>({{{1, 0}, {0, 0}},
{{0, 0}, {1 / std::sqrt(2), 1 / std::sqrt(2)}}}); {{0, 0}, {1 / std::sqrt(2), 1 / std::sqrt(2)}}});
const cmatrix_t Matrix::Tdg = make_matrix<complex_t>({{{1, 0}, {0, 0}},
{{0, 0}, {1 / std::sqrt(2), -1 / std::sqrt(2)}}});
const cmatrix_t Matrix::H = make_matrix<complex_t>({{{1 / std::sqrt(2.), 0}, {1 / std::sqrt(2.), 0}}, const cmatrix_t Matrix::H = make_matrix<complex_t>({{{1 / std::sqrt(2.), 0}, {1 / std::sqrt(2.), 0}},
{{1 / std::sqrt(2.), 0}, {-1 / std::sqrt(2.), 0}}}); {{1 / std::sqrt(2.), 0}, {-1 / std::sqrt(2.), 0}}});
@ -212,23 +247,31 @@ const cmatrix_t Matrix::X90 = make_matrix<complex_t>({{{1. / std::sqrt(2.), 0},
{{0, -1. / std::sqrt(2.)}, {1. / std::sqrt(2.), 0}}}); {{0, -1. / std::sqrt(2.)}, {1. / std::sqrt(2.), 0}}});
const cmatrix_t Matrix::CX = make_matrix<complex_t>({{{1, 0}, {0, 0}, {0, 0}, {0, 0}}, const cmatrix_t Matrix::CX = make_matrix<complex_t>({{{1, 0}, {0, 0}, {0, 0}, {0, 0}},
{{0, 0}, {0, 0}, {0, 0}, {1, 0}}, {{0, 0}, {0, 0}, {0, 0}, {1, 0}},
{{0, 0}, {0, 0}, {1, 0}, {0, 0}}, {{0, 0}, {0, 0}, {1, 0}, {0, 0}},
{{0, 0}, {1, 0}, {0, 0}, {0, 0}}}); {{0, 0}, {1, 0}, {0, 0}, {0, 0}}});
const cmatrix_t Matrix::CZ = make_matrix<complex_t>({{{1, 0}, {0, 0}, {0, 0}, {0, 0}}, const cmatrix_t Matrix::CZ = make_matrix<complex_t>({{{1, 0}, {0, 0}, {0, 0}, {0, 0}},
{{0, 0}, {1, 0}, {0, 0}, {0, 0}}, {{0, 0}, {1, 0}, {0, 0}, {0, 0}},
{{0, 0}, {0, 0}, {1, 0}, {0, 0}}, {{0, 0}, {0, 0}, {1, 0}, {0, 0}},
{{0, 0}, {0, 0}, {0, 0}, {-1, 0}}}); {{0, 0}, {0, 0}, {0, 0}, {-1, 0}}});
const cmatrix_t Matrix::SWAP = make_matrix<complex_t>({{{1, 0}, {0, 0}, {0, 0}, {0, 0}}, const cmatrix_t Matrix::SWAP = make_matrix<complex_t>({{{1, 0}, {0, 0}, {0, 0}, {0, 0}},
{{0, 0}, {0, 0}, {1, 0}, {0, 0}}, {{0, 0}, {0, 0}, {1, 0}, {0, 0}},
{{0, 0}, {1, 0}, {0, 0}, {0, 0}}, {{0, 0}, {1, 0}, {0, 0}, {0, 0}},
{{0, 0}, {0, 0}, {0, 0}, {1, 0}}}); {{0, 0}, {0, 0}, {0, 0}, {1, 0}}});
// TODO const cmatrix_t Matrix::CR = ... // TODO const cmatrix_t Matrix::CR = ...
// TODO const cmatrix_t Matrix::CR90 = ... // TODO const cmatrix_t Matrix::CR90 = ...
// Lookup table
const std::unordered_map<std::string, const cmatrix_t*> Matrix::label_map_ = {
{"id", &Matrix::I}, {"x", &Matrix::X}, {"y", &Matrix::Y}, {"z", &Matrix::Z},
{"h", &Matrix::H}, {"s", &Matrix::S}, {"sdg", &Matrix::Sdg},
{"t", &Matrix::T}, {"tdg", &Matrix::Tdg}, {"x90", &Matrix::X90},
{"cx", &Matrix::CX}, {"cz", &Matrix::CZ}, {"swap", &Matrix::SWAP}
};
cmatrix_t Matrix::Identity(size_t dim) { cmatrix_t Matrix::Identity(size_t dim) {
cmatrix_t mat(dim, dim); cmatrix_t mat(dim, dim);
for (size_t j=0; j<dim; j++) for (size_t j=0; j<dim; j++)

View File

@ -0,0 +1,59 @@
/**
* Copyright 2018, IBM.
*
* This source code is licensed under the Apache License, Version 2.0 found in
* the LICENSE.txt file in the root directory of this source tree.
*/
/**
* @file abstract_error.hpp
* @brief Abstract Error base class for Noise model errors
* @author Christopher J. Wood <cjwood@us.ibm.com>
*/
#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:
// Alias for return type
using NoiseOps = std::vector<Operations::Op>;
// 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) = 0;
// 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;
};
//-------------------------------------------------------------------------
} // end namespace Noise
//-------------------------------------------------------------------------
} // end namespace AER
//-------------------------------------------------------------------------
#endif

View File

@ -29,7 +29,7 @@ namespace Noise {
// This combines unitary and Kraus errors into one error class to prevent // This combines unitary and Kraus errors into one error class to prevent
// the inefficient use of unitary matrices in a Kraus decomposition. // the inefficient use of unitary matrices in a Kraus decomposition.
class GateError : public Error { class GateError : public AbstractError {
public: public:
GateError() = default; GateError() = default;
@ -40,8 +40,7 @@ public:
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// Sample a noisy implementation of op // Sample a noisy implementation of op
NoiseOps sample_noise(const Operations::Op &op, NoiseOps sample_noise(const reg_t &qubits,
const reg_t &qubits,
RngEngine &rng) override; RngEngine &rng) override;
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
@ -81,18 +80,17 @@ protected:
// Implementation // Implementation
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
NoiseOps GateError::sample_noise(const Operations::Op &op, GateError::NoiseOps GateError::sample_noise(const reg_t &qubits,
const reg_t &qubits, RngEngine &rng) {
RngEngine &rng) {
auto noise_type = rng.rand_int(probabilities_); auto noise_type = rng.rand_int(probabilities_);
switch (noise_type) { switch (noise_type) {
case 0: case 0:
return {op}; return {};
case 1: case 1:
return unitary_error_.sample_noise(op, qubits, rng); return unitary_error_.sample_noise(qubits, rng);
case 2: case 2:
return kraus_error_.sample_noise(op, qubits, rng); return kraus_error_.sample_noise(qubits, rng);
default: default:
// We shouldn't get here, but just in case... // We shouldn't get here, but just in case...
throw std::invalid_argument("GateError type is out of range."); throw std::invalid_argument("GateError type is out of range.");

View File

@ -14,7 +14,7 @@
#ifndef _aer_noise_kraus_error_hpp_ #ifndef _aer_noise_kraus_error_hpp_
#define _aer_noise_kraus_error_hpp_ #define _aer_noise_kraus_error_hpp_
#include "base/noise.hpp" #include "noise/abstract_error.hpp"
// TODO: optimization for gate fusion with original operator matrix // TODO: optimization for gate fusion with original operator matrix
@ -32,7 +32,7 @@ namespace Noise {
// of the Kraus error applying. If this is set the error map is // of the Kraus error applying. If this is set the error map is
// Error(rho) = (1-p) rho + p E(rho), with E defined as in (1). // Error(rho) = (1-p) rho + p E(rho), with E defined as in (1).
class KrausError : public Error { class KrausError : public AbstractError {
public: public:
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
@ -40,24 +40,48 @@ public:
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// Return the operator followed by a Kraus operation // Return the operator followed by a Kraus operation
NoiseOps sample_noise(const Operations::Op &op, NoiseOps sample_noise(const reg_t &qubits,
const reg_t &qubits,
RngEngine &rng) override; RngEngine &rng) override;
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// Additional class methods // Additional class methods
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// Set the Kraus matrices for the error
void set_kraus(const std::vector<cmatrix_t> &ops); void set_kraus(const std::vector<cmatrix_t> &ops);
// Set the probability of the Kraus error channel being applied
// the probability of no error (identity) will be 1 - p_Kraus
void set_probability(double p_kraus); void set_probability(double p_kraus);
// 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;}
// Set whether the input operator should be combined into the error
// term (if compatible). The default is True
inline void combine_error(bool val) {combine_error_ = val;}
protected: protected:
// Probabilities of Kraus error being applied // Probabilities of Kraus error being applied
// When sampled the outcomes correspond to: // When sampled the outcomes correspond to:
// 0 -> Kraus error map applied // 0 -> Kraus error map applied
// 1 -> no error // 1 -> no error
std::discrete_distribution<uint_t> probabilities_; std::discrete_distribution<uint_t> probabilities_;
Operations::Op kraus_; // CPTP Kraus map op
// The Kraus operation for the error channel
// This still needs to have the qubits it is applied to added to the Op
// which is done during the sample_noise method
Operations::Op kraus_;
// Flag for applying errors before or after the operation
bool errors_after_op_ = true;
// Combine error with input matrix to return a single operation
// if it acts on the same qubits
bool combine_error_ = true;
}; };
@ -66,9 +90,8 @@ protected:
//========================================================================= //=========================================================================
// TODO add gate fusion optimization // TODO add gate fusion optimization
NoiseOps KrausError::sample_noise(const Operations::Op &op, KrausError::NoiseOps KrausError::sample_noise(const reg_t &qubits,
const reg_t &qubits, RngEngine &rng) {
RngEngine &rng) {
// check we have the correct number of qubits in the op for the error // check we have the correct number of qubits in the op for the error
//if (qubits.size() != num_qubits_) { //if (qubits.size() != num_qubits_) {
@ -76,13 +99,13 @@ NoiseOps KrausError::sample_noise(const Operations::Op &op,
//} //}
auto r = rng.rand_int(probabilities_); auto r = rng.rand_int(probabilities_);
if (r == 1) { if (r == 1) {
// no error return {}; // no error
return {op};
} }
Operations::Op err = kraus_; Operations::Op error = kraus_;
err.qubits = qubits; error.qubits = qubits;
return {op, err}; return {error};
}; }
void KrausError::set_kraus(const std::vector<cmatrix_t> &ops) { void KrausError::set_kraus(const std::vector<cmatrix_t> &ops) {
kraus_ = Operations::Op(); kraus_ = Operations::Op();
@ -90,6 +113,7 @@ void KrausError::set_kraus(const std::vector<cmatrix_t> &ops) {
kraus_.mats = ops; kraus_.mats = ops;
} }
void KrausError::set_probability(double p_kraus) { void KrausError::set_probability(double p_kraus) {
if (p_kraus < 0 || p_kraus > 1) { if (p_kraus < 0 || p_kraus > 1) {
throw std::invalid_argument("Invalid KrausError probability."); throw std::invalid_argument("Invalid KrausError probability.");

127
src/noise/reset_error.hpp Normal file
View File

@ -0,0 +1,127 @@
/**
* Copyright 2018, IBM.
*
* This source code is licensed under the Apache License, Version 2.0 found in
* the LICENSE.txt file in the root directory of this source tree.
*/
/**
* @file reset_error.hpp
* @brief Reset Error class for Qiskit-Aer simulator
* @author Christopher J. Wood <cjwood@us.ibm.com>
*/
#ifndef _aer_noise_reset_error_hpp_
#define _aer_noise_reset_error_hpp_
#include "noise/unitary_error.hpp"
namespace AER {
namespace Noise {
//=========================================================================
// Reset Error class
//=========================================================================
// Single qubit reset error:
// E(rho) = (1 - P) rho + \sum_j p_j |j><j|
// where: P = sum_j p_j
// p_j are the probabilities of reset to each Z-basis state
// If this error is applied to multiple qubits (eg for a multi-qubit gate)
// The probabilities can either be independent of which qubit is being
// reset, or be specified for each qubit being reset.
class ResetError : public AbstractError {
public:
//-----------------------------------------------------------------------
// Error base required methods
//-----------------------------------------------------------------------
// Sample a noisy implementation of op
NoiseOps sample_noise(const reg_t &qubits,
RngEngine &rng) override;
//-----------------------------------------------------------------------
// Additional class methods
//-----------------------------------------------------------------------
// Set the default probabilities for reset error for a qubit.
// The input vector should be length-2 with
// probs[0] being the probability of reseting to |0> state
// probs[1] the probability of resetting to the |1> state
// The probability of no-reset error is given by 1 - p[0] - p[1]
inline void set_default_probabilities(const rvector_t &probs) {
default_probabilities_ = format_probabilities(probs);
}
// Set the reset error probabilities for a specific qubit.
// This will override the default probabilities for that qubit.
inline void set_qubit_probabilities(uint_t qubit, const rvector_t &probs) {
multi_probabilities_[qubit] = format_probabilities(probs);
}
protected:
using probs_t = std::discrete_distribution<uint_t>;
// Flag for applying errors before or after the operation
bool errors_after_op_ = true;
// Reset error probabilities for each qubit
// 0 -> no reset
// 1 -> reset to |0>
// 2 -> reset to |1>
probs_t default_probabilities_;
// Map for getting reset parameters for each qubit
std::unordered_map<uint_t, probs_t> multi_probabilities_;
// Helper function to convert input to internal probability format
probs_t format_probabilities(const rvector_t &probs);
};
//-------------------------------------------------------------------------
// Implementation: Mixed unitary error subclass
//-------------------------------------------------------------------------
ResetError::NoiseOps ResetError::sample_noise(const reg_t &qubits,
RngEngine &rng) {
// Initialize return ops,
NoiseOps ret;
// Sample reset error for each qubit
for (const auto &q: qubits) {
auto it = multi_probabilities_.find(q);
auto r = (it == multi_probabilities_.end())
? rng.rand_int(default_probabilities_)
: rng.rand_int(it->second);
if (r > 0)
ret.push_back(Operations::make_reset(q, r));
}
return ret;
}
ResetError::probs_t ResetError::format_probabilities(const rvector_t &probs) {
// error probability vector with [0] = p_identity
rvector_t error_probs = {1.};
for (const auto &p : probs) {
if (p < 0 || p > 1) {
throw std::invalid_argument("ResetError probability is not valid (p=" + std::to_string(p) +").");
}
error_probs[0] -= p;
error_probs.push_back(p);
}
// Check the p_identity value is valid
if (error_probs[0] < 0) {
throw std::invalid_argument("ResetError probabilities > 1.");
}
return probs_t(error_probs.begin(), error_probs.end());
}
//-------------------------------------------------------------------------
} // end namespace Noise
//-------------------------------------------------------------------------
} // end namespace AER
//-------------------------------------------------------------------------
#endif

View File

@ -14,7 +14,7 @@
#ifndef _aer_noise_unitary_error_hpp_ #ifndef _aer_noise_unitary_error_hpp_
#define _aer_noise_unitary_error_hpp_ #define _aer_noise_unitary_error_hpp_
#include "base/noise.hpp" #include "noise/abstract_error.hpp"
// TODO optimization for gate fusion with original operator matrix // TODO optimization for gate fusion with original operator matrix
@ -28,16 +28,14 @@ namespace Noise {
// Unitary error class that can model mixed unitary error channels, and // Unitary error class that can model mixed unitary error channels, and
// coherent unitary errors. // coherent unitary errors.
class UnitaryError : public Error { class UnitaryError : public AbstractError {
public: public:
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// Error base required methods // Error base required methods
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
// Sample a noisy implementation of op // Sample a noisy implementation of op
NoiseOps sample_noise(const Operations::Op &op, NoiseOps sample_noise(const reg_t &qubits,
const reg_t &qubits,
RngEngine &rng) override; RngEngine &rng) override;
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
@ -53,27 +51,42 @@ public:
// Sets the unitary error matrices for the error map // Sets the unitary error matrices for the error map
void set_unitaries(const std::vector<cmatrix_t> &mats); void set_unitaries(const std::vector<cmatrix_t> &mats);
// 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;}
// Set whether the input operator should be combined into the error
// term (if compatible). The default is True
inline void combine_error(bool val) {combine_error_ = val;}
protected: protected:
// Probabilities, first entry is no-error (identity) // Probabilities, first entry is no-error (identity)
std::discrete_distribution<uint_t> probabilities_; std::discrete_distribution<uint_t> probabilities_;
// List of unitary error matrices // List of unitary error matrices
std::vector<cmatrix_t> unitaries_; std::vector<cmatrix_t> unitaries_;
// Flag for applying errors before or after the operation
bool errors_after_op_ = true;
// Combine error with input matrix to return a single operation
// if it acts on the same qubits
bool combine_error_ = true;
}; };
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
// Implementation: Mixed unitary error subclass // Implementation: Mixed unitary error subclass
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
// TODO combine terms into single matrix UnitaryError::NoiseOps UnitaryError::sample_noise(const reg_t &qubits,
NoiseOps UnitaryError::sample_noise(const Operations::Op &op, RngEngine &rng) {
const reg_t &qubits,
RngEngine &rng) {
auto r = rng.rand_int(probabilities_); auto r = rng.rand_int(probabilities_);
// Check for no error case // Check for no error case
if (r == 0) { if (r == 0) {
// no error // no error
return {op}; return {};
} }
// Check for invalid arguments // Check for invalid arguments
if (r > unitaries_.size()) { if (r > unitaries_.size()) {
@ -82,17 +95,15 @@ NoiseOps UnitaryError::sample_noise(const Operations::Op &op,
if (unitaries_.empty()) { if (unitaries_.empty()) {
throw std::invalid_argument("Unitary error matrices are not set."); throw std::invalid_argument("Unitary error matrices are not set.");
} }
//if (qubits.size() != num_qubits_) { // Get the error operation for the unitary
// throw std::invalid_argument("Incorrect number of qubits for the error.");
//}
// TODO: combine op and error into single matrix operation
Operations::Op error; Operations::Op error;
error.name = "mat"; error.name = "mat";
error.mats.push_back(unitaries_[r - 1]); error.mats.push_back(unitaries_[r - 1]);
error.qubits = qubits; error.qubits = qubits;
return {op, error}; return {error};
} }
void UnitaryError::set_probabilities(const rvector_t &probs) { void UnitaryError::set_probabilities(const rvector_t &probs) {
rvector_t probs_with_identity({1.}); rvector_t probs_with_identity({1.});
bool probs_valid = true; bool probs_valid = true;

View File

@ -424,7 +424,7 @@ void State::apply_op(const Operations::Op &op) {
data_.apply_cz(op.qubits[0], op.qubits[1]); data_.apply_cz(op.qubits[0], op.qubits[1]);
break; break;
case Gates::reset: case Gates::reset:
apply_reset(op.qubits, 0); apply_reset(op.qubits, uint_t(std::real(op.params[0])));
break; break;
case Gates::barrier: case Gates::barrier:
break; break;