1use std::collections::{HashMap, HashSet};
18
19use petgraph::graphmap::GraphMap;
20use petgraph::Directed;
21
22use self::dependency_queue::DependencyQueue;
23use crate::instruction::{
24 ExternSignatureMap, FrameIdentifier, Instruction, InstructionHandler, Target,
25};
26use crate::program::analysis::{
27 BasicBlock, BasicBlockOwned, BasicBlockTerminator, ControlFlowGraph,
28};
29use crate::{instruction::InstructionRole, program::Program, quil::Quil};
30
31pub use crate::program::memory::MemoryAccessType;
32
33mod dependency_queue;
34
35#[derive(Debug, Clone, Copy)]
36pub enum ScheduleErrorVariant {
37 DuplicateLabel,
38 Extern,
39 UncalibratedInstruction,
40 UnresolvedCallInstruction,
41 ControlFlowNotBlockTerminator,
42 UnschedulableInstruction,
43}
44
45#[derive(Debug, Clone, thiserror::Error)]
46#[error(
47 "Error scheduling {}: {}: {variant:?}",
48 .instruction_node.map_or_else(|| "an instruction".to_string(), |node| node.to_string()),
49 .instruction.to_quil_or_debug(),
50)]
51pub struct ScheduleError {
52 pub instruction_node: Option<ScheduledGraphNode>,
53 pub instruction: Instruction,
54 pub variant: ScheduleErrorVariant,
55}
56
57pub type ScheduleResult<T> = Result<T, ScheduleError>;
58
59#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Hash, Ord)]
60pub enum ScheduledGraphNode {
61 BlockStart,
62 InstructionIndex(usize),
63 BlockEnd,
64}
65
66impl std::fmt::Display for ScheduledGraphNode {
67 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68 match self {
69 Self::BlockStart => write!(f, "the start of the block"),
70 Self::InstructionIndex(ix) => write!(f, "instruction {ix}"),
71 Self::BlockEnd => write!(f, "the end-of-block terminator"),
72 }
73 }
74}
75
76#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
79struct MemoryAccessDependency {
80 pub access_type: MemoryAccessType,
83
84 pub node_id: ScheduledGraphNode,
86}
87
88#[derive(Clone, Debug, Eq, PartialEq, Hash)]
89pub enum ExecutionDependency {
90 AwaitMemoryAccess(MemoryAccessType),
92
93 Scheduled,
97
98 StableOrdering,
100}
101
102macro_rules! add_dependency {
104 ($graph:expr, $source:expr => $target:expr, $dependency:expr) => {{
105 let source = $source;
106 let target = $target;
107 let dependency = $dependency;
108 match $graph.edge_weight_mut(source, target) {
109 Some(edge) => {
110 edge.insert(dependency);
111 }
112 None => {
113 let mut edge = HashSet::new();
114 edge.insert(dependency);
115 $graph.add_edge(source, target, edge);
116 }
117 }
118 }};
119}
120
121pub type DependencyGraph = GraphMap<ScheduledGraphNode, HashSet<ExecutionDependency>, Directed>;
122
123#[derive(Clone, Debug)]
129pub struct ScheduledBasicBlock<'a> {
130 basic_block: BasicBlock<'a>,
131 pub(super) graph: DependencyGraph,
132}
133
134#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
151pub enum InstructionFrameInteraction {
152 Blocking,
153 Using,
154}
155
156impl<'a> ScheduledBasicBlock<'a> {
157 pub fn build<H: InstructionHandler>(
159 basic_block: BasicBlock<'a>,
160 program: &'a Program,
161 handler: &H,
162 ) -> ScheduleResult<Self> {
163 let mut graph: DependencyGraph = GraphMap::new();
164 graph.add_node(ScheduledGraphNode::BlockStart);
166
167 let mut trailing_classical_instructions: HashSet<ScheduledGraphNode> = HashSet::new();
171
172 let mut last_instruction_by_frame: HashMap<
174 FrameIdentifier,
175 DependencyQueue<InstructionFrameInteraction>,
176 > = HashMap::new();
177
178 let mut last_timed_instruction_by_frame: HashMap<
179 FrameIdentifier,
180 DependencyQueue<InstructionFrameInteraction>,
181 > = HashMap::new();
182
183 let mut pending_memory_access: HashMap<String, DependencyQueue<MemoryAccessType>> =
194 HashMap::new();
195
196 let extern_signature_map = ExternSignatureMap::try_from(program.extern_pragma_map.clone())
197 .map_err(|(pragma, _)| ScheduleError {
198 instruction_node: None,
199 instruction: Instruction::Pragma(pragma),
200 variant: ScheduleErrorVariant::Extern,
201 })?;
202
203 let terminator = basic_block.terminator().clone().into_instruction();
204 let terminator_ref = terminator.as_ref();
205
206 let instructions_iter = basic_block
207 .instructions()
208 .iter()
209 .enumerate()
210 .map(|(index, instr)| (ScheduledGraphNode::InstructionIndex(index), *instr))
211 .chain(terminator_ref.map(|instr| (ScheduledGraphNode::BlockEnd, instr)));
212
213 for (node, instruction) in instructions_iter {
214 graph.add_node(node);
215
216 let accesses = handler
217 .memory_accesses(&extern_signature_map, instruction)
218 .map_err(|_| ScheduleError {
219 instruction_node: Some(node),
220 instruction: instruction.clone(),
221 variant: ScheduleErrorVariant::UnresolvedCallInstruction,
222 })?;
223
224 let memory_dependencies = [
225 (accesses.reads, MemoryAccessType::Read),
226 (accesses.writes, MemoryAccessType::Write),
227 (accesses.captures, MemoryAccessType::Capture),
228 ]
229 .into_iter()
230 .flat_map(|(regions, access_type)| {
231 regions
232 .into_iter()
233 .flat_map(|region| {
234 pending_memory_access
235 .entry(region)
236 .or_default()
237 .record_access_and_get_dependencies(node, access_type)
240 })
241 .collect::<Vec<_>>()
244 })
245 .collect::<Vec<_>>();
246
247 let mut leading_instruction = true;
250
251 for memory_dependency in memory_dependencies {
252 if memory_dependency.node_id != node {
254 let execution_dependency =
255 ExecutionDependency::AwaitMemoryAccess(memory_dependency.access_type);
256
257 add_dependency!(graph, memory_dependency.node_id => node, execution_dependency);
258
259 leading_instruction = false;
261
262 trailing_classical_instructions.remove(&memory_dependency.node_id);
266 }
267 }
268
269 let leading_instruction = leading_instruction;
271
272 match handler.role(instruction) {
273 InstructionRole::ClassicalCompute => {
275 if leading_instruction {
277 add_dependency!(graph, ScheduledGraphNode::BlockStart => node, ExecutionDependency::StableOrdering);
278 }
279 trailing_classical_instructions.insert(node);
280 Ok(())
281 }
282 InstructionRole::RFControl => {
283 let matched_frames = handler.matching_frames(program, instruction);
284 let is_scheduled = handler.is_scheduled(instruction);
285
286 if let Some(matched_frames) = matched_frames {
287 for frame in matched_frames.used {
288 if is_scheduled {
289 let previous_node_ids = last_timed_instruction_by_frame
290 .entry((*frame).clone())
291 .or_default()
292 .record_access_and_get_dependencies(
293 node,
294 InstructionFrameInteraction::Using,
295 );
296
297 for previous_node_id in previous_node_ids {
298 add_dependency!(graph, previous_node_id => node, ExecutionDependency::Scheduled);
299 }
300 }
301
302 let previous_node_ids = last_instruction_by_frame
303 .entry((*frame).clone())
304 .or_default()
305 .record_access_and_get_dependencies(
306 node,
307 InstructionFrameInteraction::Using,
308 );
309
310 for previous_node_id in previous_node_ids {
311 add_dependency!(graph, previous_node_id => node, ExecutionDependency::StableOrdering);
312 }
313 }
314
315 for frame in matched_frames.blocked {
316 if is_scheduled {
317 let previous_node_ids = last_timed_instruction_by_frame
318 .entry((*frame).clone())
319 .or_default()
320 .record_access_and_get_dependencies(
321 node,
322 InstructionFrameInteraction::Blocking,
323 );
324 for previous_node_id in previous_node_ids {
325 add_dependency!(graph, previous_node_id => node, ExecutionDependency::Scheduled);
326 }
327 }
328
329 let previous_node_ids = last_instruction_by_frame
330 .entry((*frame).clone())
331 .or_default()
332 .record_access_and_get_dependencies(
333 node,
334 InstructionFrameInteraction::Blocking,
335 );
336 for previous_node_id in previous_node_ids {
337 add_dependency!(graph, previous_node_id => node, ExecutionDependency::StableOrdering);
338 }
339 }
340 }
341
342 Ok(())
343 }
344 InstructionRole::ControlFlow => {
345 if let ScheduledGraphNode::BlockEnd = node {
346 Ok(())
347 } else {
348 Err(ScheduleError {
349 instruction_node: Some(node),
350 instruction: instruction.clone(),
351 variant: ScheduleErrorVariant::ControlFlowNotBlockTerminator,
352 })
353 }
354 }
355 InstructionRole::ProgramComposition => Err(ScheduleError {
356 instruction_node: Some(node),
357 instruction: instruction.clone(),
358 variant: ScheduleErrorVariant::UnschedulableInstruction,
359 }),
360 }?;
361 }
362
363 for trailing_classical_instruction in trailing_classical_instructions {
366 add_dependency!(graph, trailing_classical_instruction => ScheduledGraphNode::BlockEnd, ExecutionDependency::StableOrdering);
367 }
368
369 for node in last_timed_instruction_by_frame
370 .into_values()
371 .flat_map(DependencyQueue::into_pending_dependencies)
372 {
373 add_dependency!(graph, node => ScheduledGraphNode::BlockEnd, ExecutionDependency::Scheduled);
374 }
375
376 for node in last_instruction_by_frame
377 .into_values()
378 .flat_map(DependencyQueue::into_pending_dependencies)
379 {
380 add_dependency!(graph, node => ScheduledGraphNode::BlockEnd, ExecutionDependency::StableOrdering);
381 }
382
383 if basic_block.instructions().is_empty() {
385 add_dependency!(graph, ScheduledGraphNode::BlockStart => ScheduledGraphNode::BlockEnd, ExecutionDependency::StableOrdering);
386 }
387
388 Ok(ScheduledBasicBlock { graph, basic_block })
389 }
390
391 pub fn get_dependency_graph(&self) -> &DependencyGraph {
392 &self.graph
393 }
394
395 pub fn instructions(&'a self) -> &'a [&'a Instruction] {
396 self.basic_block.instructions()
397 }
398
399 pub fn get_instruction(&self, node_id: usize) -> Option<&Instruction> {
401 self.instructions().get(node_id).copied()
402 }
403
404 pub fn label(&self) -> Option<&Target> {
405 self.basic_block.label()
406 }
407
408 pub fn len(&self) -> usize {
410 self.instructions().len()
411 }
412
413 pub fn is_empty(&self) -> bool {
415 self.instructions().is_empty()
416 }
417
418 pub fn terminator(&self) -> &BasicBlockTerminator<'a> {
419 self.basic_block.terminator()
420 }
421
422 pub fn basic_block(&self) -> &BasicBlock<'a> {
423 &self.basic_block
424 }
425}
426
427#[derive(Clone, Debug)]
432pub struct ScheduledProgram<'a> {
433 basic_blocks: Vec<ScheduledBasicBlock<'a>>,
434}
435
436impl<'a> ScheduledProgram<'a> {
437 pub fn from_program<H: InstructionHandler>(
439 program: &'a Program,
440 handler: &H,
441 ) -> ScheduleResult<Self> {
442 let control_flow_graph = ControlFlowGraph::from(program);
443 Ok(Self {
444 basic_blocks: control_flow_graph
445 .into_blocks()
446 .into_iter()
447 .map(|block| ScheduledBasicBlock::build(block, program, handler))
448 .collect::<ScheduleResult<Vec<_>>>()?,
449 })
450 }
451
452 pub fn basic_blocks(&self) -> &[ScheduledBasicBlock<'_>] {
453 self.basic_blocks.as_ref()
454 }
455
456 pub fn into_basic_blocks(self) -> Vec<ScheduledBasicBlock<'a>> {
457 self.basic_blocks
458 }
459}
460
461#[derive(Clone, Debug)]
462pub struct ScheduledBasicBlockOwned {
463 basic_block: BasicBlockOwned,
464 graph: DependencyGraph,
465}
466
467impl<'a> From<&'a ScheduledBasicBlockOwned> for ScheduledBasicBlock<'a> {
468 fn from(block: &'a ScheduledBasicBlockOwned) -> Self {
469 Self {
470 basic_block: (&block.basic_block).into(),
471 graph: block.graph.clone(),
472 }
473 }
474}
475
476impl From<ScheduledBasicBlock<'_>> for ScheduledBasicBlockOwned {
477 fn from(block: ScheduledBasicBlock) -> Self {
478 Self {
479 basic_block: block.basic_block.into(),
480 graph: block.graph.clone(),
481 }
482 }
483}
484
485#[cfg(all(test, feature = "graphviz-dot"))]
486mod graphviz_dot_tests {
487 use super::*;
488
489 use crate::program::scheduling::graphviz_dot::tests::build_dot_format_snapshot_test_case;
490
491 mod custom_handler {
492 use super::*;
493
494 use crate::instruction::DefaultHandler;
495 use crate::instruction::Pragma;
496 use crate::instruction::PragmaArgument;
497 use crate::program::frame::FrameMatchCondition;
498 use crate::program::MemoryAccessesError;
499 use crate::program::{MatchedFrames, MemoryAccesses};
500
501 struct CustomHandler;
510
511 const NO_OP: &str = "NO-OP";
512 const RAW_INSTRUCTION: &str = "RAW-INSTRUCTION";
513
514 impl InstructionHandler for CustomHandler {
515 fn is_scheduled(&self, instruction: &Instruction) -> bool {
516 match instruction {
517 Instruction::Pragma(Pragma { name, .. }) if name == NO_OP => false,
518 Instruction::Pragma(Pragma { name, .. }) if name == RAW_INSTRUCTION => true,
519 _ => DefaultHandler.is_scheduled(instruction),
520 }
521 }
522
523 fn role(&self, instruction: &Instruction) -> InstructionRole {
524 match instruction {
525 Instruction::Pragma(Pragma { name, .. }) if name == NO_OP => {
526 InstructionRole::ClassicalCompute
527 }
528 Instruction::Pragma(Pragma { name, .. }) if name == RAW_INSTRUCTION => {
529 InstructionRole::RFControl
530 }
531 _ => DefaultHandler.role(instruction),
532 }
533 }
534
535 fn matching_frames<'p>(
536 &self,
537 program: &'p Program,
538 instruction: &Instruction,
539 ) -> Option<MatchedFrames<'p>> {
540 match instruction {
541 Instruction::Pragma(Pragma { name, .. }) if name == NO_OP => None,
542 Instruction::Pragma(Pragma {
543 name, arguments, ..
544 }) if name == RAW_INSTRUCTION => {
545 let frame_condition = if arguments.is_empty() {
546 FrameMatchCondition::All
547 } else {
548 FrameMatchCondition::AnyOfNames(
549 arguments
550 .iter()
551 .filter_map(|arg| match arg {
552 PragmaArgument::Identifier(name) => Some(name.as_str()),
553 PragmaArgument::Integer(_) => None,
554 })
555 .collect(),
556 )
557 };
558
559 let used = program
560 .frames
561 .get_matching_keys_for_condition(frame_condition);
562
563 Some(MatchedFrames {
564 used,
565 blocked: HashSet::new(),
566 })
567 }
568 _ => DefaultHandler.matching_frames(program, instruction),
569 }
570 }
571
572 fn memory_accesses(
573 &self,
574 extern_signature_map: &ExternSignatureMap,
575 instruction: &Instruction,
576 ) -> Result<MemoryAccesses, MemoryAccessesError> {
577 match instruction {
578 Instruction::Pragma(Pragma { name, .. }) if name == NO_OP => {
579 Ok(MemoryAccesses::none())
580 }
581 Instruction::Pragma(Pragma { name, .. }) if name == RAW_INSTRUCTION => {
582 Ok(MemoryAccesses {
583 reads: ["ro".to_owned()].into(),
584 writes: HashSet::new(),
585 captures: HashSet::new(),
586 })
587 }
588 _ => DefaultHandler.memory_accesses(extern_signature_map, instruction),
589 }
590 }
591 }
592
593 build_dot_format_snapshot_test_case! {
594 only_pragmas_without_frames,
595 r#"
596DEFFRAME 0 "quux":
597 SAMPLE-RATE: 1.0
598 INITIAL-FREQUENCY: 1e8
599PRAGMA NO-OP
600PRAGMA RAW-INSTRUCTION
601PRAGMA RAW-INSTRUCTION
602PRAGMA NO-OP
603PRAGMA RAW-INSTRUCTION
604"#,
605 &CustomHandler,
606 }
607
608 build_dot_format_snapshot_test_case! {
609 only_pragmas_with_frames,
610 r#"
611DEFFRAME 0 "foo":
612 SAMPLE-RATE: 1.0
613 INITIAL-FREQUENCY: 1e8
614DEFFRAME 1 "bar":
615 SAMPLE-RATE: 1.0
616 INITIAL-FREQUENCY: 1e8
617
618PRAGMA NO-OP
619PRAGMA RAW-INSTRUCTION foo
620PRAGMA RAW-INSTRUCTION bar
621PRAGMA NO-OP
622PRAGMA RAW-INSTRUCTION foo bar
623PRAGMA NO-OP
624PRAGMA RAW-INSTRUCTION foo
625"#,
626 &CustomHandler,
627 }
628
629 build_dot_format_snapshot_test_case! {
630 mixed_pragmas_and_pulses,
631 r#"
632DEFFRAME 0 "foo":
633 SAMPLE-RATE: 1.0
634 INITIAL-FREQUENCY: 1e8
635DEFFRAME 1 "bar":
636 SAMPLE-RATE: 1.0
637 INITIAL-FREQUENCY: 1e8
638
639PRAGMA NO-OP
640PRAGMA RAW-INSTRUCTION foo
641PULSE 1 "bar" gaussian(duration: 1, fwhm: 2, t0: 3)
642PRAGMA RAW-INSTRUCTION foo bar
643PRAGMA NO-OP
644PULSE 0 "foo" gaussian(duration: 1, fwhm: 2, t0: 3)
645PRAGMA RAW-INSTRUCTION bar
646PULSE 0 "foo" gaussian(duration: 1, fwhm: 2, t0: 3)
647PULSE 1 "bar" gaussian(duration: 1, fwhm: 2, t0: 3)
648PRAGMA NO-OP
649PRAGMA RAW-INSTRUCTION foo
650"#,
651 &CustomHandler,
652 }
653 }
654
655 build_dot_format_snapshot_test_case! {
658 classical_write_read_load_load,
659 r#"
660DECLARE params1 REAL[1]
661DECLARE params2 REAL[1]
662DECLARE params3 REAL[1]
663DECLARE integers INTEGER[1]
664LOAD params2[0] params3 integers[0] # writes params2
665LOAD params1[0] params2 integers[0] # reads params2
666"#
667 }
668
669 build_dot_format_snapshot_test_case! {
672 classical_write_read_mul_load,
673 r#"
674DECLARE params1 REAL[1]
675DECLARE params2 REAL[1]
676DECLARE integers INTEGER[1]
677
678MUL params2[0] 2 # reads and writes params2
679LOAD params1[0] params2 integers[0] # just reads params2
680"#
681 }
682
683 build_dot_format_snapshot_test_case! {
686 classical_write_read_add_mul,
687 r#"
688DECLARE params1 REAL[1]
689DECLARE params2 REAL[1]
690DECLARE integers INTEGER[1]
691
692ADD params1[0] 1 # this reads and writes params1
693MUL params1[0] 2 # this reads and writes params1
694"#
695 }
696
697 build_dot_format_snapshot_test_case! {
700 classical_read_write_load_load,
701 r#"
702DECLARE params1 REAL[1]
703DECLARE params2 REAL[1]
704DECLARE integers INTEGER[1]
705
706LOAD params1[0] params2 integers[0] # reads params2
707LOAD params2[0] params3 integers[0] # writes params2
708"#
709 }
710
711 build_dot_format_snapshot_test_case! {
714 classical_read_write_load_mul,
715 r#"
716DECLARE params1 REAL[1]
717DECLARE params2 REAL[1]
718DECLARE integers INTEGER[1]
719
720LOAD params1[0] params2 integers[0] # reads params2
721MUL params2[0] 2 # reads and writes params2
722"#
723 }
724
725 build_dot_format_snapshot_test_case! {
730 quantum_write_parameterized_operations,
731 r#"
732DEFFRAME 0 "rx":
733 INITIAL-FREQUENCY: 1e8
734DEFFRAME 1 "rx":
735 INITIAL-FREQUENCY: 1e8
736
737DECLARE params1 REAL[1]
738DECLARE params2 REAL[1]
739DECLARE integers INTEGER[1]
740
741LOAD params2[0] params1 integers[0] # writes params2
742SHIFT-PHASE 0 "rf" params2[0] # reads params2
743LOAD params2[0] params1 integers[1] # writes params2
744SHIFT-PHASE 1 "rf" params2[0] # reads params2
745"#
746 }
747
748 build_dot_format_snapshot_test_case! {
750 classical_no_memory_pragma,
751 r#"PRAGMA example"#
752 }
753
754 build_dot_format_snapshot_test_case! {
755 write_capture_read,
756 r#"
757DECLARE bits BIT[1]
758DECLARE integers INTEGER[1]
759LOAD bits[0] bits2 integers[0] # write
760NONBLOCKING 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
761LOAD bits3[0] bits integers[0] # read
762"#
763 }
764
765 build_dot_format_snapshot_test_case! {
766 write_write_read,
767 r#"
768DECLARE bits BIT[1]
769DECLARE bits2 BIT[1]
770DECLARE bits3 BIT[1]
771DECLARE integers INTEGER[1]
772LOAD bits[0] bits2 integers[0] # write
773LOAD bits[0] bits3 integers[0] # write
774LOAD bits4[0] bits integers[0] # read
775"#
776 }
777
778 build_dot_format_snapshot_test_case! {
779 memory_dependency_not_in_block_terminator,
780 r#"
781DECLARE ro BIT
782DECLARE depends_on_ro BIT
783
784NONBLOCKING CAPTURE 0 "ro_rx" flat(duration: 2.0000000000000003e-06, iq: 1.0, scale: 1.0, phase: 0.8745492960861506, detuning: 0.0) ro
785MOVE depends_on_ro ro
786JUMP @eq
787LABEL @eq
788PULSE 0 "ro_tx" gaussian(duration: 1, fwhm: 2, t0: 3)
789"#
790 }
791
792 build_dot_format_snapshot_test_case! {
793 memory_dependency_in_block_terminator,
794 r#"
795DECLARE ro BIT
796
797NONBLOCKING CAPTURE 0 "ro_rx" flat(duration: 2.0000000000000003e-06, iq: 1.0, scale: 1.0, phase: 0.8745492960861506, detuning: 0.0) ro
798JUMP-WHEN @eq ro
799LABEL @eq
800PULSE 0 "ro_tx" gaussian(duration: 1, fwhm: 2, t0: 3)
801"#
802 }
803
804 build_dot_format_snapshot_test_case! {
805 no_memory_dependency_across_blocks,
806 r#"
807DECLARE ro BIT
808DECLARE depends_on_ro BIT
809
810NONBLOCKING CAPTURE 0 "ro_rx" flat(duration: 2.0000000000000003e-06, iq: 1.0, scale: 1.0, phase: 0.8745492960861506, detuning: 0.0) ro
811JUMP @eq
812LABEL @eq
813MOVE depends_on_ro ro
814PULSE 0 "ro_tx" gaussian(duration: 1, fwhm: 2, t0: 3)
815"#
816 }
817
818 build_dot_format_snapshot_test_case! {
819 memory_dependency_one_variable,
820 r#"DEFFRAME 0 "frame0":
821 DIRECTION: "tx"
822 INITIAL-FREQUENCY: 6864214214.214214
823 CENTER-FREQUENCY: 7250000000.0
824 HARDWARE-OBJECT: "{\"instrument_name\": \"tsunami00\", \"card_index\": 0, \"channel_type\": \"QGSx2Channel\", \"channel_index\": 0, \"sequencer_index\": 0, \"nco_index\": 0}"
825 SAMPLE-RATE: 1000000000.0
826DEFFRAME 1 "frame1":
827 DIRECTION: "tx"
828 INITIAL-FREQUENCY: 6864214214.214214
829 CENTER-FREQUENCY: 7250000000.0
830 HARDWARE-OBJECT: "{\"instrument_name\": \"tsunami00\", \"card_index\": 1, \"channel_type\": \"QGSx2Channel\", \"channel_index\": 0, \"sequencer_index\": 0, \"nco_index\": 0}"
831 SAMPLE-RATE: 1000000000.0
832
833DELAY 2e-8
834
835DECLARE phase REAL
836MOVE phase 0.1
837
838SET-PHASE 0 "frame0" 2*pi*phase
839SET-PHASE 1 "frame1" 2*pi*phase
840
841PULSE 0 "frame0" flat(iq: 1, duration: 4e-9)
842PULSE 1 "frame1" flat(iq: 1, duration: 4e-9)
843"#
844 }
845
846 build_dot_format_snapshot_test_case! {
847 memory_dependency_array,
848 r#"DEFFRAME 0 "frame0":
849 DIRECTION: "tx"
850 INITIAL-FREQUENCY: 6864214214.214214
851 CENTER-FREQUENCY: 7250000000.0
852 HARDWARE-OBJECT: "{\"instrument_name\": \"tsunami00\", \"card_index\": 0, \"channel_type\": \"QGSx2Channel\", \"channel_index\": 0, \"sequencer_index\": 0, \"nco_index\": 0}"
853 SAMPLE-RATE: 1000000000.0
854DEFFRAME 1 "frame1":
855 DIRECTION: "tx"
856 INITIAL-FREQUENCY: 6864214214.214214
857 CENTER-FREQUENCY: 7250000000.0
858 HARDWARE-OBJECT: "{\"instrument_name\": \"tsunami00\", \"card_index\": 1, \"channel_type\": \"QGSx2Channel\", \"channel_index\": 0, \"sequencer_index\": 0, \"nco_index\": 0}"
859 SAMPLE-RATE: 1000000000.0
860
861DELAY 2e-8
862
863DECLARE phase REAL[2]
864MOVE phase[0] 0.1
865MOVE phase[1] 0.1
866
867SET-PHASE 0 "frame0" 2*pi*phase[0]
868SET-PHASE 1 "frame1" 2*pi*phase[1]
869
870PULSE 0 "frame0" flat(iq: 1, duration: 4e-9)
871PULSE 1 "frame1" flat(iq: 1, duration: 4e-9)
872"#
873 }
874
875 build_dot_format_snapshot_test_case! {
876 memory_dependency_two_variables,
877 r#"DEFFRAME 0 "frame0":
878 DIRECTION: "tx"
879 INITIAL-FREQUENCY: 6864214214.214214
880 CENTER-FREQUENCY: 7250000000.0
881 HARDWARE-OBJECT: "{\"instrument_name\": \"tsunami00\", \"card_index\": 0, \"channel_type\": \"QGSx2Channel\", \"channel_index\": 0, \"sequencer_index\": 0, \"nco_index\": 0}"
882 SAMPLE-RATE: 1000000000.0
883DEFFRAME 1 "frame1":
884 DIRECTION: "tx"
885 INITIAL-FREQUENCY: 6864214214.214214
886 CENTER-FREQUENCY: 7250000000.0
887 HARDWARE-OBJECT: "{\"instrument_name\": \"tsunami00\", \"card_index\": 1, \"channel_type\": \"QGSx2Channel\", \"channel_index\": 0, \"sequencer_index\": 0, \"nco_index\": 0}"
888 SAMPLE-RATE: 1000000000.0
889
890DELAY 2e-8
891
892DECLARE phase0 REAL
893DECLARE phase1 REAL
894MOVE phase0 0.1
895MOVE phase1 0.1
896
897SET-PHASE 0 "frame0" 2*pi*phase0
898SET-PHASE 1 "frame1" 2*pi*phase1
899
900PULSE 0 "frame0" flat(iq: 1, duration: 4e-9)
901PULSE 1 "frame1" flat(iq: 1, duration: 4e-9)
902"#
903 }
904}