Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix noise sampling on shot-branching #2098

Merged
merged 21 commits into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 17 additions & 9 deletions src/framework/operations.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,6 @@ enum class OpType {
superop,
roerror,
noise_switch,
sample_noise,
// Save instructions
save_state,
save_expval,
Expand Down Expand Up @@ -532,9 +531,6 @@ inline std::ostream &operator<<(std::ostream &stream, const OpType &type) {
case OpType::qerror_loc:
stream << "qerror_loc";
break;
case OpType::sample_noise:
stream << "sample_noise";
break;
case OpType::noise_switch:
stream << "noise_switch";
break;
Expand Down Expand Up @@ -614,11 +610,14 @@ struct Op {
string_params; // used for label, control-flow, and boolean functions

// Conditional Operations
bool conditional = false; // is gate conditional gate
uint_t conditional_reg; // (opt) the (single) register location to look up for
// conditional
BinaryOp binary_op; // (opt) boolean function relation
std::shared_ptr<CExpr> expr; // (opt) classical expression
// is gate conditional gate
bool conditional = false;
// (opt) the (single) register location to look up for conditional
uint_t conditional_reg = 0;
// (opt) boolean function relation
BinaryOp binary_op;
// (opt) classical expression
std::shared_ptr<CExpr> expr = nullptr;

// Measurement
reg_t memory; // (opt) register operation it acts on (measure)
Expand All @@ -640,6 +639,9 @@ struct Op {
// Save
DataSubType save_type = DataSubType::single;

// runtime noise sampling
bool sample_noise = false;

// runtime parameter bind
bool has_bind_params = false;
};
Expand Down Expand Up @@ -1319,8 +1321,14 @@ inline Op bind_parameter(const Op &src, const uint_t iparam,
op.type = src.type;
op.name = src.name;
op.qubits = src.qubits;
op.regs = src.regs;
op.int_params = src.int_params;
op.string_params = src.string_params;
op.conditional = src.conditional;
op.conditional_reg = src.conditional_reg;
op.binary_op = src.binary_op;
op.expr = src.expr;
op.has_bind_params = false;

if (src.params.size() > 0) {
uint_t stride = src.params.size() / num_params;
Expand Down
113 changes: 70 additions & 43 deletions src/noise/noise_model.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,13 @@ class NoiseModel {
const Method method = Method::circuit,
bool sample_at_runtime = false) const;

NoiseOps sample_noise_loc(const Operations::Op &op, RngEngine &rng) const;
NoiseOps sample_noise_at_runtime(const Operations::Op &op,
RngEngine &rng) const;

void sample_noise_at_runtime(const Operations::Op &op,
NoiseModel::NoiseOps &noise_before,
NoiseModel::NoiseOps &noise_after,
RngEngine &rng) const;

// Enable superop sampling method
// This will cause all QuantumErrors stored in the noise model
Expand Down Expand Up @@ -151,14 +157,12 @@ class NoiseModel {
void sample_local_quantum_noise(const Operations::Op &op,
NoiseOps &noise_before, NoiseOps &noise_after,
RngEngine &rng, const Method method,
const reg_t &mapping,
bool sample_at_runtime) const;
const reg_t &mapping) const;

void sample_nonlocal_quantum_noise(const Operations::Op &op,
NoiseOps &noise_ops, NoiseOps &noise_after,
RngEngine &rng, const Method method,
const reg_t &mapping,
bool sample_at_runtime) const;
const reg_t &mapping) const;

// Sample noise for the current operation
NoiseOps sample_noise_helper(const Operations::Op &op, RngEngine &rng,
Expand All @@ -176,9 +180,6 @@ class NoiseModel {
const std::vector<reg_t> &op_qubits,
const std::vector<reg_t> &noise_qubits);

// create loc noise
NoiseOps create_noise_loc(const Operations::Op &op) const;

// Flags which say whether the local or nonlocal error tables are used
bool local_quantum_errors_ = false;
bool nonlocal_quantum_errors_ = false;
Expand Down Expand Up @@ -245,7 +246,8 @@ class NoiseModel {
std::unordered_set<Method> enabled_methods_ =
std::unordered_set<Method>({Method::circuit});

// saved qubit mapping for runtime loc
// saved qubit mapping and method for runtime noise sampling
mutable Method circ_method_;
mutable reg_t circ_mapping_;
};

Expand Down Expand Up @@ -281,21 +283,57 @@ Circuit NoiseModel::sample_noise(const Circuit &circ, RngEngine &rng,
return sample_noise_circuit(circ, rng, method, sample_at_runtime);
}

NoiseModel::NoiseOps NoiseModel::sample_noise_loc(const Operations::Op &op,
RngEngine &rng) const {
auto noise_ops =
sample_noise_op(op, rng, Method::circuit, circ_mapping_, false);
NoiseModel::NoiseOps
NoiseModel::sample_noise_at_runtime(const Operations::Op &op,
RngEngine &rng) const {
// Return operator set
NoiseOps noise_before;
NoiseOps noise_after;

sample_noise_at_runtime(op, noise_before, noise_after, rng);

// Combine errors
auto &noise_ops = noise_before;
noise_ops.reserve(noise_before.size() + noise_after.size() + 1);
Operations::Op op_sampled = op;
op_sampled.sample_noise = false;
noise_ops.push_back(op_sampled);
noise_ops.insert(noise_ops.end(),
std::make_move_iterator(noise_after.begin()),
std::make_move_iterator(noise_after.end()));

return noise_ops;
}

void NoiseModel::sample_noise_at_runtime(const Operations::Op &op,
NoiseModel::NoiseOps &noise_before,
NoiseModel::NoiseOps &noise_after,
RngEngine &rng) const {
// Apply local errors first
sample_local_quantum_noise(op, noise_before, noise_after, rng, circ_method_,
circ_mapping_);
// Apply nonlocal errors second
sample_nonlocal_quantum_noise(op, noise_before, noise_after, rng,
circ_method_, circ_mapping_);
// Apply readout error to measure ops
if (op.type == Operations::OpType::measure) {
sample_readout_noise(op, noise_after, rng, circ_mapping_);
}

// If original op is conditional, make all the noise operations also
// conditional
if (op.conditional) {
for (auto &noise_op : noise_ops) {
for (auto &noise_op : noise_before) {
noise_op.conditional = op.conditional;
noise_op.conditional_reg = op.conditional_reg;
noise_op.binary_op = op.binary_op;
}
for (auto &noise_op : noise_after) {
noise_op.conditional = op.conditional;
noise_op.conditional_reg = op.conditional_reg;
noise_op.binary_op = op.binary_op;
}
}
return noise_ops;
}

Circuit NoiseModel::sample_noise_circuit(const Circuit &circ, RngEngine &rng,
Expand Down Expand Up @@ -355,6 +393,7 @@ Circuit NoiseModel::sample_noise_circuit(const Circuit &circ, RngEngine &rng,
noisy_circ.set_params();
if (sample_at_runtime) {
noisy_circ.can_sample = false;
circ_method_ = method;
circ_mapping_ = mapping;
}
return noisy_circ;
Expand Down Expand Up @@ -518,21 +557,27 @@ NoiseModel::sample_noise_helper(const Operations::Op &op, RngEngine &rng,
NoiseOps noise_after;
// Apply local errors first
sample_local_quantum_noise(op, noise_before, noise_after, rng, method,
mapping, sample_at_runtime);
mapping);
// Apply nonlocal errors second
sample_nonlocal_quantum_noise(op, noise_before, noise_after, rng, method,
mapping, sample_at_runtime);
mapping);
// Apply readout error to measure ops
if (op.type == Operations::OpType::measure) {
sample_readout_noise(op, noise_after, rng, mapping);
}

if (sample_at_runtime &&
(noise_before.size() > 0 || noise_after.size() > 0)) {
NoiseOps ret(1);
ret[0] = op;
ret[0].sample_noise = true;
return ret;
}

// Combine errors
auto &noise_ops = noise_before;
noise_ops.reserve(noise_before.size() + noise_after.size() + 1);
if (op.type != Operations::OpType::sample_noise) {
noise_ops.push_back(op);
}
noise_ops.push_back(op);
noise_ops.insert(noise_ops.end(),
std::make_move_iterator(noise_after.begin()),
std::make_move_iterator(noise_after.end()));
Expand Down Expand Up @@ -655,8 +700,7 @@ void NoiseModel::sample_local_quantum_noise(const Operations::Op &op,
NoiseOps &noise_before,
NoiseOps &noise_after,
RngEngine &rng, const Method method,
const reg_t &mapping,
bool sample_at_runtime) const {
const reg_t &mapping) const {
// If no errors are defined pass
if (local_quantum_errors_ == false)
return;
Expand Down Expand Up @@ -714,11 +758,7 @@ void NoiseModel::sample_local_quantum_noise(const Operations::Op &op,
: iter_default->second;
for (auto &pos : error_positions) {
NoiseOps noise_ops;
if (sample_at_runtime)
noise_ops = create_noise_loc(op);
else
noise_ops =
quantum_errors_[pos].sample_noise(op_qubits, rng, method);
noise_ops = quantum_errors_[pos].sample_noise(op_qubits, rng, method);
// Duplicate same sampled error operations
if (quantum_errors_[pos].errors_after())
noise_after.insert(noise_after.end(), noise_ops.begin(),
Expand All @@ -734,8 +774,7 @@ void NoiseModel::sample_local_quantum_noise(const Operations::Op &op,

void NoiseModel::sample_nonlocal_quantum_noise(
const Operations::Op &op, NoiseOps &noise_before, NoiseOps &noise_after,
RngEngine &rng, const Method method, const reg_t &mapping,
bool sample_at_runtime) const {
RngEngine &rng, const Method method, const reg_t &mapping) const {
// If no errors are defined pass
if (nonlocal_quantum_errors_ == false)
return;
Expand Down Expand Up @@ -783,12 +822,8 @@ void NoiseModel::sample_nonlocal_quantum_noise(
auto &error_positions = target_pair.second;
for (auto &pos : error_positions) {
NoiseOps ops;
if (sample_at_runtime)
ops = create_noise_loc(op);
else {
ops = quantum_errors_[pos].sample_noise(string2reg(target_qubits),
rng, method);
}
ops = quantum_errors_[pos].sample_noise(string2reg(target_qubits),
rng, method);
if (quantum_errors_[pos].errors_after())
noise_after.insert(noise_after.end(), ops.begin(), ops.end());
else
Expand All @@ -800,14 +835,6 @@ void NoiseModel::sample_nonlocal_quantum_noise(
}
}

NoiseModel::NoiseOps
NoiseModel::create_noise_loc(const Operations::Op &op) const {
NoiseOps ops(1);
ops[0] = op;
ops[0].type = Operations::OpType::sample_noise;
return ops;
}

cmatrix_t NoiseModel::op2superop(const Operations::Op &op) const {
switch (op.type) {
case Operations::OpType::superop:
Expand Down
Loading
Loading