1use std::collections::{HashMap, HashSet};
18
19use petgraph::graphmap::GraphMap;
20use petgraph::Directed;
21
22use crate::instruction::{
23 ExternSignatureMap, FrameIdentifier, Instruction, InstructionHandler, Target,
24};
25use crate::program::analysis::{
26 BasicBlock, BasicBlockOwned, BasicBlockTerminator, ControlFlowGraph,
27};
28use crate::{instruction::InstructionRole, program::Program, quil::Quil};
29
30pub use crate::program::memory::MemoryAccessType;
31
32#[derive(Debug, Clone, Copy)]
33pub enum ScheduleErrorVariant {
34 DuplicateLabel,
35 Extern,
36 UncalibratedInstruction,
37 UnresolvedCallInstruction,
38 ControlFlowNotBlockTerminator,
39 UnschedulableInstruction,
40}
41
42#[derive(Debug, Clone, thiserror::Error)]
43#[error(
44 "Error scheduling {}: {}: {variant:?}",
45 .instruction_node.map_or_else(|| "an instruction".to_string(), |node| node.to_string()),
46 .instruction.to_quil_or_debug(),
47)]
48pub struct ScheduleError {
49 pub instruction_node: Option<ScheduledGraphNode>,
50 pub instruction: Instruction,
51 pub variant: ScheduleErrorVariant,
52}
53
54pub type ScheduleResult<T> = Result<T, ScheduleError>;
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Hash, Ord)]
57pub enum ScheduledGraphNode {
58 BlockStart,
59 InstructionIndex(usize),
60 BlockEnd,
61}
62
63impl std::fmt::Display for ScheduledGraphNode {
64 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65 match self {
66 Self::BlockStart => write!(f, "the start of the block"),
67 Self::InstructionIndex(ix) => write!(f, "instruction {ix}"),
68 Self::BlockEnd => write!(f, "the end-of-block terminator"),
69 }
70 }
71}
72
73#[derive(Debug, Default, Clone)]
78struct MemoryAccessQueue {
79 pending_capture: Option<ScheduledGraphNode>,
80 pending_reads: Vec<ScheduledGraphNode>,
81 pending_write: Option<ScheduledGraphNode>,
82}
83
84#[derive(Clone, Debug)]
87struct MemoryAccessDependency {
88 pub access_type: MemoryAccessType,
91
92 pub node_id: ScheduledGraphNode,
94}
95
96#[derive(Clone, Debug, Eq, PartialEq, Hash)]
97pub enum ExecutionDependency {
98 AwaitMemoryAccess(MemoryAccessType),
100
101 Scheduled,
105
106 StableOrdering,
108}
109
110impl MemoryAccessQueue {
113 pub fn get_blocking_nodes(
130 &mut self,
131 node_id: ScheduledGraphNode,
132 access: &MemoryAccessType,
133 ) -> Vec<MemoryAccessDependency> {
134 use MemoryAccessType::*;
135
136 let mut result = vec![];
137 if let Some(node_id) = self.pending_write {
138 result.push(MemoryAccessDependency {
139 node_id,
140 access_type: Write,
141 });
142 }
143 if let Some(node_id) = self.pending_capture {
144 result.push(MemoryAccessDependency {
145 node_id,
146 access_type: Capture,
147 });
148 }
149
150 self.pending_capture = None;
151 self.pending_write = None;
152
153 match access {
154 Read => {
155 self.pending_reads.push(node_id);
156 }
157 Capture => {
158 for upstream_node_id in self.pending_reads.iter() {
159 result.push(MemoryAccessDependency {
160 node_id: *upstream_node_id,
161 access_type: Read,
162 });
163 }
164
165 self.pending_reads = vec![];
166 self.pending_capture = Some(node_id);
167 }
168
169 Write => {
170 for upstream_node_id in self.pending_reads.iter() {
171 result.push(MemoryAccessDependency {
172 node_id: *upstream_node_id,
173 access_type: Read,
174 });
175 }
176
177 self.pending_reads = vec![];
178 self.pending_write = Some(node_id);
179 }
180 }
181
182 result
183 }
184}
185
186macro_rules! add_dependency {
188 ($graph:expr, $source:expr => $target:expr, $dependency:expr) => {{
189 let source = $source;
190 let target = $target;
191 let dependency = $dependency;
192 match $graph.edge_weight_mut(source, target) {
193 Some(edge) => {
194 edge.insert(dependency);
195 }
196 None => {
197 let mut edge = HashSet::new();
198 edge.insert(dependency);
199 $graph.add_edge(source, target, edge);
200 }
201 }
202 }};
203}
204
205pub type DependencyGraph = GraphMap<ScheduledGraphNode, HashSet<ExecutionDependency>, Directed>;
206
207#[derive(Clone, Debug)]
213pub struct ScheduledBasicBlock<'a> {
214 basic_block: BasicBlock<'a>,
215 pub(super) graph: DependencyGraph,
216}
217struct PreviousNodes {
238 using: Option<ScheduledGraphNode>,
239 blocking: HashSet<ScheduledGraphNode>,
240}
241
242impl Default for PreviousNodes {
243 fn default() -> Self {
249 Self {
250 using: Some(ScheduledGraphNode::BlockStart),
251 blocking: HashSet::new(),
252 }
253 }
254}
255
256impl PreviousNodes {
257 fn get_dependencies_for_next_user(
261 &mut self,
262 node: ScheduledGraphNode,
263 ) -> HashSet<ScheduledGraphNode> {
264 let mut result = std::mem::take(&mut self.blocking);
265 if let Some(previous_user) = self.using.replace(node) {
266 result.insert(previous_user);
267 }
268
269 result
270 }
271
272 fn get_dependency_for_next_blocker(
279 &mut self,
280 node: ScheduledGraphNode,
281 ) -> Option<ScheduledGraphNode> {
282 self.blocking.insert(node);
283 self.using
284 }
285
286 pub fn into_hashset(mut self) -> HashSet<ScheduledGraphNode> {
288 if let Some(using) = self.using {
289 self.blocking.insert(using);
290 }
291 self.blocking
292 }
293}
294
295impl<'a> ScheduledBasicBlock<'a> {
296 pub fn build(
298 basic_block: BasicBlock<'a>,
299 program: &'a Program,
300 custom_handler: &mut InstructionHandler,
301 ) -> ScheduleResult<Self> {
302 let mut graph: DependencyGraph = GraphMap::new();
303 graph.add_node(ScheduledGraphNode::BlockStart);
305
306 let mut trailing_classical_instructions: HashSet<ScheduledGraphNode> = HashSet::new();
310
311 let mut last_instruction_by_frame: HashMap<FrameIdentifier, PreviousNodes> = HashMap::new();
313 let mut last_timed_instruction_by_frame: HashMap<FrameIdentifier, PreviousNodes> =
314 HashMap::new();
315
316 let mut pending_memory_access: HashMap<String, MemoryAccessQueue> = HashMap::new();
319
320 let extern_signature_map = ExternSignatureMap::try_from(program.extern_pragma_map.clone())
321 .map_err(|(pragma, _)| ScheduleError {
322 instruction_node: None,
323 instruction: Instruction::Pragma(pragma),
324 variant: ScheduleErrorVariant::Extern,
325 })?;
326
327 let terminator = basic_block.terminator().clone().into_instruction();
328 let terminator_ref = terminator.as_ref();
329
330 let instructions_iter = basic_block
331 .instructions()
332 .iter()
333 .enumerate()
334 .map(|(index, instr)| (ScheduledGraphNode::InstructionIndex(index), *instr))
335 .chain(terminator_ref.map(|instr| (ScheduledGraphNode::BlockEnd, instr)));
336
337 for (node, instruction) in instructions_iter {
338 graph.add_node(node);
339
340 let accesses = custom_handler
341 .memory_accesses(instruction, &extern_signature_map)
342 .map_err(|_| ScheduleError {
343 instruction_node: Some(node),
344 instruction: instruction.clone(),
345 variant: ScheduleErrorVariant::UnresolvedCallInstruction,
346 })?;
347
348 let memory_dependencies = [
349 (accesses.reads, MemoryAccessType::Read),
350 (accesses.writes, MemoryAccessType::Write),
351 (accesses.captures, MemoryAccessType::Capture),
352 ]
353 .iter()
354 .flat_map(|(regions, access_type)| {
355 regions
356 .iter()
357 .flat_map(|region| {
358 pending_memory_access
359 .entry(region.clone())
360 .or_default()
361 .get_blocking_nodes(node, access_type)
364 })
365 .collect::<Vec<_>>()
367 })
368 .collect::<Vec<_>>();
369 let has_memory_dependencies = !memory_dependencies.is_empty();
370 for memory_dependency in memory_dependencies {
371 if memory_dependency.node_id != node {
373 let execution_dependency =
374 ExecutionDependency::AwaitMemoryAccess(memory_dependency.access_type);
375 add_dependency!(graph, memory_dependency.node_id => node, execution_dependency);
376 trailing_classical_instructions.remove(&memory_dependency.node_id);
380 }
381 }
382
383 match custom_handler.role_for_instruction(instruction) {
384 InstructionRole::ClassicalCompute => {
386 if !has_memory_dependencies {
389 add_dependency!(graph, ScheduledGraphNode::BlockStart => node, ExecutionDependency::StableOrdering);
390 }
391 trailing_classical_instructions.insert(node);
392 Ok(())
393 }
394 InstructionRole::RFControl => {
395 let matched_frames = custom_handler.matching_frames(instruction, program);
396 let is_scheduled = custom_handler.is_scheduled(instruction);
397
398 if let Some(matched_frames) = matched_frames {
399 for frame in matched_frames.used() {
400 if is_scheduled {
401 let previous_node_ids = last_timed_instruction_by_frame
402 .entry((*frame).clone())
403 .or_default()
404 .get_dependencies_for_next_user(node);
405
406 for previous_node_id in previous_node_ids {
407 add_dependency!(graph, previous_node_id => node, ExecutionDependency::Scheduled);
408 }
409 }
410
411 let previous_node_ids = last_instruction_by_frame
412 .entry((*frame).clone())
413 .or_default()
414 .get_dependencies_for_next_user(node);
415
416 for previous_node_id in previous_node_ids {
417 add_dependency!(graph, previous_node_id => node, ExecutionDependency::StableOrdering);
418 }
419 }
420
421 for frame in matched_frames.blocked() {
422 if is_scheduled {
423 if let Some(previous_node_id) = last_timed_instruction_by_frame
424 .entry((*frame).clone())
425 .or_default()
426 .get_dependency_for_next_blocker(node)
427 {
428 add_dependency!(graph, previous_node_id => node, ExecutionDependency::Scheduled);
429 }
430 }
431
432 if let Some(previous_node_id) = last_instruction_by_frame
433 .entry((*frame).clone())
434 .or_default()
435 .get_dependency_for_next_blocker(node)
436 {
437 add_dependency!(graph, previous_node_id => node, ExecutionDependency::StableOrdering);
438 }
439 }
440 }
441
442 Ok(())
443 }
444 InstructionRole::ControlFlow => {
445 if let ScheduledGraphNode::BlockEnd = node {
446 Ok(())
447 } else {
448 Err(ScheduleError {
449 instruction_node: Some(node),
450 instruction: instruction.clone(),
451 variant: ScheduleErrorVariant::ControlFlowNotBlockTerminator,
452 })
453 }
454 }
455 InstructionRole::ProgramComposition => Err(ScheduleError {
456 instruction_node: Some(node),
457 instruction: instruction.clone(),
458 variant: ScheduleErrorVariant::UnschedulableInstruction,
459 }),
460 }?;
461 }
462
463 for trailing_classical_instruction in trailing_classical_instructions {
466 add_dependency!(graph, trailing_classical_instruction => ScheduledGraphNode::BlockEnd, ExecutionDependency::StableOrdering);
467 }
468
469 for previous_nodes in last_timed_instruction_by_frame.into_values() {
470 for node in previous_nodes.into_hashset() {
471 add_dependency!(graph, node => ScheduledGraphNode::BlockEnd, ExecutionDependency::Scheduled);
472 }
473 }
474
475 for previous_nodes in last_instruction_by_frame.into_values() {
476 for node in previous_nodes.into_hashset() {
477 add_dependency!(graph, node => ScheduledGraphNode::BlockEnd, ExecutionDependency::StableOrdering);
478 }
479 }
480
481 if basic_block.instructions().is_empty() {
483 add_dependency!(graph, ScheduledGraphNode::BlockStart => ScheduledGraphNode::BlockEnd, ExecutionDependency::StableOrdering);
484 }
485
486 Ok(ScheduledBasicBlock { graph, basic_block })
487 }
488
489 pub fn get_dependency_graph(&self) -> &DependencyGraph {
490 &self.graph
491 }
492
493 pub fn instructions(&'a self) -> &'a [&'a Instruction] {
494 self.basic_block.instructions()
495 }
496
497 pub fn get_instruction(&self, node_id: usize) -> Option<&Instruction> {
499 self.instructions().get(node_id).copied()
500 }
501
502 pub fn label(&self) -> Option<&Target> {
503 self.basic_block.label()
504 }
505
506 pub fn len(&self) -> usize {
508 self.instructions().len()
509 }
510
511 pub fn is_empty(&self) -> bool {
513 self.instructions().is_empty()
514 }
515
516 pub fn terminator(&self) -> &BasicBlockTerminator {
517 self.basic_block.terminator()
518 }
519
520 pub fn basic_block(&self) -> &BasicBlock<'a> {
521 &self.basic_block
522 }
523}
524
525#[derive(Clone, Debug)]
530pub struct ScheduledProgram<'a> {
531 basic_blocks: Vec<ScheduledBasicBlock<'a>>,
532}
533
534impl<'a> ScheduledProgram<'a> {
535 pub fn from_program(
537 program: &'a Program,
538 custom_handler: &mut InstructionHandler,
539 ) -> ScheduleResult<Self> {
540 let control_flow_graph = ControlFlowGraph::from(program);
541 Ok(Self {
542 basic_blocks: control_flow_graph
543 .into_blocks()
544 .into_iter()
545 .map(|block| ScheduledBasicBlock::build(block, program, custom_handler))
546 .collect::<ScheduleResult<Vec<_>>>()?,
547 })
548 }
549
550 pub fn basic_blocks(&self) -> &[ScheduledBasicBlock<'_>] {
551 self.basic_blocks.as_ref()
552 }
553
554 pub fn into_basic_blocks(self) -> Vec<ScheduledBasicBlock<'a>> {
555 self.basic_blocks
556 }
557}
558
559#[derive(Clone, Debug)]
560pub struct ScheduledBasicBlockOwned {
561 basic_block: BasicBlockOwned,
562 graph: DependencyGraph,
563}
564
565impl<'a> From<&'a ScheduledBasicBlockOwned> for ScheduledBasicBlock<'a> {
566 fn from(block: &'a ScheduledBasicBlockOwned) -> Self {
567 Self {
568 basic_block: (&block.basic_block).into(),
569 graph: block.graph.clone(),
570 }
571 }
572}
573
574impl From<ScheduledBasicBlock<'_>> for ScheduledBasicBlockOwned {
575 fn from(block: ScheduledBasicBlock) -> Self {
576 Self {
577 basic_block: block.basic_block.into(),
578 graph: block.graph.clone(),
579 }
580 }
581}
582
583#[cfg(all(test, feature = "graphviz-dot"))]
584mod graphviz_dot_tests {
585 use super::*;
586
587 use crate::program::scheduling::graphviz_dot::tests::build_dot_format_snapshot_test_case;
588
589 mod custom_handler {
590 use super::*;
591
592 use crate::instruction::Pragma;
593 use crate::instruction::PragmaArgument;
594 use crate::program::frame::FrameMatchCondition;
595 use crate::program::{MatchedFrames, MemoryAccesses};
596
597 fn get_custom_handler() -> InstructionHandler {
606 const NO_OP: &str = "NO-OP";
607 const RAW_INSTRUCTION: &str = "RAW-INSTRUCTION";
608
609 InstructionHandler::default()
610 .set_is_scheduled(|instruction| match instruction {
611 Instruction::Pragma(Pragma { name, .. }) if name == NO_OP => Some(false),
612 Instruction::Pragma(Pragma { name, .. }) if name == RAW_INSTRUCTION => {
613 Some(true)
614 }
615 _ => None,
616 })
617 .set_role_for_instruction(|instruction| match instruction {
618 Instruction::Pragma(Pragma { name, .. }) if name == NO_OP => {
619 Some(InstructionRole::ClassicalCompute)
620 }
621 Instruction::Pragma(Pragma { name, .. }) if name == RAW_INSTRUCTION => {
622 Some(InstructionRole::RFControl)
623 }
624 _ => None,
625 })
626 .set_matching_frames(|instruction, program| match instruction {
627 Instruction::Pragma(Pragma { name, .. }) if name == NO_OP => Some(None),
628 Instruction::Pragma(Pragma {
629 name, arguments, ..
630 }) if name == RAW_INSTRUCTION => Some(Some({
631 let frame_condition = if arguments.is_empty() {
632 FrameMatchCondition::All
633 } else {
634 FrameMatchCondition::AnyOfNames(
635 arguments
636 .iter()
637 .filter_map(|arg| match arg {
638 PragmaArgument::Identifier(name) => Some(name.as_str()),
639 PragmaArgument::Integer(_) => None,
640 })
641 .collect(),
642 )
643 };
644
645 let used = program
646 .frames
647 .get_matching_keys_for_condition(frame_condition);
648
649 MatchedFrames {
650 used,
651 blocked: HashSet::new(),
652 }
653 })),
654 _ => None,
655 })
656 .set_memory_accesses(|instruction| match instruction {
657 Instruction::Pragma(Pragma { name, .. }) if name == NO_OP => {
658 Some(MemoryAccesses::default())
659 }
660 Instruction::Pragma(Pragma { name, .. }) if name == RAW_INSTRUCTION => Some({
661 MemoryAccesses {
662 captures: HashSet::new(),
663 reads: [String::from("ro")].into(),
664 writes: HashSet::new(),
665 }
666 }),
667 _ => None,
668 })
669 }
670
671 build_dot_format_snapshot_test_case! {
672 only_pragmas_without_frames,
673 r#"
674DEFFRAME 0 "quux":
675 SAMPLE-RATE: 1.0
676 INITIAL-FREQUENCY: 1e8
677PRAGMA NO-OP
678PRAGMA RAW-INSTRUCTION
679PRAGMA RAW-INSTRUCTION
680PRAGMA NO-OP
681PRAGMA RAW-INSTRUCTION
682"#,
683 &mut get_custom_handler(),
684 }
685
686 build_dot_format_snapshot_test_case! {
687 only_pragmas_with_frames,
688 r#"
689DEFFRAME 0 "foo":
690 SAMPLE-RATE: 1.0
691 INITIAL-FREQUENCY: 1e8
692DEFFRAME 1 "bar":
693 SAMPLE-RATE: 1.0
694 INITIAL-FREQUENCY: 1e8
695
696PRAGMA NO-OP
697PRAGMA RAW-INSTRUCTION foo
698PRAGMA RAW-INSTRUCTION bar
699PRAGMA NO-OP
700PRAGMA RAW-INSTRUCTION foo bar
701PRAGMA NO-OP
702PRAGMA RAW-INSTRUCTION foo
703"#,
704 &mut get_custom_handler(),
705 }
706
707 build_dot_format_snapshot_test_case! {
708 mixed_pragmas_and_pulses,
709 r#"
710DEFFRAME 0 "foo":
711 SAMPLE-RATE: 1.0
712 INITIAL-FREQUENCY: 1e8
713DEFFRAME 1 "bar":
714 SAMPLE-RATE: 1.0
715 INITIAL-FREQUENCY: 1e8
716
717PRAGMA NO-OP
718PRAGMA RAW-INSTRUCTION foo
719PULSE 1 "bar" gaussian(duration: 1, fwhm: 2, t0: 3)
720PRAGMA RAW-INSTRUCTION foo bar
721PRAGMA NO-OP
722PULSE 0 "foo" gaussian(duration: 1, fwhm: 2, t0: 3)
723PRAGMA RAW-INSTRUCTION bar
724PULSE 0 "foo" gaussian(duration: 1, fwhm: 2, t0: 3)
725PULSE 1 "bar" gaussian(duration: 1, fwhm: 2, t0: 3)
726PRAGMA NO-OP
727PRAGMA RAW-INSTRUCTION foo
728"#,
729 &mut get_custom_handler(),
730 }
731 }
732
733 build_dot_format_snapshot_test_case! {
736 classical_write_read_load_load,
737 r#"
738DECLARE params1 REAL[1]
739DECLARE params2 REAL[1]
740DECLARE params3 REAL[1]
741DECLARE integers INTEGER[1]
742LOAD params2[0] params3 integers[0] # writes params2
743LOAD params1[0] params2 integers[0] # reads params2
744"#
745 }
746
747 build_dot_format_snapshot_test_case! {
750 classical_write_read_mul_load,
751 r#"
752DECLARE params1 REAL[1]
753DECLARE params2 REAL[1]
754DECLARE integers INTEGER[1]
755
756MUL params2[0] 2 # reads and writes params2
757LOAD params1[0] params2 integers[0] # just reads params2
758"#
759 }
760
761 build_dot_format_snapshot_test_case! {
764 classical_write_read_add_mul,
765 r#"
766DECLARE params1 REAL[1]
767DECLARE params2 REAL[1]
768DECLARE integers INTEGER[1]
769
770ADD params1[0] 1 # this reads and writes params1
771MUL params1[0] 2 # this reads and writes params1
772"#
773 }
774
775 build_dot_format_snapshot_test_case! {
778 classical_read_write_load_load,
779 r#"
780DECLARE params1 REAL[1]
781DECLARE params2 REAL[1]
782DECLARE integers INTEGER[1]
783
784LOAD params1[0] params2 integers[0] # reads params2
785LOAD params2[0] params3 integers[0] # writes params2
786"#
787 }
788
789 build_dot_format_snapshot_test_case! {
792 classical_read_write_load_mul,
793 r#"
794DECLARE params1 REAL[1]
795DECLARE params2 REAL[1]
796DECLARE integers INTEGER[1]
797
798LOAD params1[0] params2 integers[0] # reads params2
799MUL params2[0] 2 # reads and writes params2
800"#
801 }
802
803 build_dot_format_snapshot_test_case! {
807 quantum_write_parameterized_operations,
808 r#"
809DEFFRAME 0 "rx":
810 INITIAL-FREQUENCY: 1e8
811DEFFRAME 1 "rx":
812 INITIAL-FREQUENCY: 1e8
813
814DECLARE params1 REAL[1]
815DECLARE params2 REAL[1]
816DECLARE integers INTEGER[1]
817
818LOAD params2[0] params1 integers[0] # writes params2
819SHIFT-PHASE 0 "rf" params2[0] # reads params2
820LOAD params2[0] params1 integers[1] # writes params2
821SHIFT-PHASE 1 "rf" params2[0] # reads params2
822"#
823 }
824
825 build_dot_format_snapshot_test_case! {
827 classical_no_memory_pragma,
828 r#"PRAGMA example"#
829 }
830
831 build_dot_format_snapshot_test_case! {
832 write_capture_read,
833 r#"
834DECLARE bits BIT[1]
835DECLARE integers INTEGER[1]
836LOAD bits[0] bits2 integers[0] # write
837NONBLOCKING CAPTURE 0 "Transmon-0_readout_rx" flat(duration: 2.0000000000000003e-06, iq: 1.0, scale: 1.0, phase: 0.8745492960861506, detuning: 0.0) bits[0] # capture
838LOAD bits3[0] bits integers[0] # read
839"#
840 }
841
842 build_dot_format_snapshot_test_case! {
843 write_write_read,
844 r#"
845DECLARE bits BIT[1]
846DECLARE bits2 BIT[1]
847DECLARE bits3 BIT[1]
848DECLARE integers INTEGER[1]
849LOAD bits[0] bits2 integers[0] # write
850LOAD bits[0] bits3 integers[0] # write
851LOAD bits4[0] bits integers[0] # read
852"#
853 }
854
855 build_dot_format_snapshot_test_case! {
856 memory_dependency_not_in_block_terminator,
857 r#"
858DECLARE ro BIT
859DECLARE depends_on_ro BIT
860
861NONBLOCKING CAPTURE 0 "ro_rx" flat(duration: 2.0000000000000003e-06, iq: 1.0, scale: 1.0, phase: 0.8745492960861506, detuning: 0.0) ro
862MOVE depends_on_ro ro
863JUMP @eq
864LABEL @eq
865PULSE 0 "ro_tx" gaussian(duration: 1, fwhm: 2, t0: 3)
866"#
867 }
868
869 build_dot_format_snapshot_test_case! {
870 memory_dependency_in_block_terminator,
871 r#"
872DECLARE ro BIT
873
874NONBLOCKING CAPTURE 0 "ro_rx" flat(duration: 2.0000000000000003e-06, iq: 1.0, scale: 1.0, phase: 0.8745492960861506, detuning: 0.0) ro
875JUMP-WHEN @eq ro
876LABEL @eq
877PULSE 0 "ro_tx" gaussian(duration: 1, fwhm: 2, t0: 3)
878"#
879 }
880
881 build_dot_format_snapshot_test_case! {
882 no_memory_dependency_across_blocks,
883 r#"
884DECLARE ro BIT
885DECLARE depends_on_ro BIT
886
887NONBLOCKING CAPTURE 0 "ro_rx" flat(duration: 2.0000000000000003e-06, iq: 1.0, scale: 1.0, phase: 0.8745492960861506, detuning: 0.0) ro
888JUMP @eq
889LABEL @eq
890MOVE depends_on_ro ro
891PULSE 0 "ro_tx" gaussian(duration: 1, fwhm: 2, t0: 3)
892"#
893 }
894}