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(
159 basic_block: BasicBlock<'a>,
160 program: &'a Program,
161 custom_handler: &mut InstructionHandler,
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 = custom_handler
217 .memory_accesses(instruction, &extern_signature_map)
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 let no_memory_dependencies = memory_dependencies.is_empty();
247 for memory_dependency in memory_dependencies {
248 if memory_dependency.node_id != node {
250 let execution_dependency =
251 ExecutionDependency::AwaitMemoryAccess(memory_dependency.access_type);
252 add_dependency!(graph, memory_dependency.node_id => node, execution_dependency);
253 trailing_classical_instructions.remove(&memory_dependency.node_id);
257 }
258 }
259
260 match custom_handler.role_for_instruction(instruction) {
261 InstructionRole::ClassicalCompute => {
263 if no_memory_dependencies {
266 add_dependency!(graph, ScheduledGraphNode::BlockStart => node, ExecutionDependency::StableOrdering);
267 }
268 trailing_classical_instructions.insert(node);
269 Ok(())
270 }
271 InstructionRole::RFControl => {
272 let matched_frames = custom_handler.matching_frames(instruction, program);
273 let is_scheduled = custom_handler.is_scheduled(instruction);
274
275 if let Some(matched_frames) = matched_frames {
276 for frame in matched_frames.used {
277 if is_scheduled {
278 let previous_node_ids = last_timed_instruction_by_frame
279 .entry((*frame).clone())
280 .or_default()
281 .record_access_and_get_dependencies(
282 node,
283 InstructionFrameInteraction::Using,
284 );
285
286 for previous_node_id in previous_node_ids {
287 add_dependency!(graph, previous_node_id => node, ExecutionDependency::Scheduled);
288 }
289 }
290
291 let previous_node_ids = last_instruction_by_frame
292 .entry((*frame).clone())
293 .or_default()
294 .record_access_and_get_dependencies(
295 node,
296 InstructionFrameInteraction::Using,
297 );
298
299 for previous_node_id in previous_node_ids {
300 add_dependency!(graph, previous_node_id => node, ExecutionDependency::StableOrdering);
301 }
302 }
303
304 for frame in matched_frames.blocked {
305 if is_scheduled {
306 let previous_node_ids = last_timed_instruction_by_frame
307 .entry((*frame).clone())
308 .or_default()
309 .record_access_and_get_dependencies(
310 node,
311 InstructionFrameInteraction::Blocking,
312 );
313 for previous_node_id in previous_node_ids {
314 add_dependency!(graph, previous_node_id => node, ExecutionDependency::Scheduled);
315 }
316 }
317
318 let previous_node_ids = last_instruction_by_frame
319 .entry((*frame).clone())
320 .or_default()
321 .record_access_and_get_dependencies(
322 node,
323 InstructionFrameInteraction::Blocking,
324 );
325 for previous_node_id in previous_node_ids {
326 add_dependency!(graph, previous_node_id => node, ExecutionDependency::StableOrdering);
327 }
328 }
329 }
330
331 Ok(())
332 }
333 InstructionRole::ControlFlow => {
334 if let ScheduledGraphNode::BlockEnd = node {
335 Ok(())
336 } else {
337 Err(ScheduleError {
338 instruction_node: Some(node),
339 instruction: instruction.clone(),
340 variant: ScheduleErrorVariant::ControlFlowNotBlockTerminator,
341 })
342 }
343 }
344 InstructionRole::ProgramComposition => Err(ScheduleError {
345 instruction_node: Some(node),
346 instruction: instruction.clone(),
347 variant: ScheduleErrorVariant::UnschedulableInstruction,
348 }),
349 }?;
350 }
351
352 for trailing_classical_instruction in trailing_classical_instructions {
355 add_dependency!(graph, trailing_classical_instruction => ScheduledGraphNode::BlockEnd, ExecutionDependency::StableOrdering);
356 }
357
358 for node in last_timed_instruction_by_frame
359 .into_values()
360 .flat_map(DependencyQueue::into_pending_dependencies)
361 {
362 add_dependency!(graph, node => ScheduledGraphNode::BlockEnd, ExecutionDependency::Scheduled);
363 }
364
365 for node in last_instruction_by_frame
366 .into_values()
367 .flat_map(DependencyQueue::into_pending_dependencies)
368 {
369 add_dependency!(graph, node => ScheduledGraphNode::BlockEnd, ExecutionDependency::StableOrdering);
370 }
371
372 if basic_block.instructions().is_empty() {
374 add_dependency!(graph, ScheduledGraphNode::BlockStart => ScheduledGraphNode::BlockEnd, ExecutionDependency::StableOrdering);
375 }
376
377 Ok(ScheduledBasicBlock { graph, basic_block })
378 }
379
380 pub fn get_dependency_graph(&self) -> &DependencyGraph {
381 &self.graph
382 }
383
384 pub fn instructions(&'a self) -> &'a [&'a Instruction] {
385 self.basic_block.instructions()
386 }
387
388 pub fn get_instruction(&self, node_id: usize) -> Option<&Instruction> {
390 self.instructions().get(node_id).copied()
391 }
392
393 pub fn label(&self) -> Option<&Target> {
394 self.basic_block.label()
395 }
396
397 pub fn len(&self) -> usize {
399 self.instructions().len()
400 }
401
402 pub fn is_empty(&self) -> bool {
404 self.instructions().is_empty()
405 }
406
407 pub fn terminator(&self) -> &BasicBlockTerminator<'a> {
408 self.basic_block.terminator()
409 }
410
411 pub fn basic_block(&self) -> &BasicBlock<'a> {
412 &self.basic_block
413 }
414}
415
416#[derive(Clone, Debug)]
421pub struct ScheduledProgram<'a> {
422 basic_blocks: Vec<ScheduledBasicBlock<'a>>,
423}
424
425impl<'a> ScheduledProgram<'a> {
426 pub fn from_program(
428 program: &'a Program,
429 custom_handler: &mut InstructionHandler,
430 ) -> ScheduleResult<Self> {
431 let control_flow_graph = ControlFlowGraph::from(program);
432 Ok(Self {
433 basic_blocks: control_flow_graph
434 .into_blocks()
435 .into_iter()
436 .map(|block| ScheduledBasicBlock::build(block, program, custom_handler))
437 .collect::<ScheduleResult<Vec<_>>>()?,
438 })
439 }
440
441 pub fn basic_blocks(&self) -> &[ScheduledBasicBlock<'_>] {
442 self.basic_blocks.as_ref()
443 }
444
445 pub fn into_basic_blocks(self) -> Vec<ScheduledBasicBlock<'a>> {
446 self.basic_blocks
447 }
448}
449
450#[derive(Clone, Debug)]
451pub struct ScheduledBasicBlockOwned {
452 basic_block: BasicBlockOwned,
453 graph: DependencyGraph,
454}
455
456impl<'a> From<&'a ScheduledBasicBlockOwned> for ScheduledBasicBlock<'a> {
457 fn from(block: &'a ScheduledBasicBlockOwned) -> Self {
458 Self {
459 basic_block: (&block.basic_block).into(),
460 graph: block.graph.clone(),
461 }
462 }
463}
464
465impl From<ScheduledBasicBlock<'_>> for ScheduledBasicBlockOwned {
466 fn from(block: ScheduledBasicBlock) -> Self {
467 Self {
468 basic_block: block.basic_block.into(),
469 graph: block.graph.clone(),
470 }
471 }
472}
473
474#[cfg(all(test, feature = "graphviz-dot"))]
475mod graphviz_dot_tests {
476 use super::*;
477
478 use crate::program::scheduling::graphviz_dot::tests::build_dot_format_snapshot_test_case;
479
480 mod custom_handler {
481 use super::*;
482
483 use crate::instruction::Pragma;
484 use crate::instruction::PragmaArgument;
485 use crate::program::frame::FrameMatchCondition;
486 use crate::program::{MatchedFrames, MemoryAccesses};
487
488 fn get_custom_handler() -> InstructionHandler {
497 const NO_OP: &str = "NO-OP";
498 const RAW_INSTRUCTION: &str = "RAW-INSTRUCTION";
499
500 InstructionHandler::default()
501 .set_is_scheduled(|instruction| match instruction {
502 Instruction::Pragma(Pragma { name, .. }) if name == NO_OP => Some(false),
503 Instruction::Pragma(Pragma { name, .. }) if name == RAW_INSTRUCTION => {
504 Some(true)
505 }
506 _ => None,
507 })
508 .set_role_for_instruction(|instruction| match instruction {
509 Instruction::Pragma(Pragma { name, .. }) if name == NO_OP => {
510 Some(InstructionRole::ClassicalCompute)
511 }
512 Instruction::Pragma(Pragma { name, .. }) if name == RAW_INSTRUCTION => {
513 Some(InstructionRole::RFControl)
514 }
515 _ => None,
516 })
517 .set_matching_frames(|instruction, program| match instruction {
518 Instruction::Pragma(Pragma { name, .. }) if name == NO_OP => Some(None),
519 Instruction::Pragma(Pragma {
520 name, arguments, ..
521 }) if name == RAW_INSTRUCTION => Some(Some({
522 let frame_condition = if arguments.is_empty() {
523 FrameMatchCondition::All
524 } else {
525 FrameMatchCondition::AnyOfNames(
526 arguments
527 .iter()
528 .filter_map(|arg| match arg {
529 PragmaArgument::Identifier(name) => Some(name.as_str()),
530 PragmaArgument::Integer(_) => None,
531 })
532 .collect(),
533 )
534 };
535
536 let used = program
537 .frames
538 .get_matching_keys_for_condition(frame_condition);
539
540 MatchedFrames {
541 used,
542 blocked: HashSet::new(),
543 }
544 })),
545 _ => None,
546 })
547 .set_memory_accesses(|instruction| match instruction {
548 Instruction::Pragma(Pragma { name, .. }) if name == NO_OP => {
549 Some(MemoryAccesses::default())
550 }
551 Instruction::Pragma(Pragma { name, .. }) if name == RAW_INSTRUCTION => Some({
552 MemoryAccesses {
553 captures: HashSet::new(),
554 reads: [String::from("ro")].into(),
555 writes: HashSet::new(),
556 }
557 }),
558 _ => None,
559 })
560 }
561
562 build_dot_format_snapshot_test_case! {
563 only_pragmas_without_frames,
564 r#"
565DEFFRAME 0 "quux":
566 SAMPLE-RATE: 1.0
567 INITIAL-FREQUENCY: 1e8
568PRAGMA NO-OP
569PRAGMA RAW-INSTRUCTION
570PRAGMA RAW-INSTRUCTION
571PRAGMA NO-OP
572PRAGMA RAW-INSTRUCTION
573"#,
574 &mut get_custom_handler(),
575 }
576
577 build_dot_format_snapshot_test_case! {
578 only_pragmas_with_frames,
579 r#"
580DEFFRAME 0 "foo":
581 SAMPLE-RATE: 1.0
582 INITIAL-FREQUENCY: 1e8
583DEFFRAME 1 "bar":
584 SAMPLE-RATE: 1.0
585 INITIAL-FREQUENCY: 1e8
586
587PRAGMA NO-OP
588PRAGMA RAW-INSTRUCTION foo
589PRAGMA RAW-INSTRUCTION bar
590PRAGMA NO-OP
591PRAGMA RAW-INSTRUCTION foo bar
592PRAGMA NO-OP
593PRAGMA RAW-INSTRUCTION foo
594"#,
595 &mut get_custom_handler(),
596 }
597
598 build_dot_format_snapshot_test_case! {
599 mixed_pragmas_and_pulses,
600 r#"
601DEFFRAME 0 "foo":
602 SAMPLE-RATE: 1.0
603 INITIAL-FREQUENCY: 1e8
604DEFFRAME 1 "bar":
605 SAMPLE-RATE: 1.0
606 INITIAL-FREQUENCY: 1e8
607
608PRAGMA NO-OP
609PRAGMA RAW-INSTRUCTION foo
610PULSE 1 "bar" gaussian(duration: 1, fwhm: 2, t0: 3)
611PRAGMA RAW-INSTRUCTION foo bar
612PRAGMA NO-OP
613PULSE 0 "foo" gaussian(duration: 1, fwhm: 2, t0: 3)
614PRAGMA RAW-INSTRUCTION bar
615PULSE 0 "foo" gaussian(duration: 1, fwhm: 2, t0: 3)
616PULSE 1 "bar" gaussian(duration: 1, fwhm: 2, t0: 3)
617PRAGMA NO-OP
618PRAGMA RAW-INSTRUCTION foo
619"#,
620 &mut get_custom_handler(),
621 }
622 }
623
624 build_dot_format_snapshot_test_case! {
627 classical_write_read_load_load,
628 r#"
629DECLARE params1 REAL[1]
630DECLARE params2 REAL[1]
631DECLARE params3 REAL[1]
632DECLARE integers INTEGER[1]
633LOAD params2[0] params3 integers[0] # writes params2
634LOAD params1[0] params2 integers[0] # reads params2
635"#
636 }
637
638 build_dot_format_snapshot_test_case! {
641 classical_write_read_mul_load,
642 r#"
643DECLARE params1 REAL[1]
644DECLARE params2 REAL[1]
645DECLARE integers INTEGER[1]
646
647MUL params2[0] 2 # reads and writes params2
648LOAD params1[0] params2 integers[0] # just reads params2
649"#
650 }
651
652 build_dot_format_snapshot_test_case! {
655 classical_write_read_add_mul,
656 r#"
657DECLARE params1 REAL[1]
658DECLARE params2 REAL[1]
659DECLARE integers INTEGER[1]
660
661ADD params1[0] 1 # this reads and writes params1
662MUL params1[0] 2 # this reads and writes params1
663"#
664 }
665
666 build_dot_format_snapshot_test_case! {
669 classical_read_write_load_load,
670 r#"
671DECLARE params1 REAL[1]
672DECLARE params2 REAL[1]
673DECLARE integers INTEGER[1]
674
675LOAD params1[0] params2 integers[0] # reads params2
676LOAD params2[0] params3 integers[0] # writes params2
677"#
678 }
679
680 build_dot_format_snapshot_test_case! {
683 classical_read_write_load_mul,
684 r#"
685DECLARE params1 REAL[1]
686DECLARE params2 REAL[1]
687DECLARE integers INTEGER[1]
688
689LOAD params1[0] params2 integers[0] # reads params2
690MUL params2[0] 2 # reads and writes params2
691"#
692 }
693
694 build_dot_format_snapshot_test_case! {
699 quantum_write_parameterized_operations,
700 r#"
701DEFFRAME 0 "rx":
702 INITIAL-FREQUENCY: 1e8
703DEFFRAME 1 "rx":
704 INITIAL-FREQUENCY: 1e8
705
706DECLARE params1 REAL[1]
707DECLARE params2 REAL[1]
708DECLARE integers INTEGER[1]
709
710LOAD params2[0] params1 integers[0] # writes params2
711SHIFT-PHASE 0 "rf" params2[0] # reads params2
712LOAD params2[0] params1 integers[1] # writes params2
713SHIFT-PHASE 1 "rf" params2[0] # reads params2
714"#
715 }
716
717 build_dot_format_snapshot_test_case! {
719 classical_no_memory_pragma,
720 r#"PRAGMA example"#
721 }
722
723 build_dot_format_snapshot_test_case! {
724 write_capture_read,
725 r#"
726DECLARE bits BIT[1]
727DECLARE integers INTEGER[1]
728LOAD bits[0] bits2 integers[0] # write
729NONBLOCKING 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
730LOAD bits3[0] bits integers[0] # read
731"#
732 }
733
734 build_dot_format_snapshot_test_case! {
735 write_write_read,
736 r#"
737DECLARE bits BIT[1]
738DECLARE bits2 BIT[1]
739DECLARE bits3 BIT[1]
740DECLARE integers INTEGER[1]
741LOAD bits[0] bits2 integers[0] # write
742LOAD bits[0] bits3 integers[0] # write
743LOAD bits4[0] bits integers[0] # read
744"#
745 }
746
747 build_dot_format_snapshot_test_case! {
748 memory_dependency_not_in_block_terminator,
749 r#"
750DECLARE ro BIT
751DECLARE depends_on_ro BIT
752
753NONBLOCKING CAPTURE 0 "ro_rx" flat(duration: 2.0000000000000003e-06, iq: 1.0, scale: 1.0, phase: 0.8745492960861506, detuning: 0.0) ro
754MOVE depends_on_ro ro
755JUMP @eq
756LABEL @eq
757PULSE 0 "ro_tx" gaussian(duration: 1, fwhm: 2, t0: 3)
758"#
759 }
760
761 build_dot_format_snapshot_test_case! {
762 memory_dependency_in_block_terminator,
763 r#"
764DECLARE ro BIT
765
766NONBLOCKING CAPTURE 0 "ro_rx" flat(duration: 2.0000000000000003e-06, iq: 1.0, scale: 1.0, phase: 0.8745492960861506, detuning: 0.0) ro
767JUMP-WHEN @eq ro
768LABEL @eq
769PULSE 0 "ro_tx" gaussian(duration: 1, fwhm: 2, t0: 3)
770"#
771 }
772
773 build_dot_format_snapshot_test_case! {
774 no_memory_dependency_across_blocks,
775 r#"
776DECLARE ro BIT
777DECLARE depends_on_ro BIT
778
779NONBLOCKING CAPTURE 0 "ro_rx" flat(duration: 2.0000000000000003e-06, iq: 1.0, scale: 1.0, phase: 0.8745492960861506, detuning: 0.0) ro
780JUMP @eq
781LABEL @eq
782MOVE depends_on_ro ro
783PULSE 0 "ro_tx" gaussian(duration: 1, fwhm: 2, t0: 3)
784"#
785 }
786
787 build_dot_format_snapshot_test_case! {
788 memory_dependency_one_variable,
789 r#"DEFFRAME 0 "frame0":
790 DIRECTION: "tx"
791 INITIAL-FREQUENCY: 6864214214.214214
792 CENTER-FREQUENCY: 7250000000.0
793 HARDWARE-OBJECT: "{\"instrument_name\": \"tsunami00\", \"card_index\": 0, \"channel_type\": \"QGSx2Channel\", \"channel_index\": 0, \"sequencer_index\": 0, \"nco_index\": 0}"
794 SAMPLE-RATE: 1000000000.0
795DEFFRAME 1 "frame1":
796 DIRECTION: "tx"
797 INITIAL-FREQUENCY: 6864214214.214214
798 CENTER-FREQUENCY: 7250000000.0
799 HARDWARE-OBJECT: "{\"instrument_name\": \"tsunami00\", \"card_index\": 1, \"channel_type\": \"QGSx2Channel\", \"channel_index\": 0, \"sequencer_index\": 0, \"nco_index\": 0}"
800 SAMPLE-RATE: 1000000000.0
801
802DELAY 2e-8
803
804DECLARE phase REAL
805MOVE phase 0.1
806
807SET-PHASE 0 "frame0" 2*pi*phase
808SET-PHASE 1 "frame1" 2*pi*phase
809
810PULSE 0 "frame0" flat(iq: 1, duration: 4e-9)
811PULSE 1 "frame1" flat(iq: 1, duration: 4e-9)
812"#
813 }
814
815 build_dot_format_snapshot_test_case! {
816 memory_dependency_array,
817 r#"DEFFRAME 0 "frame0":
818 DIRECTION: "tx"
819 INITIAL-FREQUENCY: 6864214214.214214
820 CENTER-FREQUENCY: 7250000000.0
821 HARDWARE-OBJECT: "{\"instrument_name\": \"tsunami00\", \"card_index\": 0, \"channel_type\": \"QGSx2Channel\", \"channel_index\": 0, \"sequencer_index\": 0, \"nco_index\": 0}"
822 SAMPLE-RATE: 1000000000.0
823DEFFRAME 1 "frame1":
824 DIRECTION: "tx"
825 INITIAL-FREQUENCY: 6864214214.214214
826 CENTER-FREQUENCY: 7250000000.0
827 HARDWARE-OBJECT: "{\"instrument_name\": \"tsunami00\", \"card_index\": 1, \"channel_type\": \"QGSx2Channel\", \"channel_index\": 0, \"sequencer_index\": 0, \"nco_index\": 0}"
828 SAMPLE-RATE: 1000000000.0
829
830DELAY 2e-8
831
832DECLARE phase REAL[2]
833MOVE phase[0] 0.1
834MOVE phase[1] 0.1
835
836SET-PHASE 0 "frame0" 2*pi*phase[0]
837SET-PHASE 1 "frame1" 2*pi*phase[1]
838
839PULSE 0 "frame0" flat(iq: 1, duration: 4e-9)
840PULSE 1 "frame1" flat(iq: 1, duration: 4e-9)
841"#
842 }
843
844 build_dot_format_snapshot_test_case! {
845 memory_dependency_two_variables,
846 r#"DEFFRAME 0 "frame0":
847 DIRECTION: "tx"
848 INITIAL-FREQUENCY: 6864214214.214214
849 CENTER-FREQUENCY: 7250000000.0
850 HARDWARE-OBJECT: "{\"instrument_name\": \"tsunami00\", \"card_index\": 0, \"channel_type\": \"QGSx2Channel\", \"channel_index\": 0, \"sequencer_index\": 0, \"nco_index\": 0}"
851 SAMPLE-RATE: 1000000000.0
852DEFFRAME 1 "frame1":
853 DIRECTION: "tx"
854 INITIAL-FREQUENCY: 6864214214.214214
855 CENTER-FREQUENCY: 7250000000.0
856 HARDWARE-OBJECT: "{\"instrument_name\": \"tsunami00\", \"card_index\": 1, \"channel_type\": \"QGSx2Channel\", \"channel_index\": 0, \"sequencer_index\": 0, \"nco_index\": 0}"
857 SAMPLE-RATE: 1000000000.0
858
859DELAY 2e-8
860
861DECLARE phase0 REAL
862DECLARE phase1 REAL
863MOVE phase0 0.1
864MOVE phase1 0.1
865
866SET-PHASE 0 "frame0" 2*pi*phase0
867SET-PHASE 1 "frame1" 2*pi*phase1
868
869PULSE 0 "frame0" flat(iq: 1, duration: 4e-9)
870PULSE 1 "frame1" flat(iq: 1, duration: 4e-9)
871"#
872 }
873}