You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
We want that packaging story (pip install cudaq + vendor wheel via cudaq.backends). We cannot use the execution model that mock_rest demonstrates today (platform-qpu: remote_rest + custom ServerHelper only), because QESEM is a server-side observe API (same class of problem as in-tree Fermioniq).
Server returns mitigated { value, error_bar }. Job lifecycle is:
create → ESTIMATED → start → poll → SUCCEEDED
Not “create → poll sample counts → client reconstruct ⟨H⟩”.
The estimate→start handshake is handled in the vendor plugin via a custom Executor (same extension point as in-tree Braket-style backends). We are not asking CUDA-Q for first-class estimate→start hooks on stock remote_rest.
Why mock_rest / stock remote_rest is insufficient
Under remote_rest, observe typically splitsH = Σ cᵢ Pᵢ into measurement circuits beforeServerHelper::createJob. The helper sees post-split KernelExecutions — not the original (U, H) request. That destroys the observe contract before any external helper runs.
Coalescing the split /jobs array inside ServerHelper is not enough: wrong artifacts (measurement circuits), observable already gone, wrong product semantics (joint mitigation over full H), wrong result type (counts vs mitigated expectation).
What we do today
Fermioniq-style custom QPU in a CUDA-Q fork / in-tree backend:
platform-qpu: qedma + custom QedmaQPU
pauliTermSplitObservable = std::nullopt
Attach full spin_op; custom executor for estimate→start→poll
That works, but blocks the recommended end-user story (stock pip CUDA-Q + cudaq-qedma wheel) until external plugins can express the same observe hooks (full H, no Pauli split, expectation results). Lifecycle remains a plugin Executor concern.
Proposed directions (smallest change preferred)
Proposal A (preferred): External custom QPU plugins
Allow a package to ship a QPU .so (not only ServerHelper), e.g.:
Stock CUDA-Q dlopens external QPU types the same way it loads external ServerHelpers
Target YAML platform-qpu: <name> resolves to that registered QPU
Documented reference (e.g. external Fermioniq-style observe), not only mock_rest
Proposal B: Observe mode on stock remote_rest
If external QPUs are out of scope, add hooks so remote_rest + ServerHelper can do server-side observe:
YAML / target flag — e.g. observe-mode: server-side (equiv. pauliTermSplitObservable = std::nullopt): one KernelExecution for bare U
Pass full spin_op into the helper — attach on execution / createObserveJob(circuit, spin_op, config)
Observe-shaped results — e.g. processObserveResults(...) so CUDA-Q does not re-estimate ⟨H⟩ from bitstrings
Non-trivial job lifecycles (QESEM estimate→start) stay in a vendor Executor, not as new stock remote_rest APIs.
Proposal C: Document in-tree-only for observe backends
If A/B are not near-term: state clearly that server-side-observe backends remain in-tree, and mock_rest is sample-style REST only. Workable, but a different distribution story than “pip install on stock CUDA-Q.”
If not, can Proposal B be scoped so mock_rest-style packages cover Fermioniq/QESEM-class observe APIs (items 1–3 above)?
Until then, should Qedma continue the in-tree / fork path?
We are happy to prototype against a draft API, contribute a reference plugin alongside mock_rest (e.g. mock_observe_rest), or share QESEM job payload examples on a working session.
Decision matrix
Choice
Qedma distribution
Effort on CUDA-Q
A External QPU plugins
Wheel on stock CUDA-Q
Medium — generalize Fermioniq load path
B Observe mode on stock remote_rest
Wheel; ServerHelper (+ optional Executor)
Medium — flags + observe path on existing REST QPU
C In-tree only for observe backends
Upstream PR / custom build
Low short-term; ongoing fork cost for us
Fermioniq precedent (in-tree today)
Mechanism
Location
Custom QPU
runtime/cudaq/platform/fermioniq/FermioniqQPU.*
Disable Pauli split
pauliTermSplitObservable = std::nullopt
Attach full spin_op
attachFermioniqObservable(...)
Target YAML
platform-qpu: fermioniq
Externalizing that observe pattern (A or B) is what Qedma needs to use the recommended pip packaging without changing QESEM’s API.
Context
Qedma is integrating with CUDA-Q and was directed to the external-plugin packaging model on the
external-backendswork:mock_reston @anpaz's forkWe want that packaging story (
pip install cudaq+ vendor wheel viacudaq.backends). We cannot use the execution model thatmock_restdemonstrates today (platform-qpu: remote_rest+ customServerHelperonly), because QESEM is a server-side observe API (same class of problem as in-tree Fermioniq).cc @anpaz
What QESEM needs for
cudaq.observe(U, H)One job containing:
U(QASM)H(Pauli terms + coefficients)Server returns mitigated
{ value, error_bar }. Job lifecycle is:Not “create → poll sample counts → client reconstruct ⟨H⟩”.
The estimate→start handshake is handled in the vendor plugin via a custom
Executor(same extension point as in-tree Braket-style backends). We are not asking CUDA-Q for first-class estimate→start hooks on stockremote_rest.Why
mock_rest/ stockremote_restis insufficientUnder
remote_rest, observe typically splitsH = Σ cᵢ Pᵢinto measurement circuits beforeServerHelper::createJob. The helper sees post-splitKernelExecutions — not the original(U, H)request. That destroys the observe contract before any external helper runs.Coalescing the split
/jobsarray insideServerHelperis not enough: wrong artifacts (measurement circuits), observable already gone, wrong product semantics (joint mitigation over fullH), wrong result type (counts vs mitigated expectation).What we do today
Fermioniq-style custom QPU in a CUDA-Q fork / in-tree backend:
platform-qpu: qedma+ customQedmaQPUpauliTermSplitObservable = std::nulloptspin_op; custom executor for estimate→start→pollThat works, but blocks the recommended end-user story (stock pip CUDA-Q +
cudaq-qedmawheel) until external plugins can express the same observe hooks (fullH, no Pauli split, expectation results). Lifecycle remains a pluginExecutorconcern.Proposed directions (smallest change preferred)
Proposal A (preferred): External custom QPU plugins
Allow a package to ship a QPU
.so(not onlyServerHelper), e.g.:Requirements:
dlopens external QPU types the same way it loads externalServerHelpersplatform-qpu: <name>resolves to that registered QPUmock_restProposal B: Observe mode on stock
remote_restIf external QPUs are out of scope, add hooks so
remote_rest+ServerHelpercan do server-side observe:observe-mode: server-side(equiv.pauliTermSplitObservable = std::nullopt): oneKernelExecutionfor bareUspin_opinto the helper — attach on execution /createObserveJob(circuit, spin_op, config)processObserveResults(...)so CUDA-Q does not re-estimate ⟨H⟩ from bitstringsNon-trivial job lifecycles (QESEM estimate→start) stay in a vendor
Executor, not as new stockremote_restAPIs.Proposal C: Document in-tree-only for observe backends
If A/B are not near-term: state clearly that server-side-observe backends remain in-tree, and
mock_restis sample-style REST only. Workable, but a different distribution story than “pip install on stock CUDA-Q.”Ask
external-backends/ External backends Plugin Framework #4754?mock_rest-style packages cover Fermioniq/QESEM-class observe APIs (items 1–3 above)?We are happy to prototype against a draft API, contribute a reference plugin alongside
mock_rest(e.g.mock_observe_rest), or share QESEM job payload examples on a working session.Decision matrix
remote_restFermioniq precedent (in-tree today)
runtime/cudaq/platform/fermioniq/FermioniqQPU.*pauliTermSplitObservable = std::nulloptspin_opattachFermioniqObservable(...)platform-qpu: fermioniqExternalizing that observe pattern (A or B) is what Qedma needs to use the recommended pip packaging without changing QESEM’s API.
— Qedma (cudaq-qedma)