Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions python/cudaq/_experimental/runtime_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,11 @@ def estimate(self, module: CompiledModule, args: KernelArgs,
**kwargs) -> EstimateResult:
"""Estimate the resources a compiled kernel would use.

Keyword arguments: ``choice``, a callable returning a `bool` that
resolves each measurement so that kernels branching on measurement
results take a definite path.
Keyword arguments include ``choice``, a callable returning a ``bool``
that resolves each measurement so that kernels branching on measurement
results take a definite path. Additional keyword arguments passed to
:func:`cudaq.estimate` are forwarded unchanged for endpoint-specific
estimation options.
"""
...

Expand Down
5 changes: 4 additions & 1 deletion python/cudaq/runtime/resource_count.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,11 @@ def estimate(kernel, *args, **kwargs):
decorator = mk_decorator(kernel)
processedArgs, module = decorator.prepare_call(*args)
choice = kwargs.get("choice", None)
endpoint_options = {
key: value for key, value in kwargs.items() if key != "choice"
}
return cudaq_runtime.estimate_impl(decorator.uniqName, module, choice,
*processedArgs)
endpoint_options, *processedArgs)


@trace.traced
Expand Down
15 changes: 11 additions & 4 deletions python/runtime/cudaq/algorithms/py_resource_count.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "py_resource_count.h"
#include "common/Resources.h"
#include "common/cudaq_json.h"
#include "runtime/cudaq/platform/PyRuntimeEndpoint.h"
#include "runtime/cudaq/platform/py_alt_launch_kernel.h"
#include "utils/JsonNanobindAdaptors.h"
#include "utils/OpaqueArguments.h"
Expand All @@ -25,7 +26,7 @@ using namespace cudaq;
static estimate_result
estimate_impl(const std::string &kernelName, MlirModule kernelMod,
std::optional<std::function<bool()>> choice,
nanobind::args args) {
nanobind::dict endpointOptions, nanobind::args args) {
auto &platform = cudaq::get_platform();
args = simplifiedValidateInputArguments(args);

Expand All @@ -47,6 +48,7 @@ estimate_impl(const std::string &kernelName, MlirModule kernelMod,
estimate_policy policy{
.kernelName = kernelName,
.choice = *std::move(choice),
.endpointOptions = makePythonEndpointOptions(std::move(endpointOptions)),
};
return detail::launch(policy, 0, ctx, platform, [&]() {
// Pass nullptr for the compiled slot to disable JIT-artifact caching:
Expand All @@ -61,8 +63,10 @@ estimate_impl(const std::string &kernelName, MlirModule kernelMod,
static Resources
estimate_resources_impl(const std::string &kernelName, MlirModule kernelMod,
std::optional<std::function<bool()>> choice,
nanobind::args args) {
return estimate_impl(kernelName, kernelMod, choice, args).get_resources();
nanobind::dict endpointOptions, nanobind::args args) {
return estimate_impl(kernelName, kernelMod, choice,
std::move(endpointOptions), args)
.get_resources();
}

void cudaq::bindCountResources(nanobind::module_ &mod) {
Expand Down Expand Up @@ -117,9 +121,12 @@ void cudaq::bindCountResources(nanobind::module_ &mod) {

mod.def("estimate_impl", estimate_impl, nanobind::arg("kernel_name"),
nanobind::arg("kernel_mod"), nanobind::arg("choice").none(),
nanobind::arg("endpoint_options") = nanobind::dict(),
nanobind::arg("args"), "See python documentation for estimate.");
mod.def("estimate_resources_impl", estimate_resources_impl,
nanobind::arg("kernel_name"), nanobind::arg("kernel_mod"),
nanobind::arg("choice").none(), nanobind::arg("args"),
nanobind::arg("choice").none(),
nanobind::arg("endpoint_options") = nanobind::dict(),
nanobind::arg("args"),
"See python documentation for estimate_resources.");
}
39 changes: 39 additions & 0 deletions python/runtime/cudaq/platform/PyRuntimeEndpoint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,25 @@ struct PyKernelArgs {
template <launch_policy Policy>
struct PyProtocol {};

/// Python-owned options that are opaque to core launch policies.
struct PythonEndpointOptions final : endpoint_options {
explicit PythonEndpointOptions(nanobind::dict options)
: options(std::move(options)) {}

nanobind::dict options;
};

template <typename Policy>
static void appendEndpointOptions(const Policy &policy,
nanobind::dict &kwargs) {
auto options =
std::dynamic_pointer_cast<PythonEndpointOptions>(policy.endpointOptions);
if (!options)
return;
for (auto [key, value] : options->options)
kwargs[key] = value;
}

template <>
struct PyProtocol<sample_policy> {
static constexpr const char *Method = "sample";
Expand Down Expand Up @@ -261,6 +280,7 @@ pyLaunch(std::any &impl, const Policy &policy, const CompiledModule &module,
nanobind::rv_policy::move);

auto kwargs = Protocol::kwargs(policy);
appendEndpointOptions(policy, kwargs);
auto result = obj.attr(Protocol::Method)(pyModule, pyArgs, **kwargs);

if (!nanobind::isinstance<Result>(result)) {
Expand All @@ -278,6 +298,25 @@ pyLaunch(std::any &impl, const Policy &policy, const CompiledModule &module,
return nanobind::cast<typename Policy::result_type>(result);
}

std::shared_ptr<endpoint_options>
cudaq::makePythonEndpointOptions(nanobind::dict options) {
if (options.empty())
return {};

auto optionsDestructor = +[](endpoint_options *base) {
auto *options = static_cast<PythonEndpointOptions *>(base);
if (!Py_IsInitialized()) {
(void)options->options.release();
delete options;
return;
}
nanobind::gil_scoped_acquire gil;
delete options;
};
return std::shared_ptr<endpoint_options>(
new PythonEndpointOptions(std::move(options)), optionsDestructor);
}

static bool getAttrOrDefault(const nanobind::object &obj, const char *attr,
bool defaultValue) {
if (nanobind::hasattr(obj, attr))
Expand Down
5 changes: 5 additions & 0 deletions python/runtime/cudaq/platform/PyRuntimeEndpoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,16 @@
#pragma once

#include "cudaq/Target/RuntimeEndpoint.h"
#include <memory>
#include <nanobind/nanobind.h>

namespace cudaq {

/// Create python bindings for C++ code in this compilation unit.
void bindRuntimeEndpoint(nanobind::module_ &mod);

/// Preserve Python-only endpoint keyword arguments on a launch policy.
std::shared_ptr<endpoint_options>
makePythonEndpointOptions(nanobind::dict options);

} // namespace cudaq
12 changes: 12 additions & 0 deletions python/tests/backends/test_experimental_runtime_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,18 @@ def test_estimate_forwards_the_choice_function():
assert kwargs["choice"]() is True


def test_estimate_forwards_endpoint_options():
endpoint = DemoEndpoint()
set_runtime_endpoint(endpoint)

marker = object()
cudaq.estimate(kernel, 1, [1, 2, 3], tier="logical", marker=marker)

_, _, kwargs = endpoint.calls[0]
assert kwargs["tier"] == "logical"
assert kwargs["marker"] is marker


def test_estimate_resources_launch():
endpoint = DemoEndpoint()
set_runtime_endpoint(endpoint)
Expand Down
2 changes: 2 additions & 0 deletions runtime/cudaq/algorithms/dem/policy.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "common/CompileOptions.h"
#include "cudaq/algorithms/dem/options.h"
#include "cudaq/algorithms/dem/result.h"
#include "cudaq/algorithms/endpoint_options.h"
#include <string>

namespace cudaq {
Expand All @@ -24,6 +25,7 @@ struct dem_policy {
dem_options options;
std::string kernelName;
const noise_model *noiseModel = nullptr;
std::shared_ptr<endpoint_options> endpointOptions;

friend CompileOptions get_compile_options_impl(const dem_policy &);
};
Expand Down
21 changes: 21 additions & 0 deletions runtime/cudaq/algorithms/endpoint_options.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/****************************************************************-*- C++ -*-****

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need this additional struct. You can just forward declare PythonEndpointOptions in runtime/cudaq/algorithms/observe/policy.h.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That being said, with the suggestion I'm making above, having some type like this to wrap the opaque values will be useful. Maybe something like

struct EndpointOptionValue {
    std::any value
};

This type should live in the detail namespace.

* Copyright (c) 2026 NVIDIA Corporation & Affiliates. *
* All rights reserved. *
* *
* This source code and the accompanying materials are made available under *
* the terms of the Apache License 2.0 which accompanies this distribution. *
******************************************************************************/

#pragma once

#include <memory>

namespace cudaq {

/// Type-erased, endpoint-specific options attached to a launch policy.
/// Core policies remain independent of the language used by an endpoint.
struct endpoint_options {
virtual ~endpoint_options() = default;
};

} // namespace cudaq
4 changes: 4 additions & 0 deletions runtime/cudaq/algorithms/estimate/policy.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#pragma once

#include "common/CompileOptions.h"
#include "cudaq/algorithms/endpoint_options.h"
#include "cudaq/algorithms/estimate/result.h"
#include <functional>
#include <string>
Expand All @@ -28,6 +29,9 @@ struct estimate_policy {
/// follow when the kernel branches on a measurement result.
std::function<bool()> choice;

/// Options for a runtime endpoint that CUDA-Q itself does not interpret.
std::shared_ptr<endpoint_options> endpointOptions;

friend CompileOptions get_compile_options_impl(const estimate_policy &);
};

Expand Down
3 changes: 3 additions & 0 deletions runtime/cudaq/algorithms/observe/policy.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "common/CompileOptions.h"
#include "common/Future.h"
#include "common/ObserveResult.h"
#include "cudaq/algorithms/endpoint_options.h"
#include "cudaq/algorithms/observe/options.h"
#include "cudaq/operators.h"

Expand Down Expand Up @@ -44,6 +45,8 @@ struct observe_policy {

mutable bool canHandleObserve = false;

std::shared_ptr<endpoint_options> endpointOptions;

friend observe_result
finalize_execution_manager_impl(ExecutionManager &mgr,
const observe_policy &policy,
Expand Down
3 changes: 3 additions & 0 deletions runtime/cudaq/algorithms/sample/policy.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "common/CompileOptions.h"
#include "common/Future.h"
#include "common/SampleResult.h"
#include "cudaq/algorithms/endpoint_options.h"
#include "cudaq/algorithms/sample/options.h"

namespace nvqir {
Expand Down Expand Up @@ -42,6 +43,8 @@ struct sample_policy {

mutable const noise_model *noiseModel = nullptr;

std::shared_ptr<endpoint_options> endpointOptions;

friend sample_result
finalize_execution_manager_impl(ExecutionManager &mgr,
const sample_policy &policy);
Expand Down
Loading