-
Notifications
You must be signed in to change notification settings - Fork 368
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
Add support for parameterized qobj #485
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,7 +29,7 @@ namespace AER { | |
//============================================================================ | ||
|
||
class Qobj { | ||
public: | ||
public: | ||
//---------------------------------------------------------------- | ||
// Constructors | ||
//---------------------------------------------------------------- | ||
|
@@ -51,13 +51,12 @@ class Qobj { | |
json_t config; // (optional) qobj level config data | ||
}; | ||
|
||
|
||
//============================================================================ | ||
// JSON initialization and deserialization | ||
//============================================================================ | ||
|
||
// JSON deserialization | ||
inline void from_json(const json_t &js, Qobj &qobj) {qobj = Qobj(js);} | ||
inline void from_json(const json_t &js, Qobj &qobj) { qobj = Qobj(js); } | ||
|
||
Qobj::Qobj(const json_t &js) { | ||
// Check required fields | ||
|
@@ -83,23 +82,76 @@ Qobj::Qobj(const json_t &js) { | |
int_t seed = -1; | ||
uint_t seed_shift = 0; | ||
JSON::get_value(seed, "seed_simulator", config); | ||
|
||
// Parse experiments | ||
const json_t &circs = js["experiments"]; | ||
for (const auto &circ : circs) { | ||
Circuit circuit(circ, config); | ||
// Override random seed with fixed seed if set | ||
// We shift the seed for each successive experiment | ||
// So that results aren't correlated between experiments | ||
if (seed >= 0) { | ||
const size_t num_circs = circs.size(); | ||
|
||
// Check if parameterized qobj | ||
// It should be of the form | ||
// [exp0_params, exp1_params, ...] | ||
// where: | ||
// expk_params = [((i, j), pars), ....] | ||
// i is the instruction index in the experiment | ||
// j is the param index in the instruction | ||
// pars = [par0, par1, ...] is a list of different parameterizations | ||
using pos_t = std::pair<uint_t, uint_t>; | ||
using exp_params_t = std::vector<std::pair<pos_t, std::vector<double>>>; | ||
std::vector<exp_params_t> param_table; | ||
JSON::get_value(param_table, "parameterizations", config); | ||
|
||
// Validate parameterizations for number of circuis | ||
if (!param_table.empty() && param_table.size() != num_circs) { | ||
throw std::invalid_argument( | ||
R"(Invalid parameterized qobj: "parameterizations" length does not match number of circuits.)"); | ||
} | ||
|
||
// Load circuits | ||
for (size_t i=0; i<num_circs; i++) { | ||
// Get base circuit from qobj | ||
Circuit circuit(circs[i], config); | ||
if (param_table.empty() || param_table[i].empty()) { | ||
// Non parameterized circuit | ||
circuits.push_back(circuit); | ||
} else { | ||
// Load different parameterizations of the initial circuit | ||
const auto circ_params = param_table[i]; | ||
const size_t num_params = circ_params[0].second.size(); | ||
const size_t num_instr = circuit.ops.size(); | ||
for (size_t j=0; j<num_params; j++) { | ||
// Make a copy of the initial circuit | ||
Circuit param_circuit = circuit; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
for (const auto ¶ms : circ_params) { | ||
const auto instr_pos = params.first.first; | ||
const auto param_pos = params.first.second; | ||
// Validation | ||
if (instr_pos >= num_instr) { | ||
throw std::invalid_argument(R"(Invalid parameterized qobj: instruction position out of range)"); | ||
} | ||
auto &op = param_circuit.ops[instr_pos]; | ||
if (param_pos >= op.params.size()) { | ||
throw std::invalid_argument(R"(Invalid parameterized qobj: instruction param position out of range)"); | ||
} | ||
if (j >= params.second.size()) { | ||
throw std::invalid_argument(R"(Invalid parameterized qobj: parameterization value out of range)"); | ||
} | ||
// Update the param | ||
op.params[param_pos] = params.second[j]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. how does the typing work here? Do we only allow a single parameter type? Or am I missing some clever templating? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At the moment this only works for complex or double params (basically u1, u2, u3 gates). |
||
} | ||
circuits.push_back(param_circuit); | ||
} | ||
} | ||
} | ||
// Override random seed with fixed seed if set | ||
// We shift the seed for each successive experiment | ||
// So that results aren't correlated between experiments | ||
if (seed >= 0) { | ||
for (auto& circuit : circuits) { | ||
circuit.set_seed(seed + seed_shift); | ||
seed_shift += 2113; // Shift the seed | ||
seed_shift += 2113; // Shift the seed | ||
} | ||
circuits.push_back(circuit); | ||
} | ||
} | ||
|
||
//------------------------------------------------------------------------------ | ||
} // end namespace QISKIT | ||
} // namespace AER | ||
//------------------------------------------------------------------------------ | ||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
# This code is part of Qiskit. | ||
# | ||
# (C) Copyright IBM 2018, 2019. | ||
# | ||
# This code is licensed under the Apache License, Version 2.0. You may | ||
# obtain a copy of this license in the LICENSE.txt file in the root directory | ||
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. | ||
# | ||
# Any modifications or derivative works of this code must retain this | ||
# copyright notice, and modified files need to carry a notice indicating | ||
# that they have been altered from the originals. | ||
""" | ||
Integration Tests for Parameterized Qobj execution, testing qasm_simulator, | ||
statevector_simulator, and expectation value snapshots. | ||
""" | ||
|
||
import unittest | ||
import numpy as np | ||
|
||
from test.terra import common | ||
|
||
from qiskit import assemble | ||
from test.terra.reference.ref_snapshot_expval import ( | ||
snapshot_expval_circuits, snapshot_expval_counts, snapshot_expval_labels, | ||
snapshot_expval_pre_meas_values, snapshot_expval_circuit_parameterized, | ||
snapshot_expval_final_statevecs) | ||
from qiskit.providers.aer import QasmSimulator, StatevectorSimulator | ||
|
||
|
||
class TestParameterizedQobj(common.QiskitAerTestCase): | ||
"""Parameterized Qobj extension tests""" | ||
|
||
BACKEND_OPTS = { | ||
"seed_simulator": 2113 | ||
} | ||
|
||
@staticmethod | ||
def expval_snapshots(data, labels): | ||
"""Format snapshots as nested dicts""" | ||
# Check snapshot entry exists in data | ||
output = {} | ||
for label in labels: | ||
snaps = data.get("snapshots", {}).get("expectation_value", | ||
{}).get(label, []) | ||
# Convert list into dict | ||
inner = {} | ||
for snap_dict in snaps: | ||
inner[snap_dict['memory']] = snap_dict['value'] | ||
output[label] = inner | ||
return output | ||
|
||
@staticmethod | ||
def parameterized_qobj(backend, shots=1000, measure=True, snapshot=False): | ||
"""Return ParameterizedQobj for settings.""" | ||
single_shot = shots == 1 | ||
pcirc1, param1 = snapshot_expval_circuit_parameterized(single_shot=single_shot, | ||
measure=measure, | ||
snapshot=snapshot) | ||
circuits2to4 = snapshot_expval_circuits(pauli=True, | ||
skip_measure=(not measure), | ||
single_shot=single_shot) | ||
pcirc2, param2 = snapshot_expval_circuit_parameterized(single_shot=single_shot, | ||
measure=measure, | ||
snapshot=snapshot) | ||
circuits = [pcirc1] + circuits2to4 + [pcirc2] | ||
params = [param1, [], [], [], param2] | ||
qobj = assemble(circuits, | ||
backend=backend, | ||
shots=shots, | ||
parameterizations=params) | ||
return qobj | ||
|
||
def test_parameterized_qobj_qasm_snapshot_expval(self): | ||
"""Test parameterized qobj with Expectation Value snapshot and qasm simulator.""" | ||
shots = 1000 | ||
labels = snapshot_expval_labels() * 3 | ||
counts_targets = snapshot_expval_counts(shots) * 3 | ||
value_targets = snapshot_expval_pre_meas_values() * 3 | ||
|
||
backend = QasmSimulator() | ||
qobj = self.parameterized_qobj(backend=backend, | ||
shots=1000, | ||
measure=True, | ||
snapshot=True) | ||
self.assertIn('parameterizations', qobj.to_dict()['config']) | ||
job = backend.run(qobj, self.BACKEND_OPTS) | ||
result = job.result() | ||
success = getattr(result, 'success', False) | ||
num_circs = len(result.to_dict()['results']) | ||
self.assertTrue(success) | ||
self.compare_counts(result, | ||
range(num_circs), | ||
counts_targets, | ||
delta=0.1 * shots) | ||
# Check snapshots | ||
for j in range(num_circs): | ||
data = result.data(j) | ||
all_snapshots = self.expval_snapshots(data, labels) | ||
for label in labels: | ||
snaps = all_snapshots.get(label, {}) | ||
self.assertTrue(len(snaps), 1) | ||
for memory, value in snaps.items(): | ||
target = value_targets[j].get(label, | ||
{}).get(memory, {}) | ||
self.assertAlmostEqual(value, target, delta=1e-7) | ||
|
||
def test_parameterized_qobj_statevector(self): | ||
"""Test parameterized qobj with Expectation Value snapshot and qasm simulator.""" | ||
statevec_targets = snapshot_expval_final_statevecs() * 3 | ||
|
||
backend = StatevectorSimulator() | ||
qobj = self.parameterized_qobj(backend=backend, | ||
measure=False, | ||
snapshot=False) | ||
self.assertIn('parameterizations', qobj.to_dict()['config']) | ||
job = backend.run(qobj, self.BACKEND_OPTS) | ||
result = job.result() | ||
success = getattr(result, 'success', False) | ||
num_circs = len(result.to_dict()['results']) | ||
self.assertTrue(success) | ||
|
||
for j in range(num_circs): | ||
statevector = result.get_statevector(j) | ||
np.testing.assert_array_almost_equal(statevector, statevec_targets[j].data, decimal=7) | ||
|
||
if __name__ == '__main__': | ||
unittest.main() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
circuits.reserve(num_circs)