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

Make controller_wrapper functions functors #945

Merged
merged 10 commits into from Sep 24, 2020
8 changes: 4 additions & 4 deletions qiskit/providers/aer/backends/qasm_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,10 +256,10 @@ class QasmSimulator(AerBackend):
}

def __init__(self, configuration=None, provider=None):
super().__init__(qasm_controller_execute,
QasmBackendConfiguration.from_dict(
self.DEFAULT_CONFIGURATION),
provider=provider)
super().__init__(
qasm_controller_execute(),
QasmBackendConfiguration.from_dict(self.DEFAULT_CONFIGURATION),
provider=provider)

def _validate(self, qobj, backend_options, noise_model):
"""Semantic validations of the qobj which cannot be done via schemas.
Expand Down
2 changes: 1 addition & 1 deletion qiskit/providers/aer/backends/statevector_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ class StatevectorSimulator(AerBackend):
}

def __init__(self, configuration=None, provider=None):
super().__init__(statevector_controller_execute,
super().__init__(statevector_controller_execute(),
QasmBackendConfiguration.from_dict(self.DEFAULT_CONFIGURATION),
provider=provider)

Expand Down
5 changes: 2 additions & 3 deletions qiskit/providers/aer/backends/unitary_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,8 @@ class UnitarySimulator(AerBackend):
}

def __init__(self, configuration=None, provider=None):
super().__init__(unitary_controller_execute,
QasmBackendConfiguration.from_dict(
self.DEFAULT_CONFIGURATION),
super().__init__(unitary_controller_execute(),
QasmBackendConfiguration.from_dict(self.DEFAULT_CONFIGURATION),
provider=provider)

def _validate(self, qobj, backend_options, noise_model):
Expand Down
41 changes: 27 additions & 14 deletions qiskit/providers/aer/backends/wrappers/bindings.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,34 @@ DISABLE_WARNING_POP
#include "controllers/unitary_controller.hpp"
#include "controllers/controller_execute.hpp"

PYBIND11_MODULE(controller_wrappers, m) {

m.def("qasm_controller_execute_json", &AER::controller_execute_json<AER::Simulator::QasmController>, "instance of controller_execute for QasmController");
vvilpas marked this conversation as resolved.
Show resolved Hide resolved
m.def("qasm_controller_execute", [](const py::object &qobj) -> py::object {
return AerToPy::from_result(AER::controller_execute<AER::Simulator::QasmController>(qobj));
}, pybind11::return_value_policy::take_ownership);
template<typename T>
class ControllerExecutor {
public:
ControllerExecutor() = default;
py::object operator()(const py::object &qobj) { return AerToPy::from_result(AER::controller_execute<T>(qobj)); }
};

m.def("statevector_controller_execute_json", &AER::controller_execute_json<AER::Simulator::StatevectorController>, "instance of controller_execute for StatevectorController");
m.def("statevector_controller_execute", [](const py::object &qobj) -> py::object {
return AerToPy::from_result(AER::controller_execute<AER::Simulator::StatevectorController>(qobj));
}, pybind11::return_value_policy::take_ownership);
PYBIND11_MODULE(controller_wrappers, m) {

m.def("unitary_controller_execute_json", &AER::controller_execute_json<AER::Simulator::UnitaryController>, "instance of controller_execute for UnitaryController");
m.def("unitary_controller_execute", [](const py::object &qobj) -> py::object {
return AerToPy::from_result(AER::controller_execute<AER::Simulator::UnitaryController>(qobj));
}, pybind11::return_value_policy::take_ownership);
py::class_<ControllerExecutor<AER::Simulator::QasmController> > qasm_ctrl (m, "qasm_controller_execute");
qasm_ctrl.def(py::init<>());
qasm_ctrl.def("__call__", &ControllerExecutor<AER::Simulator::QasmController>::operator());
qasm_ctrl.def("__reduce__", [qasm_ctrl](const ControllerExecutor<AER::Simulator::QasmController> &self) {
return py::make_tuple(qasm_ctrl, py::tuple());
});

py::class_<ControllerExecutor<AER::Simulator::StatevectorController> > statevec_ctrl (m, "statevector_controller_execute");
statevec_ctrl.def(py::init<>());
statevec_ctrl.def("__call__", &ControllerExecutor<AER::Simulator::StatevectorController>::operator());
statevec_ctrl.def("__reduce__", [statevec_ctrl](const ControllerExecutor<AER::Simulator::StatevectorController> &self) {
return py::make_tuple(statevec_ctrl, py::tuple());
});

py::class_<ControllerExecutor<AER::Simulator::UnitaryController> > unitary_ctrl (m, "unitary_controller_execute");
unitary_ctrl.def(py::init<>());
unitary_ctrl.def("__call__", &ControllerExecutor<AER::Simulator::UnitaryController>::operator());
unitary_ctrl.def("__reduce__", [unitary_ctrl](const ControllerExecutor<AER::Simulator::UnitaryController> &self) {
return py::make_tuple(unitary_ctrl, py::tuple());
});

}
6 changes: 6 additions & 0 deletions releasenotes/notes/pybind-functors-a0b7b357fbe67dfb.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
fixes:
- |
The controller_execute wrappers have been adjusted to be functors (objects)
rather than free functions. Among other things, this allows them to be used
in multiprocessing.pool.map calls.
19 changes: 1 addition & 18 deletions src/controllers/controller_execute.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,24 +24,7 @@
// Controller Execute interface
//=========================================================================

// This is used to make wrapping Controller classes in Cython easier
// by handling the parsing of std::string input into JSON objects.
namespace AER {
template <class controller_t>
std::string controller_execute_json(const std::string &qobj_str) {
controller_t controller;
auto qobj_js = json_t::parse(qobj_str);

// Fix for MacOS and OpenMP library double initialization crash.
// Issue: https://github.com/Qiskit/qiskit-aer/issues/1
if (JSON::check_key("config", qobj_js)) {
std::string path;
JSON::get_value(path, "library_dir", qobj_js["config"]);
Hacks::maybe_load_openmp(path);
}

return controller.execute(qobj_js).to_json().dump(-1);
}
namespace AER {

template <class controller_t>
Result controller_execute(const json_t &qobj_js) {
Expand Down
86 changes: 86 additions & 0 deletions test/terra/extensions/test_wrappers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# 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.


import unittest
import copy
import pickle
import os
from multiprocessing import Pool

from qiskit import assemble, transpile
from qiskit.providers.aer.backends import QasmSimulator, StatevectorSimulator, UnitarySimulator
from qiskit.providers.aer.backends.controller_wrappers import (qasm_controller_execute,
statevector_controller_execute,
unitary_controller_execute)
from test.terra.reference import ref_algorithms, ref_measure, ref_1q_clifford


class TestControllerExecuteWrappers(unittest.TestCase):
"""Basic functionality tests for pybind-generated wrappers"""

CFUNCS = [qasm_controller_execute(), statevector_controller_execute(), unitary_controller_execute()]

def test_deepcopy(self):
"""Test that the functors are deepcopy-able."""
for cfunc in self.CFUNCS:
cahpy = copy.deepcopy(cfunc)

def test_pickleable(self):
"""Test that the functors are pickle-able (directly)."""
for cfunc in self.CFUNCS:
bites = pickle.dumps(cfunc)
cahpy = pickle.loads(bites)

def _create_qobj(self, circs, backend, backend_options=None, noise_model=None):
circs = transpile(circs, backend)
qobj = assemble(circs, backend)
fqobj = backend._format_qobj(qobj, backend_options=backend_options, noise_model=noise_model)
return fqobj

def _map_and_test(self, cfunc, qobj):
n = max(os.cpu_count(), 2)
with Pool(processes=n) as p:
rs = p.map(cfunc, [copy.deepcopy(qobj) for _ in range(n)])

self.assertEqual(len(rs), n)
for r in rs:
self.assertTrue(r['success'])

def test_mappable_qasm(self):
"""Test that the qasm controller can be mapped."""
cfunc = qasm_controller_execute()
sim = QasmSimulator()
circs = ref_algorithms.teleport_circuit()
fqobj = self._create_qobj(circs, sim)
self._map_and_test(cfunc, fqobj)

def test_mappable_statevector(self):
"""Test that the statevector controller can be mapped."""
cfunc = statevector_controller_execute()
sim = StatevectorSimulator()
circs = ref_measure.measure_circuits_deterministic()
fqobj = self._create_qobj(circs, sim)
self._map_and_test(cfunc, fqobj)

def test_mappable_unitary(self):
"""Test that the unitary controller can be mapped."""
cfunc = unitary_controller_execute()
sim = UnitarySimulator()
circs = ref_1q_clifford.h_gate_circuits_deterministic(
final_measure=False)
fqobj = self._create_qobj(circs, sim)
self._map_and_test(cfunc, fqobj)


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