From b46040fcf631d56175d5edd8b185648220afaa9e Mon Sep 17 00:00:00 2001 From: Ben Howe Date: Sat, 9 Sep 2023 13:26:33 +0000 Subject: [PATCH] Convert noise_model usage to shared_ptr to fix new test --- python/runtime/common/py_NoiseModel.cpp | 2 +- .../runtime/cudaq/algorithms/py_observe.cpp | 34 +++++++------ python/runtime/cudaq/algorithms/py_observe.h | 7 +-- python/runtime/cudaq/algorithms/py_sample.cpp | 28 ++++++----- runtime/common/ExecutionContext.h | 2 +- runtime/cudaq.h | 2 +- runtime/cudaq/cudaq.cpp | 4 +- .../platform/default/rest/RemoteRESTQPU.cpp | 2 +- runtime/cudaq/platform/qpu.h | 6 ++- runtime/cudaq/platform/quantum_platform.cpp | 2 +- runtime/cudaq/platform/quantum_platform.h | 2 +- unittests/integration/noise_tester.cpp | 50 +++++++++---------- 12 files changed, 74 insertions(+), 67 deletions(-) diff --git a/python/runtime/common/py_NoiseModel.cpp b/python/runtime/common/py_NoiseModel.cpp index b694090c169..05d64d6ead5 100644 --- a/python/runtime/common/py_NoiseModel.cpp +++ b/python/runtime/common/py_NoiseModel.cpp @@ -88,7 +88,7 @@ void bindNoiseModel(py::module &mod) { .def("append", &kraus_channel::push_back, "Add a KrausOperator to this KrausChannel."); - py::class_( + py::class_>( mod, "NoiseModel", "The cudaq NoiseModel defines a set of KrausChannels applied to " "specific qubits after the invocation specified quantum operations.") diff --git a/python/runtime/cudaq/algorithms/py_observe.cpp b/python/runtime/cudaq/algorithms/py_observe.cpp index 11b58144bc5..339dde9d1ff 100644 --- a/python/runtime/cudaq/algorithms/py_observe.cpp +++ b/python/runtime/cudaq/algorithms/py_observe.cpp @@ -37,12 +37,12 @@ std::unordered_map> /// @brief Run `cudaq::observe` on the provided kernel and spin operator. observe_result pyObserve(kernel_builder<> &kernel, spin_op &spin_operator, py::args args, int shots, - std::optional noise) { + std::optional> noise) { // Ensure the user input is correct. auto validatedArgs = validateInputArguments(kernel, args); auto &platform = cudaq::get_platform(); if (noise) - platform.set_noise(&*noise); + platform.set_noise(*noise); // TODO: would like to handle errors in the case that // `kernel.num_qubits() >= spin_operator.num_qubits()` @@ -98,10 +98,11 @@ async_observe_result pyObserveAsync(kernel_builder<> &kernel, } /// @brief Run `cudaq::observe` on the provided kernel and spin operator. -observe_result pyObservePar(const PyParType &type, kernel_builder<> &kernel, - spin_op &spin_operator, py::args args = {}, - int shots = defaultShotsValue, - std::optional noise = std::nullopt) { +observe_result +pyObservePar(const PyParType &type, kernel_builder<> &kernel, + spin_op &spin_operator, py::args args = {}, + int shots = defaultShotsValue, + std::optional> noise = std::nullopt) { // Ensure the user input is correct. auto validatedArgs = validateInputArguments(kernel, args); auto &platform = cudaq::get_platform(); @@ -165,13 +166,13 @@ observe_result pyObservePar(const PyParType &type, kernel_builder<> &kernel, std::vector pyObserveN(kernel_builder<> &kernel, spin_op &op, py::args args = {}, std::size_t shots = defaultShotsValue, - std::optional noise = std::nullopt) { + std::optional> noise = std::nullopt) { auto argSet = createArgumentSet(args); auto N = argSet.size(); auto &platform = cudaq::get_platform(); if (noise) - platform.set_noise(&*noise); + platform.set_noise(*noise); kernel.jitCode(); auto name = kernel.name(); std::vector results; @@ -216,7 +217,8 @@ void bindObserve(py::module &mod) { "observe", [&](kernel_builder<> &kernel, std::variant> &spin_operator, - py::args arguments, int shots, std::optional noise, + py::args arguments, int shots, + std::optional> noise, std::optional execution) -> std::variant> { // Observe can be a single observe call, a parallel observe call, @@ -224,34 +226,34 @@ void bindObserve(py::module &mod) { using ObserveApplicator = std::function( kernel_builder<> &, spin_op &, py::args &, int, - std::optional)>; + std::optional>)>; std::unordered_map applicator{ {"default", [](kernel_builder<> &kernel, spin_op &spin_operator, py::args arguments, int shots, - std::optional noise) { + std::optional> noise) { return std::vector{ pyObserve(kernel, spin_operator, arguments, shots, noise)}; }}, {"broadcast", [](kernel_builder<> &kernel, spin_op &spin_operator, py::args arguments, int shots, - std::optional noise) { + std::optional> noise) { return pyObserveN(kernel, spin_operator, arguments, shots, noise); }}, {"thread", [](kernel_builder<> &kernel, spin_op &spin_operator, py::args arguments, int shots, - std::optional noise) { + std::optional> noise) { return std::vector{ pyObservePar(PyParType::thread, kernel, spin_operator, arguments, shots, noise)}; }}, {"mpi", [](kernel_builder<> &kernel, spin_op &spin_operator, py::args arguments, int shots, - std::optional noise) { + std::optional> noise) { return std::vector{ pyObservePar(PyParType::mpi, kernel, spin_operator, arguments, shots, noise)}; @@ -341,7 +343,7 @@ all argument sets and a list of `observe_result` instances will be returned. "observe_async", [&](kernel_builder<> &kernel, spin_op &spin_operator, py::args arguments, std::size_t qpu_id, int shots, - std::optional noise_model) { + std::optional> noise_model) { if (!noise_model) return pyObserveAsync(kernel, spin_operator, arguments, qpu_id, shots); @@ -384,7 +386,7 @@ can be retrieved via `future.get()`. mod.def( "observe_n", [](kernel_builder<> &self, spin_op &spin_operator, py::args args, - int shots, std::optional noise_model) { + int shots, std::optional> noise_model) { PyErr_WarnEx(PyExc_DeprecationWarning, "observe_n() is deprecated, use observe() with the same " "argument-list structure.", diff --git a/python/runtime/cudaq/algorithms/py_observe.h b/python/runtime/cudaq/algorithms/py_observe.h index 406ff168f8e..20f0b0213df 100644 --- a/python/runtime/cudaq/algorithms/py_observe.h +++ b/python/runtime/cudaq/algorithms/py_observe.h @@ -24,9 +24,10 @@ inline constexpr int defaultShotsValue = -1; /// @brief Functions for running `cudaq::observe()` from python. /// Exposing pyObserve in the header for use elsewhere in the bindings. -observe_result pyObserve(kernel_builder<> &kernel, spin_op &spin_operator, - py::args args = {}, int shots = defaultShotsValue, - std::optional noise = std::nullopt); +observe_result +pyObserve(kernel_builder<> &kernel, spin_op &spin_operator, py::args args = {}, + int shots = defaultShotsValue, + std::optional> noise = std::nullopt); /// @brief Expose binding of `cudaq::observe()` and `cudaq::observe_async` to /// python. diff --git a/python/runtime/cudaq/algorithms/py_sample.cpp b/python/runtime/cudaq/algorithms/py_sample.cpp index 38ac41c45d5..7a43dbc3f7b 100644 --- a/python/runtime/cudaq/algorithms/py_sample.cpp +++ b/python/runtime/cudaq/algorithms/py_sample.cpp @@ -24,9 +24,10 @@ namespace cudaq { /// @brief Sample the state produced by the provided builder. -sample_result pySample(kernel_builder<> &builder, py::args args = {}, - std::size_t shots = 1000, - std::optional noise = std::nullopt) { +sample_result +pySample(kernel_builder<> &builder, py::args args = {}, + std::size_t shots = 1000, + std::optional> noise = std::nullopt) { // Ensure the user input is correct. auto validatedArgs = validateInputArguments(builder, args); @@ -40,7 +41,7 @@ sample_result pySample(kernel_builder<> &builder, py::args args = {}, auto &platform = cudaq::get_platform(); if (noise) - platform.set_noise(&*noise); + platform.set_noise(*noise); auto result = details::runSampling( [&]() mutable { builder.jitAndInvoke(argData.data()); }, @@ -54,14 +55,14 @@ sample_result pySample(kernel_builder<> &builder, py::args args = {}, std::vector pySampleN(kernel_builder<> &kernel, py::args args = {}, std::size_t shots = 1000, - std::optional noise = std::nullopt) { + std::optional> noise = std::nullopt) { auto argSet = createArgumentSet(args); auto N = argSet.size(); kernel.jitCode(); auto name = kernel.name(); auto &platform = cudaq::get_platform(); if (noise) - platform.set_noise(&*noise); + platform.set_noise(*noise); std::vector results; for (std::size_t currentIter = 0; auto &a : argSet) { @@ -129,16 +130,16 @@ for more information on this programming pattern.)#") mod.def( "sample", [&](kernel_builder<> &builder, py::args arguments, std::size_t shots, - std::optional noise) + std::optional> noise) -> std::variant> { // Our arguments can be a single set of arguments for the kernel // in that case the applicator should just be delegating to pySample - std::function(kernel_builder<> &, py::args, - std::size_t, - std::optional)> + std::function( + kernel_builder<> &, py::args, std::size_t, + std::optional>)> applicator = [](kernel_builder<> &builder, py::args arguments, std::size_t shots, - std::optional noise) { + std::optional> noise) { return std::vector{ pySample(builder, arguments, shots, noise)}; }; @@ -147,7 +148,8 @@ for more information on this programming pattern.)#") // we'll delegate to sample_n. if (isBroadcastRequest(builder, arguments)) applicator = [](kernel_builder<> &builder, py::args arguments, - std::size_t shots, std::optional noise) { + std::size_t shots, + std::optional> noise) { return pySampleN(builder, arguments, shots, noise); }; @@ -222,7 +224,7 @@ future whose results can be retrieved via `future.get()`. mod.def( "sample_n", [](kernel_builder<> &self, py::args args, std::size_t shots, - std::optional noise) { + std::optional> noise) { PyErr_WarnEx(PyExc_DeprecationWarning, "sample_n() is deprecated, use sample() with the same " "argument-list structure.", diff --git a/runtime/common/ExecutionContext.h b/runtime/common/ExecutionContext.h index 527513ce87e..f933d3c2503 100644 --- a/runtime/common/ExecutionContext.h +++ b/runtime/common/ExecutionContext.h @@ -48,7 +48,7 @@ class ExecutionContext { /// @brief Noise model to apply to the /// current execution. - noise_model *noiseModel = nullptr; + std::shared_ptr noiseModel = nullptr; /// @brief Flag to indicate if backend can /// handle spin_op observe task under this ExecutionContext. diff --git a/runtime/cudaq.h b/runtime/cudaq.h index e1566d8f02d..56da0eae854 100644 --- a/runtime/cudaq.h +++ b/runtime/cudaq.h @@ -183,7 +183,7 @@ void set_target_backend(const char *backend); void set_shots(const std::size_t nShots); /// @brief Set a custom noise model for simulation -void set_noise(cudaq::noise_model &model); +void set_noise(std::shared_ptr model); /// @brief Remove an existing noise model from simulation. void unset_noise(); diff --git a/runtime/cudaq/cudaq.cpp b/runtime/cudaq/cudaq.cpp index 4f8131c6e11..314c3464435 100644 --- a/runtime/cudaq/cudaq.cpp +++ b/runtime/cudaq/cudaq.cpp @@ -270,9 +270,9 @@ void clear_shots(const std::size_t nShots) { platform.clear_shots(); } -void set_noise(cudaq::noise_model &model) { +void set_noise(std::shared_ptr model) { auto &platform = cudaq::get_platform(); - platform.set_noise(&model); + platform.set_noise(model); } void unset_noise() { diff --git a/runtime/cudaq/platform/default/rest/RemoteRESTQPU.cpp b/runtime/cudaq/platform/default/rest/RemoteRESTQPU.cpp index c1f0f9673a0..208404af380 100644 --- a/runtime/cudaq/platform/default/rest/RemoteRESTQPU.cpp +++ b/runtime/cudaq/platform/default/rest/RemoteRESTQPU.cpp @@ -174,7 +174,7 @@ class RemoteRESTQPU : public cudaq::QPU { /// @brief Set the noise model, only allow this for /// emulation. - void setNoiseModel(cudaq::noise_model *model) override { + void setNoiseModel(std::shared_ptr model) override { if (!emulate && model) throw std::runtime_error( "Noise modeling is not allowed on remote physical quantum backends."); diff --git a/runtime/cudaq/platform/qpu.h b/runtime/cudaq/platform/qpu.h index 700f5bfda72..32b0d69c1b0 100644 --- a/runtime/cudaq/platform/qpu.h +++ b/runtime/cudaq/platform/qpu.h @@ -44,7 +44,7 @@ class QPU : public registry::RegisteredType { ExecutionContext *executionContext = nullptr; /// @brief Noise model specified for QPU execution. - noise_model *noiseModel = nullptr; + std::shared_ptr noiseModel = nullptr; /// @brief Check if the current execution context is a `spin_op` /// observation and perform state-preparation circuit measurement @@ -100,7 +100,9 @@ class QPU : public registry::RegisteredType { /// The destructor virtual ~QPU() = default; - virtual void setNoiseModel(noise_model *model) { noiseModel = model; } + virtual void setNoiseModel(std::shared_ptr model) { + noiseModel = model; + } /// Return the number of qubits std::size_t getNumQubits() { return numQubits; } diff --git a/runtime/cudaq/platform/quantum_platform.cpp b/runtime/cudaq/platform/quantum_platform.cpp index bbecb68e338..e04b8176a98 100644 --- a/runtime/cudaq/platform/quantum_platform.cpp +++ b/runtime/cudaq/platform/quantum_platform.cpp @@ -46,7 +46,7 @@ quantum_platform *getQuantumPlatformInternal() { return platform; } -void quantum_platform::set_noise(noise_model *model) { +void quantum_platform::set_noise(std::shared_ptr model) { auto &platformQPU = platformQPUs[platformCurrentQPU]; platformQPU->setNoiseModel(model); } diff --git a/runtime/cudaq/platform/quantum_platform.h b/runtime/cudaq/platform/quantum_platform.h index 27a5edf323f..510b43f8fbb 100644 --- a/runtime/cudaq/platform/quantum_platform.h +++ b/runtime/cudaq/platform/quantum_platform.h @@ -106,7 +106,7 @@ class quantum_platform { /// @brief Set the noise model for future invocations of /// quantum kernels. - void set_noise(noise_model *model); + void set_noise(std::shared_ptr model); /// @brief Turn off any noise models. void reset_noise(); diff --git a/unittests/integration/noise_tester.cpp b/unittests/integration/noise_tester.cpp index 4a64e35541b..4d47db6bca9 100644 --- a/unittests/integration/noise_tester.cpp +++ b/unittests/integration/noise_tester.cpp @@ -46,8 +46,8 @@ CUDAQ_TEST(NoiseTest, checkSimple) { {0.0, 0.0}, {0.0, 0.0}, {-0.05773502691896258, 0.0}}); - cudaq::noise_model noise; - noise.add_channel({0}, depol); + auto noise = std::make_shared(); + noise->add_channel({0}, depol); cudaq::set_noise(noise); auto counts = cudaq::sample(xOp{}); @@ -69,8 +69,8 @@ CUDAQ_TEST(NoiseTest, checkSimple) { CUDAQ_TEST(NoiseTest, checkAmplitudeDamping) { cudaq::kraus_channel amplitudeDamping{{1., 0., 0., .8660254037844386}, {0., 0.0, 0.5, 0.}}; - cudaq::noise_model noise; - noise.add_channel({0}, amplitudeDamping); + auto noise = std::make_shared(); + noise->add_channel({0}, amplitudeDamping); cudaq::set_noise(noise); auto counts = cudaq::sample(xOp{}); @@ -150,8 +150,8 @@ CUDAQ_TEST(NoiseTest, checkCNOT) { {-0.05773502691896258, 0.0}}; cudaq::kraus_channel cnotNoise( std::vector{op0, op1, op2, op3}); - cudaq::noise_model noise; - noise.add_channel({0, 1}, cnotNoise); + auto noise = std::make_shared(); + noise->add_channel({0, 1}, cnotNoise); cudaq::set_noise(noise); auto counts = cudaq::sample(10000, bell{}); counts.dump(); @@ -162,9 +162,9 @@ CUDAQ_TEST(NoiseTest, checkCNOT) { CUDAQ_TEST(NoiseTest, checkExceptions) { cudaq::kraus_channel amplitudeDamping{{1., 0., 0., .8660254037844386}, {0., 0.0, 0.5, 0.}}; - cudaq::noise_model noise; + auto noise = std::make_shared(); EXPECT_ANY_THROW({ - noise.add_channel({0, 1}, amplitudeDamping); + noise->add_channel({0, 1}, amplitudeDamping); }); cudaq::unset_noise(); // clear for subsequent tests } @@ -172,8 +172,8 @@ CUDAQ_TEST(NoiseTest, checkExceptions) { CUDAQ_TEST(NoiseTest, checkDepolType) { cudaq::set_random_seed(13); cudaq::depolarization_channel depol(.1); - cudaq::noise_model noise; - noise.add_channel({0}, depol); + auto noise = std::make_shared(); + noise->add_channel({0}, depol); cudaq::set_noise(noise); auto counts = cudaq::sample(xOp{}); counts.dump(); @@ -184,8 +184,8 @@ CUDAQ_TEST(NoiseTest, checkDepolType) { CUDAQ_TEST(NoiseTest, checkDepolTypeSimple) { cudaq::set_random_seed(13); cudaq::depolarization_channel depol(1.); - cudaq::noise_model noise; - noise.add_channel({0}, depol); + auto noise = std::make_shared(); + noise->add_channel({0}, depol); cudaq::set_noise(noise); auto counts = cudaq::sample(xOp{}); counts.dump(); @@ -197,8 +197,8 @@ CUDAQ_TEST(NoiseTest, checkDepolTypeSimple) { CUDAQ_TEST(NoiseTest, checkAmpDampType) { cudaq::amplitude_damping_channel ad(.25); - cudaq::noise_model noise; - noise.add_channel({0}, ad); + auto noise = std::make_shared(); + noise->add_channel({0}, ad); cudaq::set_noise(noise); auto counts = cudaq::sample(xOp{}); counts.dump(); @@ -210,8 +210,8 @@ CUDAQ_TEST(NoiseTest, checkAmpDampType) { CUDAQ_TEST(NoiseTest, checkAmpDampTypeSimple) { cudaq::amplitude_damping_channel ad(1.); - cudaq::noise_model noise; - noise.add_channel({0}, ad); + auto noise = std::make_shared(); + noise->add_channel({0}, ad); cudaq::set_noise(noise); auto counts = cudaq::sample(xOp{}); counts.dump(); @@ -222,8 +222,8 @@ CUDAQ_TEST(NoiseTest, checkAmpDampTypeSimple) { CUDAQ_TEST(NoiseTest, checkBitFlipType) { cudaq::bit_flip_channel bf(.1); - cudaq::noise_model noise; - noise.add_channel({0}, bf); + auto noise = std::make_shared(); + noise->add_channel({0}, bf); cudaq::set_noise(noise); auto counts = cudaq::sample(xOp{}); counts.dump(); @@ -235,8 +235,8 @@ CUDAQ_TEST(NoiseTest, checkBitFlipType) { CUDAQ_TEST(NoiseTest, checkBitFlipTypeSimple) { cudaq::bit_flip_channel bf(1.); - cudaq::noise_model noise; - noise.add_channel({0}, bf); + auto noise = std::make_shared(); + noise->add_channel({0}, bf); cudaq::set_noise(noise); auto counts = cudaq::sample(xOp{}); counts.dump(); @@ -256,8 +256,8 @@ CUDAQ_TEST(NoiseTest, checkPhaseFlipType) { }; cudaq::phase_flip_channel pf(1.); - cudaq::noise_model noise; - noise.add_channel({0}, pf); + auto noise = std::make_shared(); + noise->add_channel({0}, pf); cudaq::set_noise(noise); auto counts = cudaq::sample(kernel); counts.dump(); @@ -267,7 +267,7 @@ CUDAQ_TEST(NoiseTest, checkPhaseFlipType) { } CUDAQ_TEST(NoiseTest, useNoiseFromOtherStack) { - + cudaq::set_random_seed(13); auto kernel = []() __qpu__ { cudaq::qubit q; h(q); @@ -278,8 +278,8 @@ CUDAQ_TEST(NoiseTest, useNoiseFromOtherStack) { auto set_noise_func = []() { cudaq::phase_flip_channel pf(1.); - cudaq::noise_model noise; - noise.add_channel({0}, pf); + auto noise = std::make_shared(); + noise->add_channel({0}, pf); cudaq::set_noise(noise); };