From 4dffc79c12a0fcd8800b96cc748cba4b5607eb05 Mon Sep 17 00:00:00 2001 From: kalzoo <22137047+kalzoo@users.noreply.github.com> Date: Tue, 31 May 2022 18:27:02 -0700 Subject: [PATCH 01/19] Add private method for parsing a single instruction from a string --- src/instruction.rs | 12 +++++++++++- src/parser/mod.rs | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/instruction.rs b/src/instruction.rs index bd273d11..6b1e42a9 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -13,10 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. **/ +use nom::combinator::all_consuming; use serde::{Deserialize, Serialize}; use std::{collections::HashMap, fmt}; -use crate::expression::Expression; +use crate::{expression::Expression, parser::{instruction::parse_instruction, lex}}; #[cfg(test)] use proptest_derive::Arbitrary; @@ -942,6 +943,15 @@ impl Instruction { _ => {} } } + + /// Parse a single instruction from an input string. Returns an error if the input fails to parse, + /// or if there is input left over after parsing. + pub fn parse(input: &str) -> Result { + let lexed = lex(input)?; + println!("{:?}", lexed); + let (_, instruction) = all_consuming(parse_instruction)(&lexed).map_err(|e| e.to_string())?; + Ok(instruction) + } } #[cfg(test)] diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 42a86582..cb5e8954 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -28,7 +28,7 @@ mod macros; mod common; mod error; mod expression; -mod instruction; +pub(crate) mod instruction; mod lexer; type ParserInput<'a> = &'a [Token]; From 6e898994b78ba85f4aca0748a0e7e226d2756ea0 Mon Sep 17 00:00:00 2001 From: kalzoo <22137047+kalzoo@users.noreply.github.com> Date: Tue, 31 May 2022 18:27:21 -0700 Subject: [PATCH 02/19] WIP: Match frames by composable condition --- src/program/frame.rs | 89 ++++++++++++++++++++++---- src/program/mod.rs | 146 ++++++++++++++++++++++++++++++++++++------- 2 files changed, 200 insertions(+), 35 deletions(-) diff --git a/src/program/frame.rs b/src/program/frame.rs index 8b2ed984..d1cad882 100644 --- a/src/program/frame.rs +++ b/src/program/frame.rs @@ -35,18 +35,60 @@ impl FrameSet { self.frames.keys().collect() } - /// Return all contained FrameIdentifiers which include **exactly** these qubits (in any order) - /// and - if names are provided - match one of the names. - pub fn get_matching_keys(&self, qubits: &[Qubit], names: &[String]) -> Vec<&FrameIdentifier> { - let qubit_set: HashSet<&Qubit> = qubits.iter().collect(); - self.frames - .iter() - .filter(|(identifier, _)| { - (names.is_empty() || names.contains(&identifier.name)) - && qubit_set == identifier.qubits.iter().collect() - }) - .map(|(id, _)| id) - .collect::>() + /// Return all frames in the set which match all of these conditions. If a frame _would_ match, but is + /// not present in this [FrameSet], then it is not returned (notably, the [FrameMatchCondition::Specific] + /// match condition). + pub fn get_matching_keys(&self, condition: FrameMatchCondition) -> HashSet<&FrameIdentifier> { + let keys = self.frames.keys(); + + match condition { + FrameMatchCondition::All => keys.collect(), + FrameMatchCondition::AnyOfNames(names) => { + keys.filter(|&f| names.contains(&f.name)).collect() + } + FrameMatchCondition::AnyOfQubits(qubits) => { + let any_of_set: HashSet<_> = qubits.iter().collect(); + + keys.filter(|&f| f.qubits.iter().any(|q| any_of_set.contains(q))) + .collect() + } + FrameMatchCondition::ExactQubits(qubits) => { + let all_of_set: HashSet<_> = qubits.iter().collect(); + + keys.filter(|&f| f.qubits.iter().all(|q| all_of_set.contains(q))) + .collect() + } + FrameMatchCondition::Specific(frame) => { + if let Some(frame) = self.frames.get_key(&frame) { + vec![frame].into_iter().collect() + } else { + HashSet::new() + } + } + FrameMatchCondition::And(conditions) => { + let individual_sets: Vec> = conditions + .into_iter() + .map(|c| self.get_matching_keys(c)) + .collect(); + individual_sets + .into_iter() + .reduce(|acc, el| acc.into_iter().filter(|&v| el.contains(v)).collect()) + .unwrap_or_default() + } + FrameMatchCondition::Or(conditions) => { + let individual_sets: Vec> = conditions + .into_iter() + .map(|c| self.get_matching_keys(c)) + .collect(); + individual_sets + .into_iter() + .reduce(|acc, el| { + el.into_iter().for_each(|v| { acc.insert(v); }); + acc + }) + .unwrap_or_default() + } + } } /// Retrieve the attributes of a frame by its identifier. @@ -87,3 +129,26 @@ impl FrameSet { .collect() } } + +pub(crate) enum FrameMatchCondition { + /// Match all frames in the set + All, + + /// Match all frames which shares any one of these names + AnyOfNames(Vec), + + /// Match all frames which contain any of these qubits + AnyOfQubits(Vec), + + /// Match all frames which contain exactly these qubits + ExactQubits(Vec), + + /// Return these specific frames, if present in the set + Specific(FrameIdentifier), + + /// Return all frames which match all of these conditions + And(Vec), + + /// Return all frames which match any of these conditions + Or(Vec), +} diff --git a/src/program/mod.rs b/src/program/mod.rs index 5dbbb852..1a39cbe7 100644 --- a/src/program/mod.rs +++ b/src/program/mod.rs @@ -14,6 +14,7 @@ * limitations under the License. **/ use std::collections::BTreeMap; +use std::error::Error; use std::str::FromStr; use crate::instruction::{ @@ -24,6 +25,7 @@ use crate::instruction::{ use crate::parser::{lex, parse_instructions}; pub use self::calibration::CalibrationSet; +use self::frame::FrameMatchCondition; pub use self::frame::FrameSet; pub use self::memory::MemoryRegion; @@ -126,19 +128,28 @@ impl Program { /// /// Return `None` if the instruction does not execute in the context of a frame - such /// as classical instructions. + /// + /// See the [Quil-T spec](https://github.com/quil-lang/quil/blob/master/rfcs/analog/proposal.md) + /// for more information. pub fn get_frames_for_instruction<'a>( &'a self, instruction: &'a Instruction, include_blocked: bool, ) -> Option> { - match &instruction { + let condition = match &instruction { Instruction::Pulse(Pulse { blocking, frame, .. + }) + | Instruction::Capture(Capture { + blocking, frame, .. + }) + | Instruction::RawCapture(RawCapture { + blocking, frame, .. }) => { if *blocking && include_blocked { - Some(self.frames.get_keys()) + FrameMatchCondition::AnyOfQubits(frame.qubits) } else { - Some(vec![frame]) + FrameMatchCondition::Specific(frame.clone()) } } Instruction::Delay(Delay { @@ -146,35 +157,34 @@ impl Program { qubits, .. }) => { - let frame_ids = self.frames.get_matching_keys(qubits, frame_names); - Some(frame_ids) - } - Instruction::Fence(Fence { qubits }) => { - if qubits.is_empty() { - Some(self.frames.get_keys()) + if frame_names.is_empty() { + FrameMatchCondition::ExactQubits(qubits.clone()) } else { - Some(self.frames.get_matching_keys(qubits, &[])) + FrameMatchCondition::And(vec![ + FrameMatchCondition::ExactQubits(qubits.clone()), + FrameMatchCondition::AnyOfNames(frame_names.clone()), + ]) } } - Instruction::Capture(Capture { - blocking, frame, .. - }) - | Instruction::RawCapture(RawCapture { - blocking, frame, .. - }) => { - if *blocking && include_blocked { - Some(self.frames.get_keys()) + Instruction::Fence(Fence { qubits }) => { + if include_blocked { + FrameMatchCondition::AnyOfQubits(qubits.clone()) } else { - Some(vec![frame]) + return None; } } Instruction::SetFrequency(SetFrequency { frame, .. }) | Instruction::SetPhase(SetPhase { frame, .. }) | Instruction::SetScale(SetScale { frame, .. }) | Instruction::ShiftFrequency(ShiftFrequency { frame, .. }) - | Instruction::ShiftPhase(ShiftPhase { frame, .. }) => Some(vec![frame]), + | Instruction::ShiftPhase(ShiftPhase { frame, .. }) => { + FrameMatchCondition::Specific(frame.clone()) + } Instruction::SwapPhases(SwapPhases { frame_1, frame_2 }) => { - Some(vec![frame_1, frame_2]) + FrameMatchCondition::And(vec![ + FrameMatchCondition::Specific(frame_1.clone()), + FrameMatchCondition::Specific(frame_2.clone()), + ]) } Instruction::Gate(_) | Instruction::CircuitDefinition(_) @@ -196,8 +206,10 @@ impl Program { | Instruction::Store(_) | Instruction::Jump(_) | Instruction::JumpWhen(_) - | Instruction::JumpUnless(_) => None, - } + | Instruction::JumpUnless(_) => return None, + }; + + Some(self.frames.get_matching_keys(condition).into_iter().collect()) } pub fn to_instructions(&self, include_headers: bool) -> Vec { @@ -257,6 +269,8 @@ impl FromStr for Program { mod tests { use std::str::FromStr; + use crate::instruction::Instruction; + use super::Program; #[test] @@ -369,4 +383,90 @@ DECLARE ec BIT // program after being re-parsed and serialized. assert!(program1.lines().eq(program2.lines())); } + + #[test] + fn frame_blocking() { + let input = "DEFFRAME 0 \"a\": +\tHARDWARE-OBJECT: \"hardware\" + +DEFFRAME 0 \"b\": +\tHARDWARE-OBJECT: \"hardware\" + +DEFFRAME 1 \"c\": +\tHARDWARE-OBJECT: \"hardware\" + +DEFFRAME 0 1 \"2q\": +\tHARDWARE-OBJECT: \"hardware\" +"; + + let program = Program::from_str(input).unwrap(); + + for (instruction_string, expected_used_frames, expected_blocked_frames) in vec![ + // Blocking pulses block their qubits + ( + "PULSE 0 \"a\" custom_waveform", + vec!["0 \"a\""], + vec!["0 \"a\"", "0 \"b\"", "0 1 \"2q\""], + ), + ( + "PULSE 1 \"c\" custom_waveform", + vec!["1 \"c\""], + vec!["1 \"c\"", "0 1 \"2q\""], + ), + // Pulses on non-declared frames and unused qubits do not use or block any frames in the program + ("PULSE 2 \"a\" custom_waveform", vec![], vec![]), + // Captures work identically to Pulses + ( + "CAPTURE 0 \"a\" custom_waveform", + vec!["0 \"a\""], + vec!["0 \"a\"", "0 \"b\"", "0 1 \"2q\""], + ), + ( + "CAPTURE 1 \"c\" custom_waveform", + vec!["1 \"c\""], + vec!["1 \"c\"", "0 1 \"2q\""], + ), + ("CAPTURE 2 \"a\" custom_waveform", vec![], vec![]), + // A non-blocking pulse blocks only its precise frame, not other frames on the same qubits + ( + "NONBLOCKING PULSE 0 \"a\" custom_waveform", + vec!["0 \"a\""], + vec!["0 \"a\""], + ), + ( + "NONBLOCKING PULSE 1 \"c\" custom_waveform", + vec!["1 \"c\""], + vec!["1 \"c\""], + ), + ( + "NONBLOCKING PULSE 0 1 \"2q\" custom_waveform", + vec!["0 1 \"2q\""], + vec!["0 1 \"2q\""], + ), + ] { + let instruction = Instruction::parse(instruction_string).unwrap(); + let used_frames: Vec = program + .get_frames_for_instruction(&instruction, false) + .unwrap() + .into_iter() + .map(|f| f.to_string()) + .collect(); + assert_eq!( + used_frames, expected_used_frames, + "Instruction {} *used* frames `{:?}` but we expected `{:?}", + instruction, used_frames, expected_used_frames + ); + let blocked_frames: Vec = program + .get_frames_for_instruction(&instruction, true) + .unwrap() + .into_iter() + .map(|f| f.to_string()) + .collect(); + assert_eq!( + blocked_frames, expected_blocked_frames, + "Instruction {} *blocked* frames `{:?}` but we expected `{:?}", + instruction, blocked_frames, expected_blocked_frames + ); + } + } } From 42d0e59f78211d134c16a865b322ceeb3f0ff586 Mon Sep 17 00:00:00 2001 From: kalzoo <22137047+kalzoo@users.noreply.github.com> Date: Tue, 31 May 2022 18:46:18 -0700 Subject: [PATCH 03/19] Remove extraneous println --- src/instruction.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/instruction.rs b/src/instruction.rs index 6b1e42a9..400c5bc9 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -948,7 +948,6 @@ impl Instruction { /// or if there is input left over after parsing. pub fn parse(input: &str) -> Result { let lexed = lex(input)?; - println!("{:?}", lexed); let (_, instruction) = all_consuming(parse_instruction)(&lexed).map_err(|e| e.to_string())?; Ok(instruction) } From 3e9788058b57e298d5a337cd4c0887d0546e3df4 Mon Sep 17 00:00:00 2001 From: kalzoo <22137047+kalzoo@users.noreply.github.com> Date: Tue, 31 May 2022 18:47:07 -0700 Subject: [PATCH 04/19] Fix: instruction frame blocking --- src/program/frame.rs | 29 ++++++++++-------- src/program/mod.rs | 72 +++++++++++++++++++++++++++++++++----------- 2 files changed, 71 insertions(+), 30 deletions(-) diff --git a/src/program/frame.rs b/src/program/frame.rs index d1cad882..4ef6d104 100644 --- a/src/program/frame.rs +++ b/src/program/frame.rs @@ -36,9 +36,12 @@ impl FrameSet { } /// Return all frames in the set which match all of these conditions. If a frame _would_ match, but is - /// not present in this [FrameSet], then it is not returned (notably, the [FrameMatchCondition::Specific] + /// not present in this [FrameSet], then it is not returned (notably, the [FrameMatchCondition::Specific] /// match condition). - pub fn get_matching_keys(&self, condition: FrameMatchCondition) -> HashSet<&FrameIdentifier> { + pub(crate) fn get_matching_keys<'s, 'a>( + &'s self, + condition: FrameMatchCondition<'a>, + ) -> HashSet<&'s FrameIdentifier> { let keys = self.frames.keys(); match condition { @@ -59,7 +62,7 @@ impl FrameSet { .collect() } FrameMatchCondition::Specific(frame) => { - if let Some(frame) = self.frames.get_key(&frame) { + if let Some((frame, _)) = self.frames.get_key_value(&frame) { vec![frame].into_iter().collect() } else { HashSet::new() @@ -82,8 +85,10 @@ impl FrameSet { .collect(); individual_sets .into_iter() - .reduce(|acc, el| { - el.into_iter().for_each(|v| { acc.insert(v); }); + .reduce(|mut acc, el| { + el.into_iter().for_each(|v| { + acc.insert(v); + }); acc }) .unwrap_or_default() @@ -130,25 +135,25 @@ impl FrameSet { } } -pub(crate) enum FrameMatchCondition { +pub(crate) enum FrameMatchCondition<'a> { /// Match all frames in the set All, /// Match all frames which shares any one of these names - AnyOfNames(Vec), + AnyOfNames(&'a [String]), /// Match all frames which contain any of these qubits - AnyOfQubits(Vec), + AnyOfQubits(&'a [Qubit]), /// Match all frames which contain exactly these qubits - ExactQubits(Vec), + ExactQubits(&'a [Qubit]), /// Return these specific frames, if present in the set - Specific(FrameIdentifier), + Specific(&'a FrameIdentifier), /// Return all frames which match all of these conditions - And(Vec), + And(Vec>), /// Return all frames which match any of these conditions - Or(Vec), + Or(Vec>), } diff --git a/src/program/mod.rs b/src/program/mod.rs index 1a39cbe7..069b8e18 100644 --- a/src/program/mod.rs +++ b/src/program/mod.rs @@ -14,7 +14,6 @@ * limitations under the License. **/ use std::collections::BTreeMap; -use std::error::Error; use std::str::FromStr; use crate::instruction::{ @@ -147,9 +146,9 @@ impl Program { blocking, frame, .. }) => { if *blocking && include_blocked { - FrameMatchCondition::AnyOfQubits(frame.qubits) + FrameMatchCondition::AnyOfQubits(&frame.qubits) } else { - FrameMatchCondition::Specific(frame.clone()) + FrameMatchCondition::Specific(frame) } } Instruction::Delay(Delay { @@ -158,17 +157,21 @@ impl Program { .. }) => { if frame_names.is_empty() { - FrameMatchCondition::ExactQubits(qubits.clone()) + FrameMatchCondition::ExactQubits(qubits) } else { FrameMatchCondition::And(vec![ - FrameMatchCondition::ExactQubits(qubits.clone()), - FrameMatchCondition::AnyOfNames(frame_names.clone()), + FrameMatchCondition::ExactQubits(qubits), + FrameMatchCondition::AnyOfNames(frame_names), ]) } } Instruction::Fence(Fence { qubits }) => { if include_blocked { - FrameMatchCondition::AnyOfQubits(qubits.clone()) + if qubits.is_empty() { + FrameMatchCondition::All + } else { + FrameMatchCondition::AnyOfQubits(qubits) + } } else { return None; } @@ -178,12 +181,12 @@ impl Program { | Instruction::SetScale(SetScale { frame, .. }) | Instruction::ShiftFrequency(ShiftFrequency { frame, .. }) | Instruction::ShiftPhase(ShiftPhase { frame, .. }) => { - FrameMatchCondition::Specific(frame.clone()) + FrameMatchCondition::Specific(frame) } Instruction::SwapPhases(SwapPhases { frame_1, frame_2 }) => { FrameMatchCondition::And(vec![ - FrameMatchCondition::Specific(frame_1.clone()), - FrameMatchCondition::Specific(frame_2.clone()), + FrameMatchCondition::Specific(frame_1), + FrameMatchCondition::Specific(frame_2), ]) } Instruction::Gate(_) @@ -209,7 +212,12 @@ impl Program { | Instruction::JumpUnless(_) => return None, }; - Some(self.frames.get_matching_keys(condition).into_iter().collect()) + Some( + self.frames + .get_matching_keys(condition) + .into_iter() + .collect(), + ) } pub fn to_instructions(&self, include_headers: bool) -> Vec { @@ -267,7 +275,7 @@ impl FromStr for Program { #[cfg(test)] mod tests { - use std::str::FromStr; + use std::{str::FromStr, collections::HashSet}; use crate::instruction::Instruction; @@ -417,16 +425,16 @@ DEFFRAME 0 1 \"2q\": ("PULSE 2 \"a\" custom_waveform", vec![], vec![]), // Captures work identically to Pulses ( - "CAPTURE 0 \"a\" custom_waveform", + "CAPTURE 0 \"a\" custom_waveform ro[0]", vec!["0 \"a\""], vec!["0 \"a\"", "0 \"b\"", "0 1 \"2q\""], ), ( - "CAPTURE 1 \"c\" custom_waveform", + "CAPTURE 1 \"c\" custom_waveform ro[0]", vec!["1 \"c\""], vec!["1 \"c\"", "0 1 \"2q\""], ), - ("CAPTURE 2 \"a\" custom_waveform", vec![], vec![]), + ("CAPTURE 2 \"a\" custom_waveform ro[0]", vec![], vec![]), // A non-blocking pulse blocks only its precise frame, not other frames on the same qubits ( "NONBLOCKING PULSE 0 \"a\" custom_waveform", @@ -443,25 +451,53 @@ DEFFRAME 0 1 \"2q\": vec!["0 1 \"2q\""], vec!["0 1 \"2q\""], ), + ( + "FENCE 1", + vec![], + vec!["1 \"c\"", "0 1 \"2q\""], + ), + ( + "FENCE", + vec![], + vec!["0 \"a\"", "0 \"b\"", "1 \"c\"", "0 1 \"2q\""], + ), + ( + "DELAY 0 1.0", + vec!["0 \"a\"", "0 \"b\""], + vec!["0 \"a\"", "0 \"b\""], + ), + ( + "DELAY 1 1.0", + vec!["1 \"c\""], + vec!["1 \"c\""], + ), + ( + "DELAY 1 \"c\" 1.0", + vec!["1 \"c\""], + vec!["1 \"c\""], + ), ] { let instruction = Instruction::parse(instruction_string).unwrap(); - let used_frames: Vec = program + let used_frames: HashSet = program .get_frames_for_instruction(&instruction, false) - .unwrap() + .unwrap_or_default() .into_iter() .map(|f| f.to_string()) .collect(); + let expected_used_frames: HashSet = expected_used_frames.into_iter().map(|el| el.to_owned()).collect(); assert_eq!( used_frames, expected_used_frames, "Instruction {} *used* frames `{:?}` but we expected `{:?}", instruction, used_frames, expected_used_frames ); - let blocked_frames: Vec = program + + let blocked_frames: HashSet = program .get_frames_for_instruction(&instruction, true) .unwrap() .into_iter() .map(|f| f.to_string()) .collect(); + let expected_blocked_frames: HashSet = expected_blocked_frames.into_iter().map(|el| el.to_owned()).collect(); assert_eq!( blocked_frames, expected_blocked_frames, "Instruction {} *blocked* frames `{:?}` but we expected `{:?}", From c0ca7bc5428a75125a6b817b316d4551830ae06e Mon Sep 17 00:00:00 2001 From: kalzoo <22137047+kalzoo@users.noreply.github.com> Date: Tue, 31 May 2022 18:48:33 -0700 Subject: [PATCH 05/19] Chore: fmt --- src/instruction.rs | 8 ++++++-- src/program/mod.rs | 30 ++++++++++++------------------ 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/instruction.rs b/src/instruction.rs index 400c5bc9..8f37546a 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -17,7 +17,10 @@ use nom::combinator::all_consuming; use serde::{Deserialize, Serialize}; use std::{collections::HashMap, fmt}; -use crate::{expression::Expression, parser::{instruction::parse_instruction, lex}}; +use crate::{ + expression::Expression, + parser::{instruction::parse_instruction, lex}, +}; #[cfg(test)] use proptest_derive::Arbitrary; @@ -948,7 +951,8 @@ impl Instruction { /// or if there is input left over after parsing. pub fn parse(input: &str) -> Result { let lexed = lex(input)?; - let (_, instruction) = all_consuming(parse_instruction)(&lexed).map_err(|e| e.to_string())?; + let (_, instruction) = + all_consuming(parse_instruction)(&lexed).map_err(|e| e.to_string())?; Ok(instruction) } } diff --git a/src/program/mod.rs b/src/program/mod.rs index 069b8e18..05adbe72 100644 --- a/src/program/mod.rs +++ b/src/program/mod.rs @@ -275,7 +275,7 @@ impl FromStr for Program { #[cfg(test)] mod tests { - use std::{str::FromStr, collections::HashSet}; + use std::{collections::HashSet, str::FromStr}; use crate::instruction::Instruction; @@ -451,11 +451,7 @@ DEFFRAME 0 1 \"2q\": vec!["0 1 \"2q\""], vec!["0 1 \"2q\""], ), - ( - "FENCE 1", - vec![], - vec!["1 \"c\"", "0 1 \"2q\""], - ), + ("FENCE 1", vec![], vec!["1 \"c\"", "0 1 \"2q\""]), ( "FENCE", vec![], @@ -466,16 +462,8 @@ DEFFRAME 0 1 \"2q\": vec!["0 \"a\"", "0 \"b\""], vec!["0 \"a\"", "0 \"b\""], ), - ( - "DELAY 1 1.0", - vec!["1 \"c\""], - vec!["1 \"c\""], - ), - ( - "DELAY 1 \"c\" 1.0", - vec!["1 \"c\""], - vec!["1 \"c\""], - ), + ("DELAY 1 1.0", vec!["1 \"c\""], vec!["1 \"c\""]), + ("DELAY 1 \"c\" 1.0", vec!["1 \"c\""], vec!["1 \"c\""]), ] { let instruction = Instruction::parse(instruction_string).unwrap(); let used_frames: HashSet = program @@ -484,7 +472,10 @@ DEFFRAME 0 1 \"2q\": .into_iter() .map(|f| f.to_string()) .collect(); - let expected_used_frames: HashSet = expected_used_frames.into_iter().map(|el| el.to_owned()).collect(); + let expected_used_frames: HashSet = expected_used_frames + .into_iter() + .map(|el| el.to_owned()) + .collect(); assert_eq!( used_frames, expected_used_frames, "Instruction {} *used* frames `{:?}` but we expected `{:?}", @@ -497,7 +488,10 @@ DEFFRAME 0 1 \"2q\": .into_iter() .map(|f| f.to_string()) .collect(); - let expected_blocked_frames: HashSet = expected_blocked_frames.into_iter().map(|el| el.to_owned()).collect(); + let expected_blocked_frames: HashSet = expected_blocked_frames + .into_iter() + .map(|el| el.to_owned()) + .collect(); assert_eq!( blocked_frames, expected_blocked_frames, "Instruction {} *blocked* frames `{:?}` but we expected `{:?}", From 18f2cce1521887173cb6cca3440b91c8b7b8b114 Mon Sep 17 00:00:00 2001 From: kalzoo <22137047+kalzoo@users.noreply.github.com> Date: Tue, 31 May 2022 19:18:47 -0700 Subject: [PATCH 06/19] Fix visibility of Instruction::parse --- src/instruction.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/instruction.rs b/src/instruction.rs index 8f37546a..69fff0f0 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -947,9 +947,10 @@ impl Instruction { } } + #[cfg(test)] /// Parse a single instruction from an input string. Returns an error if the input fails to parse, /// or if there is input left over after parsing. - pub fn parse(input: &str) -> Result { + pub(crate) fn parse(input: &str) -> Result { let lexed = lex(input)?; let (_, instruction) = all_consuming(parse_instruction)(&lexed).map_err(|e| e.to_string())?; From f770f5a291d68e5310083370814171508f4b03f2 Mon Sep 17 00:00:00 2001 From: kalzoo <22137047+kalzoo@users.noreply.github.com> Date: Tue, 31 May 2022 19:19:45 -0700 Subject: [PATCH 07/19] Fix: frame dependency calculation in ScheduledProgram --- src/program/graph.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/program/graph.rs b/src/program/graph.rs index b1e7d242..33278803 100644 --- a/src/program/graph.rs +++ b/src/program/graph.rs @@ -234,19 +234,29 @@ impl InstructionBlock { Ok(()) } InstructionRole::RFControl => { - let frames = match program.get_frames_for_instruction(instruction, true) { + let used_frames = match program.get_frames_for_instruction(instruction, false) { + Some(frames) => frames, + None => vec![], + }; + let blocked_frames = match program.get_frames_for_instruction(instruction, true) { Some(frames) => frames, None => vec![], }; - // Mark a dependency on the last instruction which executed in the context of each target frame - for frame in frames { + // Take a dependency on any previous instructions to _block_ or _use_ a frame which this instruction _uses_. + for frame in used_frames { let previous_node_id = last_instruction_by_frame .entry(frame.clone()) .or_insert(ScheduledGraphNode::BlockStart); add_dependency!(graph, *previous_node_id => node, ExecutionDependency::ReferenceFrame); last_instruction_by_frame.insert(frame.clone(), node); } + + // We mark all "blocked" frames as such for later instructions to take a dependency on + for frame in blocked_frames { + last_instruction_by_frame.insert(frame.clone(), node); + } + Ok(()) } InstructionRole::ControlFlow => Err(ScheduleError { From 000bf2a5783b350b71584022f3e0fcff8d4aaf6a Mon Sep 17 00:00:00 2001 From: kalzoo <22137047+kalzoo@users.noreply.github.com> Date: Tue, 31 May 2022 19:20:53 -0700 Subject: [PATCH 08/19] Fix how FENCE uses frames --- src/program/mod.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/program/mod.rs b/src/program/mod.rs index 05adbe72..486eb4ab 100644 --- a/src/program/mod.rs +++ b/src/program/mod.rs @@ -166,14 +166,10 @@ impl Program { } } Instruction::Fence(Fence { qubits }) => { - if include_blocked { - if qubits.is_empty() { - FrameMatchCondition::All - } else { - FrameMatchCondition::AnyOfQubits(qubits) - } + if qubits.is_empty() { + FrameMatchCondition::All } else { - return None; + FrameMatchCondition::AnyOfQubits(qubits) } } Instruction::SetFrequency(SetFrequency { frame, .. }) From f5598684761ef31ce7feb5992bd40fc46f5170dd Mon Sep 17 00:00:00 2001 From: kalzoo <22137047+kalzoo@users.noreply.github.com> Date: Tue, 31 May 2022 19:21:15 -0700 Subject: [PATCH 09/19] Chore: fix tests for new frame-blocking behavior --- src/program/graphviz_dot.rs | 10 +++++++ src/program/mod.rs | 8 ++++-- ...sts__graph__different_frames_blocking.snap | 6 +++-- ...hviz_dot__tests__graph__fence_wrapper.snap | 27 +++++++++++++++++++ ...rametric_pulses_using_capture_results.snap | 10 +++---- 5 files changed, 51 insertions(+), 10 deletions(-) create mode 100644 src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__fence_wrapper.snap diff --git a/src/program/graphviz_dot.rs b/src/program/graphviz_dot.rs index f66b878d..b916e099 100644 --- a/src/program/graphviz_dot.rs +++ b/src/program/graphviz_dot.rs @@ -234,6 +234,8 @@ DEFFRAME 0 \"ro_rx\": INITIAL-FREQUENCY: 1e6 DEFFRAME 0 \"ro_tx\": INITIAL-FREQUENCY: 1e6 +DEFFRAME 0 1 \"cz\": + INITIAL-FREQUENCY: 1e6 "; let program = @@ -309,8 +311,16 @@ NONBLOCKING PULSE 0 \"rf\" test(duration: 1e6) NONBLOCKING PULSE 1 \"rf\" test(duration: 1e6) " ); + build_dot_format_snapshot_test_case!(fence_all, "FENCE"); + build_dot_format_snapshot_test_case!(fence_wrapper, " +FENCE +NONBLOCKING PULSE 0 1 \"cz\" test(duration: 1e-6) +NONBLOCKING PULSE 1 \"rf\" test(duration: 1e-6) +FENCE 1 +"); + build_dot_format_snapshot_test_case!( jump, "DECLARE ro BIT diff --git a/src/program/mod.rs b/src/program/mod.rs index 486eb4ab..dbaec3e4 100644 --- a/src/program/mod.rs +++ b/src/program/mod.rs @@ -447,10 +447,14 @@ DEFFRAME 0 1 \"2q\": vec!["0 1 \"2q\""], vec!["0 1 \"2q\""], ), - ("FENCE 1", vec![], vec!["1 \"c\"", "0 1 \"2q\""]), + ( + "FENCE 1", + vec!["1 \"c\"", "0 1 \"2q\""], + vec!["1 \"c\"", "0 1 \"2q\""], + ), ( "FENCE", - vec![], + vec!["0 \"a\"", "0 \"b\"", "1 \"c\"", "0 1 \"2q\""], vec!["0 \"a\"", "0 \"b\"", "1 \"c\"", "0 1 \"2q\""], ), ( diff --git a/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__different_frames_blocking.snap b/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__different_frames_blocking.snap index 5029b958..a57b2777 100644 --- a/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__different_frames_blocking.snap +++ b/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__different_frames_blocking.snap @@ -10,11 +10,13 @@ digraph { node [style="filled"]; "block_0_start" [shape=circle, label="start"]; "block_0_start" -> "block_0_0" [label="frame"]; + "block_0_start" -> "block_0_1" [label="frame"]; + "block_0_start" -> "block_0_2" [label="frame"]; "block_0_start" -> "block_0_end" [label="ordering"]; "block_0_0" [shape=rectangle, label="[0] PULSE 0 \"rf\" test(duration: 1000000)"]; - "block_0_0" -> "block_0_1" [label="frame"]; + "block_0_0" -> "block_0_end" [label="frame"]; "block_0_1" [shape=rectangle, label="[1] PULSE 1 \"rf\" test(duration: 1000000)"]; - "block_0_1" -> "block_0_2" [label="frame"]; + "block_0_1" -> "block_0_end" [label="frame"]; "block_0_2" [shape=rectangle, label="[2] PULSE 2 \"rf\" test(duration: 1000000)"]; "block_0_2" -> "block_0_end" [label="frame"]; "block_0_end" [shape=circle, label="end"]; diff --git a/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__fence_wrapper.snap b/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__fence_wrapper.snap new file mode 100644 index 00000000..a67b6d58 --- /dev/null +++ b/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__fence_wrapper.snap @@ -0,0 +1,27 @@ +--- +source: src/program/graphviz_dot.rs +expression: dot_format +--- +digraph { + entry -> "block_0_start"; + entry [label="Entry Point"]; + subgraph cluster_0 { + label="block_0"; + node [style="filled"]; + "block_0_start" [shape=circle, label="start"]; + "block_0_start" -> "block_0_0" [label="frame"]; + "block_0_start" -> "block_0_end" [label="ordering"]; + "block_0_0" [shape=rectangle, label="[0] FENCE"]; + "block_0_0" -> "block_0_1" [label="frame"]; + "block_0_0" -> "block_0_2" [label="frame"]; + "block_0_0" -> "block_0_end" [label="frame"]; + "block_0_1" [shape=rectangle, label="[1] NONBLOCKING PULSE 0 1 \"cz\" test(duration: 0.000001)"]; + "block_0_1" -> "block_0_3" [label="frame"]; + "block_0_2" [shape=rectangle, label="[2] NONBLOCKING PULSE 1 \"rf\" test(duration: 0.000001)"]; + "block_0_2" -> "block_0_3" [label="frame"]; + "block_0_3" [shape=rectangle, label="[3] FENCE 1"]; + "block_0_3" -> "block_0_end" [label="frame"]; + "block_0_end" [shape=circle, label="end"]; + } +} + diff --git a/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__parametric_pulses_using_capture_results.snap b/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__parametric_pulses_using_capture_results.snap index 5330e3de..79060814 100644 --- a/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__parametric_pulses_using_capture_results.snap +++ b/src/program/snapshots/quil_rs__program__graphviz_dot__tests__graph__parametric_pulses_using_capture_results.snap @@ -10,23 +10,21 @@ digraph { node [style="filled"]; "block_0_start" [shape=circle, label="start"]; "block_0_start" -> "block_0_0" [label="frame"]; + "block_0_start" -> "block_0_2" [label="frame"]; "block_0_start" -> "block_0_end" [label="ordering"]; "block_0_0" [shape=rectangle, label="[0] CAPTURE 0 \"ro_rx\" test(a: param[0]) ro[0]"]; "block_0_0" -> "block_0_1" [label="await capture frame"]; - "block_0_0" -> "block_0_2" [label="frame"]; "block_0_0" -> "block_0_3" [label="frame"]; "block_0_0" -> "block_0_end" [label="await read"]; "block_0_1" [shape=rectangle, label="[1] NONBLOCKING PULSE 0 \"rf\" test(a: ro[0])"]; - "block_0_1" -> "block_0_3" [label="await read -frame"]; + "block_0_1" -> "block_0_3" [label="await read"]; "block_0_2" [shape=rectangle, label="[2] NONBLOCKING PULSE 1 \"rf\" test(a: ro[0])"]; - "block_0_2" -> "block_0_3" [label="await read -frame"]; + "block_0_2" -> "block_0_3" [label="await read"]; + "block_0_2" -> "block_0_5" [label="frame"]; "block_0_3" [shape=rectangle, label="[3] CAPTURE 0 \"ro_rx\" test(a: param[0]) ro[0]"]; "block_0_3" -> "block_0_4" [label="await capture frame"]; - "block_0_3" -> "block_0_5" [label="frame"]; "block_0_3" -> "block_0_end" [label="await read frame"]; "block_0_4" [shape=rectangle, label="[4] NONBLOCKING PULSE 0 \"rf\" test(a: ro[0])"]; From 2ba1d1b4951475aa493654cf94df93162782e345 Mon Sep 17 00:00:00 2001 From: kalzoo <22137047+kalzoo@users.noreply.github.com> Date: Tue, 31 May 2022 19:24:02 -0700 Subject: [PATCH 10/19] Chore: linter fixes --- src/instruction.rs | 6 +++--- src/program/frame.rs | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/instruction.rs b/src/instruction.rs index 69fff0f0..f4281e3d 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -13,13 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. **/ -use nom::combinator::all_consuming; use serde::{Deserialize, Serialize}; use std::{collections::HashMap, fmt}; use crate::{ expression::Expression, - parser::{instruction::parse_instruction, lex}, }; #[cfg(test)] @@ -951,9 +949,11 @@ impl Instruction { /// Parse a single instruction from an input string. Returns an error if the input fails to parse, /// or if there is input left over after parsing. pub(crate) fn parse(input: &str) -> Result { + use crate::parser::{instruction::parse_instruction, lex}; + let lexed = lex(input)?; let (_, instruction) = - all_consuming(parse_instruction)(&lexed).map_err(|e| e.to_string())?; + nom::combinator::all_consuming(parse_instruction)(&lexed).map_err(|e| e.to_string())?; Ok(instruction) } } diff --git a/src/program/frame.rs b/src/program/frame.rs index 4ef6d104..0599b68c 100644 --- a/src/program/frame.rs +++ b/src/program/frame.rs @@ -155,5 +155,6 @@ pub(crate) enum FrameMatchCondition<'a> { And(Vec>), /// Return all frames which match any of these conditions + #[allow(dead_code)] Or(Vec>), } From 48d5751049f159ab412475d0e570e1c4c2a1e8e1 Mon Sep 17 00:00:00 2001 From: kalzoo <22137047+kalzoo@users.noreply.github.com> Date: Tue, 31 May 2022 19:24:11 -0700 Subject: [PATCH 11/19] Chore: fmt --- src/instruction.rs | 6 ++---- src/program/graph.rs | 3 ++- src/program/graphviz_dot.rs | 7 +++++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/instruction.rs b/src/instruction.rs index f4281e3d..9a125f5a 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -16,9 +16,7 @@ use serde::{Deserialize, Serialize}; use std::{collections::HashMap, fmt}; -use crate::{ - expression::Expression, -}; +use crate::expression::Expression; #[cfg(test)] use proptest_derive::Arbitrary; @@ -950,7 +948,7 @@ impl Instruction { /// or if there is input left over after parsing. pub(crate) fn parse(input: &str) -> Result { use crate::parser::{instruction::parse_instruction, lex}; - + let lexed = lex(input)?; let (_, instruction) = nom::combinator::all_consuming(parse_instruction)(&lexed).map_err(|e| e.to_string())?; diff --git a/src/program/graph.rs b/src/program/graph.rs index 33278803..86a0ec08 100644 --- a/src/program/graph.rs +++ b/src/program/graph.rs @@ -238,7 +238,8 @@ impl InstructionBlock { Some(frames) => frames, None => vec![], }; - let blocked_frames = match program.get_frames_for_instruction(instruction, true) { + let blocked_frames = match program.get_frames_for_instruction(instruction, true) + { Some(frames) => frames, None => vec![], }; diff --git a/src/program/graphviz_dot.rs b/src/program/graphviz_dot.rs index b916e099..bd7f27a9 100644 --- a/src/program/graphviz_dot.rs +++ b/src/program/graphviz_dot.rs @@ -314,12 +314,15 @@ NONBLOCKING PULSE 1 \"rf\" test(duration: 1e6) build_dot_format_snapshot_test_case!(fence_all, "FENCE"); - build_dot_format_snapshot_test_case!(fence_wrapper, " + build_dot_format_snapshot_test_case!( + fence_wrapper, + " FENCE NONBLOCKING PULSE 0 1 \"cz\" test(duration: 1e-6) NONBLOCKING PULSE 1 \"rf\" test(duration: 1e-6) FENCE 1 -"); +" + ); build_dot_format_snapshot_test_case!( jump, From b3958b8c757f27a791b40f0a9155747d68d49bb0 Mon Sep 17 00:00:00 2001 From: kalzoo <22137047+kalzoo@users.noreply.github.com> Date: Tue, 31 May 2022 19:42:25 -0700 Subject: [PATCH 12/19] Chore: linter fixes & inline doc --- src/program/frame.rs | 44 ++++++++++++++++++-------------------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/src/program/frame.rs b/src/program/frame.rs index 0599b68c..51ff3b73 100644 --- a/src/program/frame.rs +++ b/src/program/frame.rs @@ -62,37 +62,29 @@ impl FrameSet { .collect() } FrameMatchCondition::Specific(frame) => { - if let Some((frame, _)) = self.frames.get_key_value(&frame) { + // This unusual pattern (fetch key & value by key, discard value) allows us to return + // a reference to `self` rather than `condition`, keeping lifetimes simpler. + if let Some((frame, _)) = self.frames.get_key_value(frame) { vec![frame].into_iter().collect() } else { HashSet::new() } } - FrameMatchCondition::And(conditions) => { - let individual_sets: Vec> = conditions - .into_iter() - .map(|c| self.get_matching_keys(c)) - .collect(); - individual_sets - .into_iter() - .reduce(|acc, el| acc.into_iter().filter(|&v| el.contains(v)).collect()) - .unwrap_or_default() - } - FrameMatchCondition::Or(conditions) => { - let individual_sets: Vec> = conditions - .into_iter() - .map(|c| self.get_matching_keys(c)) - .collect(); - individual_sets - .into_iter() - .reduce(|mut acc, el| { - el.into_iter().for_each(|v| { - acc.insert(v); - }); - acc - }) - .unwrap_or_default() - } + FrameMatchCondition::And(conditions) => conditions + .into_iter() + .map(|c| self.get_matching_keys(c)) + .reduce(|acc, el| acc.into_iter().filter(|&v| el.contains(v)).collect()) + .unwrap_or_default(), + FrameMatchCondition::Or(conditions) => conditions + .into_iter() + .map(|c| self.get_matching_keys(c)) + .reduce(|mut acc, el| { + el.into_iter().for_each(|v| { + acc.insert(v); + }); + acc + }) + .unwrap_or_default(), } } From 68a2fde877d8b3836bbeb6d1325504fa0478510b Mon Sep 17 00:00:00 2001 From: kalzoo <22137047+kalzoo@users.noreply.github.com> Date: Wed, 1 Jun 2022 12:32:20 -0700 Subject: [PATCH 13/19] Chore: code cleanup --- src/program/graph.rs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/program/graph.rs b/src/program/graph.rs index 16a7b5f2..30a13b11 100644 --- a/src/program/graph.rs +++ b/src/program/graph.rs @@ -233,15 +233,12 @@ impl InstructionBlock { Ok(()) } InstructionRole::RFControl => { - let used_frames = match program.get_frames_for_instruction(instruction, false) { - Some(frames) => frames, - None => vec![], - }; - let blocked_frames = match program.get_frames_for_instruction(instruction, true) - { - Some(frames) => frames, - None => vec![], - }; + let used_frames = program + .get_frames_for_instruction(instruction, false) + .unwrap_or_default(); + let blocked_frames = program + .get_frames_for_instruction(instruction, true) + .unwrap_or_default(); // Take a dependency on any previous instructions to _block_ or _use_ a frame which this instruction _uses_. for frame in used_frames { From 67a74aa8c818b971543644679b88318186f889a1 Mon Sep 17 00:00:00 2001 From: kalzoo <22137047+kalzoo@users.noreply.github.com> Date: Wed, 1 Jun 2022 12:55:17 -0700 Subject: [PATCH 14/19] Fix bug in DELAY frame utilization --- src/program/frame.rs | 4 ++-- src/program/mod.rs | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/program/frame.rs b/src/program/frame.rs index d962308e..bec3ecfe 100644 --- a/src/program/frame.rs +++ b/src/program/frame.rs @@ -55,9 +55,9 @@ impl FrameSet { .collect() } FrameMatchCondition::ExactQubits(qubits) => { - let all_of_set: HashSet<_> = qubits.iter().collect(); + let exact_set: HashSet<_> = qubits.iter().collect(); - keys.filter(|&f| f.qubits.iter().all(|q| all_of_set.contains(q))) + keys.filter(|&f| f.qubits.iter().collect::>() == exact_set) .collect() } FrameMatchCondition::Specific(frame) => { diff --git a/src/program/mod.rs b/src/program/mod.rs index 37fe498c..254af692 100644 --- a/src/program/mod.rs +++ b/src/program/mod.rs @@ -461,8 +461,7 @@ DEFFRAME 0 1 \"2q\": vec!["0 \"a\"", "0 \"b\""], vec!["0 \"a\"", "0 \"b\""], ), - ("DELAY 1 1.0", vec!["1 \"c\""], vec!["1 \"c\""]), - ("DELAY 1 \"c\" 1.0", vec!["1 \"c\""], vec!["1 \"c\""]), + (r#"DELAY 0 1 1.0"#, vec![r#"0 1 "2q""#], vec![r#"0 1 "2q""#]), ] { let instruction = Instruction::parse(instruction_string).unwrap(); let used_frames: HashSet = program From 41249bcfc6d08e52556e263adfabb6b2620b7d9b Mon Sep 17 00:00:00 2001 From: kalzoo <22137047+kalzoo@users.noreply.github.com> Date: Wed, 1 Jun 2022 12:55:42 -0700 Subject: [PATCH 15/19] Chore: remove dead code --- src/program/frame.rs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/program/frame.rs b/src/program/frame.rs index bec3ecfe..d2089e7e 100644 --- a/src/program/frame.rs +++ b/src/program/frame.rs @@ -74,16 +74,6 @@ impl FrameSet { .map(|c| self.get_matching_keys(c)) .reduce(|acc, el| acc.into_iter().filter(|&v| el.contains(v)).collect()) .unwrap_or_default(), - FrameMatchCondition::Or(conditions) => conditions - .into_iter() - .map(|c| self.get_matching_keys(c)) - .reduce(|mut acc, el| { - el.into_iter().for_each(|v| { - acc.insert(v); - }); - acc - }) - .unwrap_or_default(), } } @@ -144,8 +134,4 @@ pub(crate) enum FrameMatchCondition<'a> { /// Return all frames which match all of these conditions And(Vec>), - - /// Return all frames which match any of these conditions - #[allow(dead_code)] - Or(Vec>), } From 3cacfcfd7d6559b7de8a63c932441bdca16d4dca Mon Sep 17 00:00:00 2001 From: kalzoo <22137047+kalzoo@users.noreply.github.com> Date: Wed, 1 Jun 2022 12:55:58 -0700 Subject: [PATCH 16/19] Chore: fix docstring --- src/program/frame.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/program/frame.rs b/src/program/frame.rs index d2089e7e..53bfb8ef 100644 --- a/src/program/frame.rs +++ b/src/program/frame.rs @@ -120,7 +120,7 @@ pub(crate) enum FrameMatchCondition<'a> { /// Match all frames in the set All, - /// Match all frames which shares any one of these names + /// Match all frames which share any one of these names AnyOfNames(&'a [String]), /// Match all frames which contain any of these qubits From 2c866485e5ab27e50cf93a982e7fe6df41d0becf Mon Sep 17 00:00:00 2001 From: kalzoo <22137047+kalzoo@users.noreply.github.com> Date: Wed, 1 Jun 2022 12:56:25 -0700 Subject: [PATCH 17/19] Chore: make test cases more readable --- src/program/mod.rs | 87 +++++++++++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 35 deletions(-) diff --git a/src/program/mod.rs b/src/program/mod.rs index 254af692..033eadee 100644 --- a/src/program/mod.rs +++ b/src/program/mod.rs @@ -405,62 +405,79 @@ DEFFRAME 0 1 \"2q\": let program = Program::from_str(input).unwrap(); for (instruction_string, expected_used_frames, expected_blocked_frames) in vec![ - // Blocking pulses block their qubits + // Blocking pulses use only the specified frame but block frames intersecting the frame's qubits ( - "PULSE 0 \"a\" custom_waveform", - vec!["0 \"a\""], - vec!["0 \"a\"", "0 \"b\"", "0 1 \"2q\""], + r#"PULSE 0 "a" custom_waveform"#, + vec![r#"0 "a""#], + vec![r#"0 "a""#, r#"0 "b""#, r#"0 1 "2q""#], ), ( - "PULSE 1 \"c\" custom_waveform", - vec!["1 \"c\""], - vec!["1 \"c\"", "0 1 \"2q\""], + r#"PULSE 1 "c" custom_waveform"#, + vec![r#"1 "c""#], + vec![r#"1 "c""#, r#"0 1 "2q""#], ), // Pulses on non-declared frames and unused qubits do not use or block any frames in the program - ("PULSE 2 \"a\" custom_waveform", vec![], vec![]), + (r#"PULSE 2 "a" custom_waveform"#, vec![], vec![]), // Captures work identically to Pulses ( - "CAPTURE 0 \"a\" custom_waveform ro[0]", - vec!["0 \"a\""], - vec!["0 \"a\"", "0 \"b\"", "0 1 \"2q\""], + r#"CAPTURE 0 "a" custom_waveform ro[0]"#, + vec![r#"0 "a""#], + vec![r#"0 "a""#, r#"0 "b""#, r#"0 1 "2q""#], ), ( - "CAPTURE 1 \"c\" custom_waveform ro[0]", - vec!["1 \"c\""], - vec!["1 \"c\"", "0 1 \"2q\""], + r#"CAPTURE 1 "c" custom_waveform ro[0]"#, + vec![r#"1 "c""#], + vec![r#"1 "c""#, r#"0 1 "2q""#], ), - ("CAPTURE 2 \"a\" custom_waveform ro[0]", vec![], vec![]), + (r#"CAPTURE 2 "a" custom_waveform ro[0]"#, vec![], vec![]), + // Raw Captures work identically to Pulses + ( + r#"RAW-CAPTURE 0 "a" 1e-6 ro[0]"#, + vec![r#"0 "a""#], + vec![r#"0 "a""#, r#"0 "b""#, r#"0 1 "2q""#], + ), + ( + r#"RAW-CAPTURE 1 "c" 1e-6 ro[0]"#, + vec![r#"1 "c""#], + vec![r#"1 "c""#, r#"0 1 "2q""#], + ), + (r#"RAW-CAPTURE 2 "a" 1e-6 ro[0]"#, vec![], vec![]), // A non-blocking pulse blocks only its precise frame, not other frames on the same qubits ( - "NONBLOCKING PULSE 0 \"a\" custom_waveform", - vec!["0 \"a\""], - vec!["0 \"a\""], + r#"NONBLOCKING PULSE 0 "a" custom_waveform"#, + vec![r#"0 "a""#], + vec![r#"0 "a""#], ), ( - "NONBLOCKING PULSE 1 \"c\" custom_waveform", - vec!["1 \"c\""], - vec!["1 \"c\""], + r#"NONBLOCKING PULSE 1 "c" custom_waveform"#, + vec![r#"1 "c""#], + vec![r#"1 "c""#], ), ( - "NONBLOCKING PULSE 0 1 \"2q\" custom_waveform", - vec!["0 1 \"2q\""], - vec!["0 1 \"2q\""], + r#"NONBLOCKING PULSE 0 1 "2q" custom_waveform"#, + vec![r#"0 1 "2q""#], + vec![r#"0 1 "2q""#], ), + // A Fence with qubits specified uses and blocks all frames intersecting that qubit ( - "FENCE 1", - vec!["1 \"c\"", "0 1 \"2q\""], - vec!["1 \"c\"", "0 1 \"2q\""], + r#"FENCE 1"#, + vec![r#"1 "c""#, r#"0 1 "2q""#], + vec![r#"1 "c""#, r#"0 1 "2q""#], ), + // Fence-all uses and blocks all frames declared in the program ( - "FENCE", - vec!["0 \"a\"", "0 \"b\"", "1 \"c\"", "0 1 \"2q\""], - vec!["0 \"a\"", "0 \"b\"", "1 \"c\"", "0 1 \"2q\""], + r#"FENCE"#, + vec![r#"0 "a""#, r#"0 "b""#, r#"1 "c""#, r#"0 1 "2q""#], + vec![r#"0 "a""#, r#"0 "b""#, r#"1 "c""#, r#"0 1 "2q""#], ), + // Delay uses and blocks frames on exactly the given qubits and with any of the given names ( - "DELAY 0 1.0", - vec!["0 \"a\"", "0 \"b\""], - vec!["0 \"a\"", "0 \"b\""], + r#"DELAY 0 1.0"#, + vec![r#"0 "a""#, r#"0 "b""#], + vec![r#"0 "a""#, r#"0 "b""#], ), + (r#"DELAY 1 1.0"#, vec![r#"1 "c""#], vec![r#"1 "c""#]), + (r#"DELAY 1 "c" 1.0"#, vec![r#"1 "c""#], vec![r#"1 "c""#]), (r#"DELAY 0 1 1.0"#, vec![r#"0 1 "2q""#], vec![r#"0 1 "2q""#]), ] { let instruction = Instruction::parse(instruction_string).unwrap(); @@ -476,7 +493,7 @@ DEFFRAME 0 1 \"2q\": .collect(); assert_eq!( used_frames, expected_used_frames, - "Instruction {} *used* frames `{:?}` but we expected `{:?}", + "Instruction {} *used* frames `{:?}` but we expected `{:?}`", instruction, used_frames, expected_used_frames ); @@ -492,7 +509,7 @@ DEFFRAME 0 1 \"2q\": .collect(); assert_eq!( blocked_frames, expected_blocked_frames, - "Instruction {} *blocked* frames `{:?}` but we expected `{:?}", + "Instruction {} *blocked* frames `{:?}` but we expected `{:?}`", instruction, blocked_frames, expected_blocked_frames ); } From 98b903a5b58e0685d0a1a59f419948a98e41b0cd Mon Sep 17 00:00:00 2001 From: kalzoo <22137047+kalzoo@users.noreply.github.com> Date: Wed, 1 Jun 2022 16:17:16 -0700 Subject: [PATCH 18/19] Simplify frame dependency code --- src/program/graph.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/program/graph.rs b/src/program/graph.rs index 30a13b11..2734be68 100644 --- a/src/program/graph.rs +++ b/src/program/graph.rs @@ -240,13 +240,12 @@ impl InstructionBlock { .get_frames_for_instruction(instruction, true) .unwrap_or_default(); - // Take a dependency on any previous instructions to _block_ or _use_ a frame which this instruction _uses_. + // Take a dependency on any previous instructions to _block_ a frame which this instruction _uses_. for frame in used_frames { let previous_node_id = last_instruction_by_frame - .entry(frame.clone()) - .or_insert(ScheduledGraphNode::BlockStart); + .get(frame) + .unwrap_or(&ScheduledGraphNode::BlockStart); add_dependency!(graph, *previous_node_id => node, ExecutionDependency::ReferenceFrame); - last_instruction_by_frame.insert(frame.clone(), node); } // We mark all "blocked" frames as such for later instructions to take a dependency on From 4cabba830bd48ce7af1a2a6b5e6892bc97376197 Mon Sep 17 00:00:00 2001 From: kalzoo <22137047+kalzoo@users.noreply.github.com> Date: Wed, 1 Jun 2022 16:20:58 -0700 Subject: [PATCH 19/19] Add Instruction.get_frame_match_condition --- src/instruction.rs | 73 ++++++++++++++++++++++++++++++++++++ src/program/mod.rs | 92 +++++----------------------------------------- 2 files changed, 82 insertions(+), 83 deletions(-) diff --git a/src/instruction.rs b/src/instruction.rs index 15692f94..d2c56b8d 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -16,6 +16,7 @@ use serde::{Deserialize, Serialize}; use std::{collections::HashMap, fmt}; use crate::expression::Expression; +use crate::program::frame::FrameMatchCondition; #[cfg(test)] use proptest_derive::Arbitrary; @@ -942,6 +943,78 @@ impl Instruction { } } + pub(crate) fn get_frame_match_condition( + &self, + include_blocked: bool, + ) -> Option { + match self { + Instruction::Pulse(Pulse { + blocking, frame, .. + }) + | Instruction::Capture(Capture { + blocking, frame, .. + }) + | Instruction::RawCapture(RawCapture { + blocking, frame, .. + }) => Some(if *blocking && include_blocked { + FrameMatchCondition::AnyOfQubits(&frame.qubits) + } else { + FrameMatchCondition::Specific(frame) + }), + Instruction::Delay(Delay { + frame_names, + qubits, + .. + }) => Some(if frame_names.is_empty() { + FrameMatchCondition::ExactQubits(qubits) + } else { + FrameMatchCondition::And(vec![ + FrameMatchCondition::ExactQubits(qubits), + FrameMatchCondition::AnyOfNames(frame_names), + ]) + }), + Instruction::Fence(Fence { qubits }) => Some(if qubits.is_empty() { + FrameMatchCondition::All + } else { + FrameMatchCondition::AnyOfQubits(qubits) + }), + Instruction::SetFrequency(SetFrequency { frame, .. }) + | Instruction::SetPhase(SetPhase { frame, .. }) + | Instruction::SetScale(SetScale { frame, .. }) + | Instruction::ShiftFrequency(ShiftFrequency { frame, .. }) + | Instruction::ShiftPhase(ShiftPhase { frame, .. }) => { + Some(FrameMatchCondition::Specific(frame)) + } + Instruction::SwapPhases(SwapPhases { frame_1, frame_2 }) => { + Some(FrameMatchCondition::And(vec![ + FrameMatchCondition::Specific(frame_1), + FrameMatchCondition::Specific(frame_2), + ])) + } + Instruction::Gate(_) + | Instruction::CircuitDefinition(_) + | Instruction::GateDefinition(_) + | Instruction::Declaration(_) + | Instruction::Measurement(_) + | Instruction::Reset(_) + | Instruction::CalibrationDefinition(_) + | Instruction::FrameDefinition(_) + | Instruction::MeasureCalibrationDefinition(_) + | Instruction::Pragma(_) + | Instruction::WaveformDefinition(_) + | Instruction::Arithmetic(_) + | Instruction::Halt + | Instruction::Label(_) + | Instruction::Move(_) + | Instruction::Exchange(_) + | Instruction::Load(_) + | Instruction::Store(_) + | Instruction::Jump(_) + | Instruction::JumpWhen(_) + | Instruction::JumpUnless(_) => None, + } + } + #[cfg(test)] /// Parse a single instruction from an input string. Returns an error if the input fails to parse, /// or if there is input left over after parsing. diff --git a/src/program/mod.rs b/src/program/mod.rs index 033eadee..e18644cf 100644 --- a/src/program/mod.rs +++ b/src/program/mod.rs @@ -16,20 +16,17 @@ use std::collections::BTreeMap; use std::str::FromStr; use crate::instruction::{ - Capture, Declaration, Delay, Fence, FrameDefinition, FrameIdentifier, Instruction, Pulse, - RawCapture, SetFrequency, SetPhase, SetScale, ShiftFrequency, ShiftPhase, SwapPhases, Waveform, - WaveformDefinition, + Declaration, FrameDefinition, FrameIdentifier, Instruction, Waveform, WaveformDefinition, }; use crate::parser::{lex, parse_instructions}; pub use self::calibration::CalibrationSet; -use self::frame::FrameMatchCondition; pub use self::frame::FrameSet; pub use self::memory::MemoryRegion; mod calibration; mod error; -mod frame; +pub(crate) mod frame; pub mod graph; mod memory; @@ -134,85 +131,14 @@ impl Program { instruction: &'a Instruction, include_blocked: bool, ) -> Option> { - let condition = match &instruction { - Instruction::Pulse(Pulse { - blocking, frame, .. + instruction + .get_frame_match_condition(include_blocked) + .map(|condition| { + self.frames + .get_matching_keys(condition) + .into_iter() + .collect() }) - | Instruction::Capture(Capture { - blocking, frame, .. - }) - | Instruction::RawCapture(RawCapture { - blocking, frame, .. - }) => { - if *blocking && include_blocked { - FrameMatchCondition::AnyOfQubits(&frame.qubits) - } else { - FrameMatchCondition::Specific(frame) - } - } - Instruction::Delay(Delay { - frame_names, - qubits, - .. - }) => { - if frame_names.is_empty() { - FrameMatchCondition::ExactQubits(qubits) - } else { - FrameMatchCondition::And(vec![ - FrameMatchCondition::ExactQubits(qubits), - FrameMatchCondition::AnyOfNames(frame_names), - ]) - } - } - Instruction::Fence(Fence { qubits }) => { - if qubits.is_empty() { - FrameMatchCondition::All - } else { - FrameMatchCondition::AnyOfQubits(qubits) - } - } - Instruction::SetFrequency(SetFrequency { frame, .. }) - | Instruction::SetPhase(SetPhase { frame, .. }) - | Instruction::SetScale(SetScale { frame, .. }) - | Instruction::ShiftFrequency(ShiftFrequency { frame, .. }) - | Instruction::ShiftPhase(ShiftPhase { frame, .. }) => { - FrameMatchCondition::Specific(frame) - } - Instruction::SwapPhases(SwapPhases { frame_1, frame_2 }) => { - FrameMatchCondition::And(vec![ - FrameMatchCondition::Specific(frame_1), - FrameMatchCondition::Specific(frame_2), - ]) - } - Instruction::Gate(_) - | Instruction::CircuitDefinition(_) - | Instruction::GateDefinition(_) - | Instruction::Declaration(_) - | Instruction::Measurement(_) - | Instruction::Reset(_) - | Instruction::CalibrationDefinition(_) - | Instruction::FrameDefinition(_) - | Instruction::MeasureCalibrationDefinition(_) - | Instruction::Pragma(_) - | Instruction::WaveformDefinition(_) - | Instruction::Arithmetic(_) - | Instruction::Halt - | Instruction::Label(_) - | Instruction::Move(_) - | Instruction::Exchange(_) - | Instruction::Load(_) - | Instruction::Store(_) - | Instruction::Jump(_) - | Instruction::JumpWhen(_) - | Instruction::JumpUnless(_) => return None, - }; - - Some( - self.frames - .get_matching_keys(condition) - .into_iter() - .collect(), - ) } pub fn to_instructions(&self, include_headers: bool) -> Vec {