Skip to content

Commit 30a0e00

Browse files
committed
Fix evolve_async missing return and incorrect condition check
Bug fixes in evolution.py: - Fix missing return statement in evolve_single_async when collapse_operators is provided with store_intermediate_results=NONE mode - Fix condition check: use 'store_intermediate_results != NONE' instead of boolean check on enum value Test improvements: - Fix expectation_values() access pattern in test to correctly handle nested list structure - Fix intermediate_states() assertion: NONE mode returns 1 (final state only) - Add test_evolve_no_intermediate_results for sync version coverage Signed-off-by: huaweil <[email protected]>
1 parent f0579fb commit 30a0e00

File tree

2 files changed

+135
-7
lines changed

2 files changed

+135
-7
lines changed

python/cudaq/dynamics/evolution.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,7 @@ def evolve_single_async(
544544
step_parameters, dt)
545545
if shots_count is None:
546546
shots_count = -1
547-
if store_intermediate_results:
547+
if store_intermediate_results != IntermediateResultSave.NONE:
548548
evolution = _evolution_kernel(
549549
num_qubits,
550550
compute_step_matrix,
@@ -579,12 +579,12 @@ def evolve_single_async(
579579
return cudaq_runtime.evolve_async(initial_state, kernel)
580580
# FIXME: permit to compute expectation values for operators defined as matrix
581581
if len(collapse_operators) > 0:
582-
cudaq_runtime.evolve_async(initial_state,
583-
kernel,
584-
parameters[-1],
585-
observable_spinops,
586-
noise_model=noise,
587-
shots_count=shots_count)
582+
return cudaq_runtime.evolve_async(initial_state,
583+
kernel,
584+
parameters[-1],
585+
observable_spinops,
586+
noise_model=noise,
587+
shots_count=shots_count)
588588
return cudaq_runtime.evolve_async(initial_state,
589589
kernel,
590590
parameters[-1],

python/tests/dynamics/test_evolve_simulators.py

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,134 @@ def test_evolve_async():
331331
atol=0.1)
332332

333333

334+
def test_evolve_no_intermediate_results():
335+
"""Test evolve with store_intermediate_results=NONE
336+
to verify the else branch in evolve_single is working."""
337+
338+
# Qubit Hamiltonian
339+
hamiltonian = 2 * np.pi * 0.1 * spin.x(0)
340+
341+
# Dimensions
342+
dimensions = {0: 2}
343+
344+
# Initial state
345+
rho0 = cudaq.State.from_data(
346+
np.array([[1.0, 0.0], [0.0, 0.0]], dtype=np.complex128))
347+
348+
# Schedule
349+
steps = np.linspace(0, 10, 101)
350+
schedule = Schedule(steps, ["time"])
351+
352+
# Test 1: NONE without observables
353+
evolution_result = cudaq.evolve(
354+
hamiltonian,
355+
dimensions,
356+
schedule,
357+
rho0,
358+
store_intermediate_results=cudaq.IntermediateResultSave.NONE)
359+
360+
# NONE mode: only final state is saved, no intermediate states
361+
assert len(evolution_result.intermediate_states()) == 1
362+
363+
# Test 2: NONE with observables
364+
schedule.reset()
365+
evolution_result = cudaq.evolve(
366+
hamiltonian,
367+
dimensions,
368+
schedule,
369+
rho0,
370+
observables=[spin.y(0), spin.z(0)],
371+
store_intermediate_results=cudaq.IntermediateResultSave.NONE)
372+
373+
# Verify final expectation value is reasonable
374+
final_exp = evolution_result.expectation_values()
375+
assert final_exp is not None
376+
377+
# Test 3: NONE with collapse_operators (tests the missing return bug)
378+
schedule.reset()
379+
evolution_result_decay = cudaq.evolve(
380+
hamiltonian,
381+
dimensions,
382+
schedule,
383+
rho0,
384+
observables=[spin.y(0), spin.z(0)],
385+
collapse_operators=[np.sqrt(0.05) * spin.x(0)],
386+
store_intermediate_results=cudaq.IntermediateResultSave.NONE)
387+
388+
# Results with decay should differ from ideal (noise should have effect)
389+
# This test would fail if the noise_model is ignored (the return bug)
390+
final_exp_decay = evolution_result_decay.expectation_values()
391+
assert final_exp_decay is not None
392+
# expectation_values() returns [[ObserveResult, ...]] - outer list is time steps,
393+
# inner list is observables. With NONE mode, there's only one time step (final).
394+
assert final_exp_decay[0][0].expectation() != final_exp[0][0].expectation()
395+
assert final_exp_decay[0][1].expectation() != final_exp[0][1].expectation()
396+
397+
398+
def test_evolve_async_no_intermediate_results():
399+
"""Test evolve_async with store_intermediate_results=NONE
400+
to verify the else branch in evolve_single_async is working."""
401+
402+
# Qubit Hamiltonian
403+
hamiltonian = 2 * np.pi * 0.1 * spin.x(0)
404+
405+
# Dimensions
406+
dimensions = {0: 2}
407+
408+
# Initial state
409+
rho0 = cudaq.State.from_data(
410+
np.array([[1.0, 0.0], [0.0, 0.0]], dtype=np.complex128))
411+
412+
# Schedule
413+
steps = np.linspace(0, 10, 101)
414+
schedule = Schedule(steps, ["time"])
415+
416+
# Test 1: NONE without observables
417+
evolution_result = cudaq.evolve_async(
418+
hamiltonian,
419+
dimensions,
420+
schedule,
421+
rho0,
422+
store_intermediate_results=cudaq.IntermediateResultSave.NONE).get()
423+
424+
# NONE mode: only final state is saved, no intermediate states
425+
assert len(evolution_result.intermediate_states()) == 1
426+
427+
# Test 2: NONE with observables
428+
schedule.reset()
429+
evolution_result = cudaq.evolve_async(
430+
hamiltonian,
431+
dimensions,
432+
schedule,
433+
rho0,
434+
observables=[spin.y(0), spin.z(0)],
435+
store_intermediate_results=cudaq.IntermediateResultSave.NONE).get()
436+
437+
# Verify final expectation value is reasonable
438+
final_exp = evolution_result.expectation_values()
439+
assert final_exp is not None
440+
441+
# Test 3: NONE with collapse_operators (tests the missing return bug)
442+
schedule.reset()
443+
evolution_result_decay = cudaq.evolve_async(
444+
hamiltonian,
445+
dimensions,
446+
schedule,
447+
rho0,
448+
observables=[spin.y(0), spin.z(0)],
449+
collapse_operators=[np.sqrt(0.05) * spin.x(0)],
450+
store_intermediate_results=cudaq.IntermediateResultSave.NONE).get()
451+
452+
# Results with decay should differ from ideal (noise should have effect)
453+
# This test would fail if the noise_model is ignored (the return bug)
454+
final_exp_decay = evolution_result_decay.expectation_values()
455+
assert final_exp_decay is not None
456+
# expectation_values() returns [[ObserveResult, ...]] - outer list is time steps,
457+
# inner list is observables. With NONE mode, there's only one time step (final).
458+
assert final_exp_decay[0][0].expectation() != final_exp[0][0].expectation()
459+
assert final_exp_decay[0][1].expectation() != final_exp[0][1].expectation()
460+
461+
334462
# leave for gdb debugging
335463
if __name__ == "__main__":
336464
loc = os.path.abspath(__file__)

0 commit comments

Comments
 (0)