cudaq.evolve() on a batch of super-operators wrongly forces a mixed (density-matrix) state
Nobody has claimed this yet.
- Dominant language
- C++
- Stars
- 1.1k
- Forks
- 455
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 165
Description
Required prerequisites
- Consult the security policy. Not a security report.
- Read the documentation; this isn't addressed there.
- Searched the issue tracker; found nothing on this.
- PR attached with a failing test.
Describe the bug
cudaq.evolve() on the dynamics target, given a batch of super-operators, wrongly
promotes the simulation to a mixed (density-matrix) state even when none of the
super-operators in the batch has a dissipator - needlessly squaring the memory/compute cost
for what should be an ordinary state-vector evolution.
should_use_mixed_state (python/cudaq/dynamics/cudm_solver.py:23-61) is meant to check
whether any super-operator in the input actually has a "right apply" term (the signature of a
genuine dissipator/Lindblad action) before deciding a density matrix is required - it does
this correctly for a single super-operator via the has_right_apply helper (:52-54). But
for a sequence of super-operators, an earlier check short-circuits first:
if isinstance(hamiltonian, Sequence):
return any(isinstance(op, SuperOperator) for op in hamiltonian)
This returns True as soon as any element is merely a SuperOperator - regardless of whether
it has a right-apply term - and makes the correct, more specific check a few lines below
(elif isinstance(hamiltonian, Sequence): return any(isinstance(op, SuperOperator) and has_right_apply(op) for op in hamiltonian)) unreachable dead code for every sequence
input.
The C++ side of the same feature gets this right: CuDensityMatEvolution.cpp:794-795 computes
the batched case as std::any_of(superOps.begin(), superOps.end(), requiresDensityMatrix),
i.e. it checks the dissipator condition (requiresDensityMatrix, :225-229) on every element
of the batch - exactly what the Python elif branch already (but unreachably) implements.
Steps to reproduce the bug
import cudaq
from cudaq import boson
from cudaq.operators import SuperOperator
from cudaq.dynamics.cudm_solver import should_use_mixed_state
hamiltonian = boson.create(0) * boson.annihilate(0)
pure_left_multiply = SuperOperator.left_multiply(-1j * hamiltonian) # no dissipator
print(should_use_mixed_state(pure_left_multiply, [])) # False (correct)
print(should_use_mixed_state([pure_left_multiply, pure_left_multiply], [])) # True (WRONG)
Expected behavior
A batch of super-operators that are all pure left-multiplies (no right-apply/dissipator term)
should not be classified as needing a mixed state, exactly like the single-super-operator case
already handles it - i.e. the second print above should also be False.
Is this a regression? If it is, put the last known working version (or commit) here.
Not a regression from a deliberate choice. should_use_mixed_state was introduced whole-cloth
in 995dac165 (#3129, "Dynamics operator batching") - the correct elif branch for the
sequence case was written in the very same commit, immediately below the premature return
that makes it unreachable, which is itself strong evidence this is an authoring oversight
rather than an intended simplification. The file has had only two commits since, both about
distributed batched results, neither touching this function. No test anywhere exercises a
batched super-operator sequence with zero dissipators (every existing batched-super-operator
test case, e.g. test_batching_bugs in python/tests/dynamics/test_evolve_dynamics.py, uses
left_right_multiply, which has a right-apply term and so is unaffected either way).
Environment
- CUDA-Q version: 0.15.1 (
aca5853a7); confirmed unchanged at currentmain
(dbf497a0e) - zero commits touchpython/cudaq/dynamics/cudm_solver.pyin that range. - Python version: 3.12.3
- Operating system: Ubuntu 24.04.4 LTS
Suggestions
Drop the premature if isinstance(hamiltonian, Sequence): return any(isinstance(op, SuperOperator) for op in hamiltonian) early return so the already-correct elif branch below
it actually runs. PR attached.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in python/cudaq/dynamics/cudm_solver.py, focusing on should_use_mixed_state and its sequence handling. Review python/tests/dynamics/test_evolve_dynamics.py and the existing batching tests, then add coverage for a batch containing only pure left-multiply super-operators. Done means the batch is not classified as requiring a mixed state and the relevant tests pass.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp, python
- Domain
- backend, testing-qa
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 90/100