Skip to content

Commit

Permalink
Fix parameter array initialization for runtime parameter binding (#2209)
Browse files Browse the repository at this point in the history
* Fix parameter bind

* lint

* add test case
  • Loading branch information
doichanj authored Aug 22, 2024
1 parent 32f3a47 commit 1b2cbbd
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 48 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
fixes:
- |
Runtime parameter binding did not populate default parameters
to all parameters in buffer, this caused issue when some of the
parameter was not bound in gates.
This fix copies default parameters in gates.
8 changes: 7 additions & 1 deletion src/controllers/controller_execute.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,13 @@ Result controller_execute(std::vector<std::shared_ptr<Circuit>> &input_circs,
R"(Invalid parameterized qobj: instruction param position out of range)");
}
// resize parameter array
op.params.resize(op.params.size() * num_params);
uint_t stride = op.params.size();
op.params.resize(stride * num_params);
// populate default params to allocated params
for (size_t j = 1; j < num_params; j++) {
for (size_t k = 0; k < stride; k++)
op.params[k + stride * j] = op.params[k];
}
op.has_bind_params = true;
}
uint_t stride = op.params.size() / num_params;
Expand Down
36 changes: 24 additions & 12 deletions src/noise/noise_model.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class NoiseModel {
const Method method = Method::circuit,
bool sample_at_runtime = false) const;

void set_config(const Circuit &circ, const Method method) const;
NoiseOps sample_noise_at_runtime(const Operations::Op &op,
RngEngine &rng) const;

Expand Down Expand Up @@ -148,7 +149,8 @@ class NoiseModel {

NoiseOps sample_noise_op(const Operations::Op &op, RngEngine &rng,
const Method method, const reg_t &mapping,
bool sample_at_runtime) const;
bool sample_at_runtime,
bool param_bind = false) const;

// Sample noise for the current operation
void sample_readout_noise(const Operations::Op &op, NoiseOps &noise_after,
Expand All @@ -167,7 +169,8 @@ class NoiseModel {
// Sample noise for the current operation
NoiseOps sample_noise_helper(const Operations::Op &op, RngEngine &rng,
const Method method, const reg_t &mapping,
bool sample_at_runtime) const;
bool sample_at_runtime,
bool param_bind = false) const;

// Add a local quantum error to the noise model for specific qubits
void add_local_quantum_error(const QuantumError &error,
Expand Down Expand Up @@ -283,6 +286,15 @@ Circuit NoiseModel::sample_noise(const Circuit &circ, RngEngine &rng,
return sample_noise_circuit(circ, rng, method, sample_at_runtime);
}

void NoiseModel::set_config(const Circuit &circ, const Method method) const {
// Qubit mapping
reg_t mapping;
if (circ.remapped_qubits)
mapping = reg_t(circ.qubits().cbegin(), circ.qubits().cend());
circ_method_ = method;
circ_mapping_ = mapping;
}

NoiseModel::NoiseOps
NoiseModel::sample_noise_at_runtime(const Operations::Op &op,
RngEngine &rng) const {
Expand Down Expand Up @@ -382,7 +394,8 @@ Circuit NoiseModel::sample_noise_circuit(const Circuit &circ, RngEngine &rng,
default:
if (noise_active) {
NoiseOps noisy_op =
sample_noise_op(op, rng, method, mapping, sample_at_runtime);
sample_noise_op(op, rng, method, mapping, sample_at_runtime,
(circ.num_bind_params > 0));
noisy_circ.ops.insert(noisy_circ.ops.end(), noisy_op.begin(),
noisy_op.end());
}
Expand All @@ -399,13 +412,12 @@ Circuit NoiseModel::sample_noise_circuit(const Circuit &circ, RngEngine &rng,
return noisy_circ;
}

NoiseModel::NoiseOps NoiseModel::sample_noise_op(const Operations::Op &op,
RngEngine &rng,
const Method method,
const reg_t &mapping,
bool sample_at_runtime) const {
auto noise_ops =
sample_noise_helper(op, rng, method, mapping, sample_at_runtime);
NoiseModel::NoiseOps
NoiseModel::sample_noise_op(const Operations::Op &op, RngEngine &rng,
const Method method, const reg_t &mapping,
bool sample_at_runtime, bool param_bind) const {
auto noise_ops = sample_noise_helper(op, rng, method, mapping,
sample_at_runtime, param_bind);

// If original op is conditional, make all the noise operations also
// conditional
Expand Down Expand Up @@ -551,7 +563,7 @@ void NoiseModel::add_nonlocal_quantum_error(
NoiseModel::NoiseOps
NoiseModel::sample_noise_helper(const Operations::Op &op, RngEngine &rng,
const Method method, const reg_t &mapping,
bool sample_at_runtime) const {
bool sample_at_runtime, bool param_bind) const {
// Return operator set
NoiseOps noise_before;
NoiseOps noise_after;
Expand Down Expand Up @@ -583,7 +595,7 @@ NoiseModel::sample_noise_helper(const Operations::Op &op, RngEngine &rng,
std::make_move_iterator(noise_after.end()));

if (op.type != Operations::OpType::measure && noise_ops.size() == 2 &&
noise_ops[0].qubits == noise_ops[1].qubits) {
noise_ops[0].qubits == noise_ops[1].qubits && !param_bind) {
// Try and fuse operations
// If either are superoperators combine superoperators
// else if either are unitaries combine unitaries
Expand Down
65 changes: 32 additions & 33 deletions src/simulators/circuit_executor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,8 @@ class Executor : public Base {
void run_circuit_with_parameter_binding(state_t &state, OpItr first,
OpItr last, ExperimentResult &result,
RngEngine &rng, const uint_t iparam,
bool final_op);
const Noise::NoiseModel *noise,
bool sample_noise, bool final_op);

template <typename InputIterator>
void measure_sampler(InputIterator first_meas, InputIterator last_meas,
Expand Down Expand Up @@ -802,9 +803,9 @@ void Executor<state_t>::run_circuit_with_sampling(Circuit &circ,
state.initialize_creg(circ.num_memory, circ.num_registers);

if (circ.num_bind_params > 1) {
run_circuit_with_parameter_binding(state, circ.ops.cbegin(),
circ.ops.cbegin() + first_meas,
result, rng, iparam, final_ops);
run_circuit_with_parameter_binding(
state, circ.ops.cbegin(), circ.ops.cbegin() + first_meas, result,
rng, iparam, nullptr, false, final_ops);
} else {
state.apply_ops(circ.ops.cbegin(), circ.ops.cbegin() + first_meas,
result, rng, final_ops);
Expand Down Expand Up @@ -846,7 +847,11 @@ void Executor<state_t>::run_circuit_shots(
shot_end[distributed_rank_] - shot_begin[distributed_rank_];

int max_matrix_qubits = 1;
if (!sample_noise) {
if (sample_noise) {
if (circ.num_bind_params > 1) {
noise.set_config(circ, Noise::NoiseModel::Method::circuit);
}
} else {
Noise::NoiseModel dummy_noise;
state_t dummy_state;
ExperimentResult fusion_result;
Expand Down Expand Up @@ -905,7 +910,7 @@ void Executor<state_t>::run_circuit_shots(
ExperimentResult &result = par_results[i][iparam];

Circuit circ_opt;
if (sample_noise) {
if (sample_noise && (circ.num_bind_params == 1)) {
Noise::NoiseModel dummy_noise;
circ_opt = noise.sample_noise(circ, rng);
fusion_pass.optimize_circuit(circ_opt, dummy_noise, state.opset(),
Expand All @@ -927,25 +932,20 @@ void Executor<state_t>::run_circuit_shots(
state.initialize_qreg(circ.num_qubits);
state.initialize_creg(circ.num_memory, circ.num_registers);

if (sample_noise) {
if (circ.num_bind_params > 1) {
run_circuit_with_parameter_binding(state, circ_opt.ops.cbegin(),
circ_opt.ops.cend(), result, rng,
iparam, true);
} else {
if (circ.num_bind_params > 1) {
run_circuit_with_parameter_binding(state, circ.ops.cbegin(),
circ.ops.cend(), result, rng, iparam,
&noise, sample_noise, true);
} else {
if (sample_noise) {
state.apply_ops(circ_opt.ops.cbegin(), circ_opt.ops.cend(), result,
rng, true);
}
} else {
if (circ.num_bind_params > 1) {
run_circuit_with_parameter_binding(state, circ.ops.cbegin(),
circ.ops.cend(), result, rng,
iparam, true);
} else {
state.apply_ops(circ.ops.cbegin(), circ.ops.cend(), result, rng,
true);
}
}

if (distributed_procs_ > 1) {
// save creg to be gathered
cregs[shot_index] = state.creg();
Expand Down Expand Up @@ -1002,29 +1002,28 @@ void Executor<state_t>::run_circuit_shots(
template <class state_t>
void Executor<state_t>::run_circuit_with_parameter_binding(
state_t &state, OpItr first, OpItr last, ExperimentResult &result,
RngEngine &rng, const uint_t iparam, bool final_op) {
OpItr op_begin = first;
RngEngine &rng, const uint_t iparam, const Noise::NoiseModel *noise,
bool sample_noise, bool final_op) {
OpItr op = first;

while (op != last) {
Operations::Op top;
std::vector<Operations::Op> ops;
// run with parameter bind
if (op->has_bind_params) {
if (op_begin != op) {
// run ops before this
state.apply_ops(op_begin, op, result, rng, false);
}

std::vector<Operations::Op> binded_op(1);
binded_op[0] = Operations::bind_parameter(*op, iparam, num_bind_params_);
state.apply_ops(binded_op.cbegin(), binded_op.cend(), result, rng,
final_op && (op == last - 1));
op_begin = op + 1;
top = Operations::bind_parameter(*op, iparam, num_bind_params_);
} else {
top = *op;
}
if (sample_noise) {
ops = noise->sample_noise_at_runtime(top, rng);
} else {
ops.push_back(top);
}
state.apply_ops(ops.cbegin(), ops.cend(), result, rng,
final_op && (op == last - 1));
op++;
}
if (op_begin != last) {
state.apply_ops(op_begin, last, result, rng, final_op);
}
}

template <class state_t>
Expand Down
5 changes: 3 additions & 2 deletions src/simulators/parallel_state_executor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -924,8 +924,9 @@ void ParallelStateExecutor<state_t>::apply_cache_blocking_ops(
// fecth chunk in cache
if (Base::states_[iChunk].qreg().fetch_chunk()) {
if (Base::num_bind_params_ > 1) {
Base::run_circuit_with_parameter_binding(
Base::states_[iChunk], first, last, result, rng, iparam, false);
Base::run_circuit_with_parameter_binding(Base::states_[iChunk], first,
last, result, rng, iparam,
nullptr, false, false);
} else {
Base::states_[iChunk].apply_ops(first, last, result, rng, false);
}
Expand Down
38 changes: 38 additions & 0 deletions test/terra/backends/test_runtime_parameterization.py
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,44 @@ def test_kraus_noise_with_shot_branching(self, method, device):

self.assertEqual(counts, counts_pre_bind)

@supported_methods(SUPPORTED_METHODS)
def test_bind_some_param(self, method, device):
"""Test parameterized circuit with gate with defaul values"""
shots = 1000
backend = self.backend(method=method, device=device)
circuit = QuantumCircuit(2)
theta = Parameter("theta")
theta_squared = theta * theta
circuit.h(0)
circuit.rx(theta, 0)
circuit.cx(0, 1)
circuit.rz(theta_squared, 1)
circuit.u(theta, theta_squared, 1.5708, 1)
circuit.measure_all()
parameter_binds = [{theta: [0, pi, 2 * pi]}]

result = backend.run(
circuit,
shots=shots,
parameter_binds=parameter_binds,
shot_branching_enable=False,
runtime_parameter_bind_enable=True,
).result()
self.assertSuccess(result)
counts = result.get_counts()

result_pre_bind = backend.run(
circuit,
shots=shots,
parameter_binds=parameter_binds,
shot_branching_enable=False,
runtime_parameter_bind_enable=False,
).result()
self.assertSuccess(result_pre_bind)
counts_pre_bind = result_pre_bind.get_counts()

self.assertEqual(counts, counts_pre_bind)


if __name__ == "__main__":
unittest.main()

0 comments on commit 1b2cbbd

Please sign in to comment.