diff --git a/quil-py/tests_py/program/test_program.py b/quil-py/tests_py/program/test_program.py index 86f81179..93e35247 100644 --- a/quil-py/tests_py/program/test_program.py +++ b/quil-py/tests_py/program/test_program.py @@ -134,8 +134,8 @@ def test_basic_block_fixed_schedule(): FENCE q0 q1 SET-PHASE q0 "flux_tx_cz" 0.0 SET-PHASE q1 "flux_tx_iswap" 0.0 - NONBLOCKING PULSE q0 "flux_tx_cz" erf_square(duration: 6.000000000000001e-08) - NONBLOCKING PULSE q1 "flux_tx_iswap" erf_square(duration: 6.000000000000001e-08) + NONBLOCKING PULSE q0 "flux_tx_cz" erf_square(duration: 6.000000000000001e-08, pad_left: 0.1e-08, pad_right: 0.1e-08) + NONBLOCKING PULSE q1 "flux_tx_iswap" erf_square(duration: 6.000000000000001e-08, pad_left: 0.1e-08, pad_right: 0.1e-08) SHIFT-PHASE q0 "flux_tx_cz" 1.0 SHIFT-PHASE q1 "flux_tx_iswap" 1.0 FENCE q0 q1 diff --git a/quil-rs/src/program/analysis/control_flow_graph.rs b/quil-rs/src/program/analysis/control_flow_graph.rs index 98f60170..1128d9d0 100644 --- a/quil-rs/src/program/analysis/control_flow_graph.rs +++ b/quil-rs/src/program/analysis/control_flow_graph.rs @@ -745,8 +745,8 @@ DEFCAL CZ q0 q1: FENCE q0 q1 SET-PHASE q0 "flux_tx_cz" 0.0 SET-PHASE q1 "flux_tx_iswap" 0.0 - NONBLOCKING PULSE q0 "flux_tx_cz" erf_square(duration: 6.000000000000001e-08) - NONBLOCKING PULSE q1 "flux_tx_iswap" erf_square(duration: 6.000000000000001e-08) + NONBLOCKING PULSE q0 "flux_tx_cz" erf_square(duration: 6.000000000000001e-08, pad_left: 0, pad_right: 0) + NONBLOCKING PULSE q1 "flux_tx_iswap" erf_square(duration: 6.000000000000001e-08, pad_left: 0, pad_right: 0) SHIFT-PHASE q0 "flux_tx_cz" 1.0 SHIFT-PHASE q1 "flux_tx_iswap" 1.0 FENCE q0 q1 diff --git a/quil-rs/src/program/scheduling/schedule.rs b/quil-rs/src/program/scheduling/schedule.rs index 58d762c9..ac848fc0 100644 --- a/quil-rs/src/program/scheduling/schedule.rs +++ b/quil-rs/src/program/scheduling/schedule.rs @@ -210,9 +210,9 @@ impl<'p> ScheduledBasicBlock<'p> { fn get_waveform_duration_seconds( program: &Program, instruction: &Instruction, - waveform_invocation: &WaveformInvocation, + WaveformInvocation { name, parameters }: &WaveformInvocation, ) -> Option { - if let Some(definition) = program.waveforms.get(&waveform_invocation.name) { + if let Some(definition) = program.waveforms.get(name) { let sample_count = definition.matrix.len(); let common_sample_rate = program @@ -243,11 +243,22 @@ impl<'p> ScheduledBasicBlock<'p> { .map(|sample_rate| sample_count as f64 / sample_rate) .map(Seconds) } else { - waveform_invocation - .parameters - .get("duration") - .and_then(|v| v.to_real().ok()) - .map(Seconds) + // Per the Quil spec, all waveform templates have a "duration" + // parameter, and "erf_square" also has "pad_left" and "pad_right". + // We explicitly choose to be more flexible here, and allow any + // built-in waveform templates to have "pad_*" parameters, as well + // as allow "erf_square" to omit them. + let parameter = |parameter_name| { + parameters + .get(parameter_name) + .and_then(|v| v.to_real().ok()) + .map(Seconds) + }; + Some( + parameter("duration")? + + parameter("pad_left").unwrap_or(Seconds::zero()) + + parameter("pad_right").unwrap_or(Seconds::zero()), + ) } } @@ -404,6 +415,16 @@ PULSE 0 "a" flat(duration: 1.0) #[case( r#"DEFFRAME 0 "a": SAMPLE-RATE: 1e9 +PULSE 0 "a" erf_square(duration: 1.0, pad_left: 0.2, pad_right: 0.3) +PULSE 0 "a" erf_square(duration: 0.1, pad_left: 0.7, pad_right: 0.7) +PULSE 0 "a" erf_square(duration: 0.5, pad_left: 0.6, pad_right: 0.4) +FENCE +"#, + Ok(vec![0.0, 1.5, 3.0, 4.5]) + )] + #[case( + r#"DEFFRAME 0 "a": + SAMPLE-RATE: 1e9 DEFFRAME 0 "b": SAMPLE-RATE: 1e9 NONBLOCKING PULSE 0 "a" flat(duration: 1.0)