From e494d2c898615f2832171a8e8891e17d96204d1d Mon Sep 17 00:00:00 2001 From: Christopher Wood Date: Fri, 6 Dec 2019 17:51:22 -0500 Subject: [PATCH 1/2] Add support for parameterized qobj --- src/framework/qobj.hpp | 80 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 66 insertions(+), 14 deletions(-) diff --git a/src/framework/qobj.hpp b/src/framework/qobj.hpp index 2a48ee8fae..b35981039d 100755 --- a/src/framework/qobj.hpp +++ b/src/framework/qobj.hpp @@ -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; + using exp_params_t = std::vector>>; + std::vector 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_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]; + } + 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 \ No newline at end of file From 786d2553cccdbd0dd9c39e4fb2c423355b283316 Mon Sep 17 00:00:00 2001 From: Donny Greenberg Date: Tue, 7 Apr 2020 17:17:21 -0400 Subject: [PATCH 2/2] Add Parameterized Qobj tests --- .../terra/backends/test_parameterized_qobj.py | 127 ++++++++++++++++++ test/terra/reference/ref_snapshot_expval.py | 113 +++++++++++++++- 2 files changed, 235 insertions(+), 5 deletions(-) create mode 100644 test/terra/backends/test_parameterized_qobj.py diff --git a/test/terra/backends/test_parameterized_qobj.py b/test/terra/backends/test_parameterized_qobj.py new file mode 100644 index 0000000000..f56d9ebc0f --- /dev/null +++ b/test/terra/backends/test_parameterized_qobj.py @@ -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() diff --git a/test/terra/reference/ref_snapshot_expval.py b/test/terra/reference/ref_snapshot_expval.py index abe4ddb1c1..7e6200a070 100644 --- a/test/terra/reference/ref_snapshot_expval.py +++ b/test/terra/reference/ref_snapshot_expval.py @@ -69,7 +69,8 @@ def snapshot_expval_params(pauli=False): def snapshot_expval_circuits(pauli=False, single_shot=False, variance=False, - post_measure=False): + post_measure=False, + skip_measure=False): """SnapshotExpectationValue test circuits with deterministic counts""" circuits = [] @@ -91,7 +92,8 @@ def snapshot_expval_circuits(pauli=False, single_shot=single_shot, variance=variance) circuit.barrier(qr) - circuit.measure(qr, cr) + if not skip_measure: + circuit.measure(qr, cr) circuit.barrier(qr) if post_measure: for label, (params, @@ -116,7 +118,8 @@ def snapshot_expval_circuits(pauli=False, single_shot=single_shot, variance=variance) circuit.barrier(qr) - circuit.measure(qr, cr) + if not skip_measure: + circuit.measure(qr, cr) circuit.barrier(qr) if post_measure: for label, (params, @@ -131,7 +134,7 @@ def snapshot_expval_circuits(pauli=False, # State |10> -i|01> circuit = QuantumCircuit(*regs) circuit.h(0) - circuit.s(0) + circuit.sdg(0) circuit.cx(0, 1) circuit.x(1) if not post_measure: @@ -143,7 +146,8 @@ def snapshot_expval_circuits(pauli=False, single_shot=single_shot, variance=variance) circuit.barrier(qr) - circuit.measure(qr, cr) + if not skip_measure: + circuit.measure(qr, cr) circuit.barrier(qr) if post_measure: for label, (params, @@ -218,3 +222,102 @@ def snapshot_expval_post_meas_values(): values[label] = inner_dict targets.append(values) return targets + +def snapshot_expval_circuit_parameterized(single_shot=False, + measure=True, + snapshot=False): + """SnapshotExpectationValue test circuits, rewritten as a single parameterized circuit and + parameterizations array. """ + + num_qubits = 2 + qr = QuantumRegister(num_qubits) + cr = ClassicalRegister(num_qubits) + regs = (qr, cr) + + circuit = QuantumCircuit(*regs) + circuit.u3(0, 0, 0, qubit=0) + circuit.u1(0, qubit=0) + circuit.u3(0, 0, 0, qubit=1) + circuit.cu3(0, 0, 0, control_qubit=0, target_qubit=1) + circuit.u3(0, 0, 0, qubit=1) + circuit.id(qubit=0) + if snapshot: + for label, (params, qubits) in snapshot_expval_params(pauli=True).items(): + circuit.snapshot_expectation_value(label, + params, + qubits, + single_shot=single_shot) + if measure: + circuit.barrier(qr) + circuit.measure(qr, cr) + circuit.barrier(qr) + + # Parameterizations + + # State |+1> + plus_one_params = { + # X on 0 + (0, 0): np.pi, + (0, 1): 0, + (0, 2): np.pi, + # No rZ + (1, 0): 0, + # H on 1 + (2, 0): np.pi / 2, + (2, 2): np.pi, + # No CrX + (3, 0): 0, + (3, 1): 0, + (3, 2): 0, + # No X + (4, 0): 0, + (4, 1): 0, + (4, 2): 0, + } + # State |00> + |11> + bell_params = { + # H 0 + (0, 0): np.pi / 2, + (0, 1): 0, + (0, 2): np.pi, + # No rZ + (1, 0): 0, + # No H + (2, 0): 0, + (2, 2): 0, + # CX from 0 on 1 + (3, 0): np.pi, + (3, 1): 0, + (3, 2): np.pi, + # No X + (4, 0): 0, + (4, 1): 0, + (4, 2): 0, + } + # State |10> -i|01> + iminus_bell_params = { + # H 0 + (0, 0): np.pi / 2, + (0, 1): 0, + (0, 2): np.pi, + # S 0 + (1, 0): - np.pi / 2, + # No H + (2, 0): 0, + (2, 2): 0, + # CX from 0 on 1 + (3, 0): np.pi, + (3, 1): 0, + (3, 2): np.pi, + # X 1 + (4, 0): np.pi, + (4, 1): 0, + (4, 2): np.pi, + } + param_mat = np.transpose([list(plus_one_params.values()), + list(bell_params.values()), + list(iminus_bell_params.values())]).tolist() + parameterizations = [[list(index), params] + for (index, params) in zip(plus_one_params.keys(), param_mat)] + + return circuit, parameterizations