Skip to content

Commit 6425bd5

Browse files
authored
Fix: Instruction used/blocked frames (#73)
* Add private method for parsing a single instruction from a string * Fix: instruction frame blocking * Fix: frame dependency calculation in ScheduledProgram * Fix how FENCE uses frames * Add Instruction.get_frame_match_condition
1 parent a48b73e commit 6425bd5

9 files changed

+354
-101
lines changed

src/instruction.rs

+85
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use serde::{Deserialize, Serialize};
1616
use std::{collections::HashMap, fmt};
1717

1818
use crate::expression::Expression;
19+
use crate::program::frame::FrameMatchCondition;
1920

2021
#[cfg(test)]
2122
use proptest_derive::Arbitrary;
@@ -941,6 +942,90 @@ impl Instruction {
941942
_ => {}
942943
}
943944
}
945+
946+
pub(crate) fn get_frame_match_condition(
947+
&self,
948+
include_blocked: bool,
949+
) -> Option<FrameMatchCondition> {
950+
match self {
951+
Instruction::Pulse(Pulse {
952+
blocking, frame, ..
953+
})
954+
| Instruction::Capture(Capture {
955+
blocking, frame, ..
956+
})
957+
| Instruction::RawCapture(RawCapture {
958+
blocking, frame, ..
959+
}) => Some(if *blocking && include_blocked {
960+
FrameMatchCondition::AnyOfQubits(&frame.qubits)
961+
} else {
962+
FrameMatchCondition::Specific(frame)
963+
}),
964+
Instruction::Delay(Delay {
965+
frame_names,
966+
qubits,
967+
..
968+
}) => Some(if frame_names.is_empty() {
969+
FrameMatchCondition::ExactQubits(qubits)
970+
} else {
971+
FrameMatchCondition::And(vec![
972+
FrameMatchCondition::ExactQubits(qubits),
973+
FrameMatchCondition::AnyOfNames(frame_names),
974+
])
975+
}),
976+
Instruction::Fence(Fence { qubits }) => Some(if qubits.is_empty() {
977+
FrameMatchCondition::All
978+
} else {
979+
FrameMatchCondition::AnyOfQubits(qubits)
980+
}),
981+
Instruction::SetFrequency(SetFrequency { frame, .. })
982+
| Instruction::SetPhase(SetPhase { frame, .. })
983+
| Instruction::SetScale(SetScale { frame, .. })
984+
| Instruction::ShiftFrequency(ShiftFrequency { frame, .. })
985+
| Instruction::ShiftPhase(ShiftPhase { frame, .. }) => {
986+
Some(FrameMatchCondition::Specific(frame))
987+
}
988+
Instruction::SwapPhases(SwapPhases { frame_1, frame_2 }) => {
989+
Some(FrameMatchCondition::And(vec![
990+
FrameMatchCondition::Specific(frame_1),
991+
FrameMatchCondition::Specific(frame_2),
992+
]))
993+
}
994+
Instruction::Gate(_)
995+
| Instruction::CircuitDefinition(_)
996+
| Instruction::GateDefinition(_)
997+
| Instruction::Declaration(_)
998+
| Instruction::Measurement(_)
999+
| Instruction::Reset(_)
1000+
| Instruction::CalibrationDefinition(_)
1001+
| Instruction::FrameDefinition(_)
1002+
| Instruction::MeasureCalibrationDefinition(_)
1003+
| Instruction::Pragma(_)
1004+
| Instruction::WaveformDefinition(_)
1005+
| Instruction::Arithmetic(_)
1006+
| Instruction::Halt
1007+
| Instruction::Label(_)
1008+
| Instruction::Move(_)
1009+
| Instruction::Exchange(_)
1010+
| Instruction::Load(_)
1011+
| Instruction::Store(_)
1012+
| Instruction::Jump(_)
1013+
| Instruction::JumpWhen(_)
1014+
| Instruction::JumpUnless(_) => None,
1015+
}
1016+
}
1017+
1018+
#[cfg(test)]
1019+
/// Parse a single instruction from an input string. Returns an error if the input fails to parse,
1020+
/// or if there is input left over after parsing.
1021+
pub(crate) fn parse(input: &str) -> Result<Self, String> {
1022+
use crate::parser::{instruction::parse_instruction, lex};
1023+
1024+
let lexed = lex(input)?;
1025+
let (_, instruction) =
1026+
nom::combinator::all_consuming(parse_instruction)(&lexed).map_err(|e| e.to_string())?;
1027+
Ok(instruction)
1028+
}
9441029
}
9451030

9461031
#[cfg(test)]

src/parser/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ mod macros;
2727
mod common;
2828
mod error;
2929
mod expression;
30-
mod instruction;
30+
pub(crate) mod instruction;
3131
mod lexer;
3232

3333
type ParserInput<'a> = &'a [Token];

src/program/frame.rs

+61-12
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,47 @@ impl FrameSet {
3434
self.frames.keys().collect()
3535
}
3636

37-
/// Return all contained FrameIdentifiers which include **exactly** these qubits (in any order)
38-
/// and - if names are provided - match one of the names.
39-
pub fn get_matching_keys(&self, qubits: &[Qubit], names: &[String]) -> Vec<&FrameIdentifier> {
40-
let qubit_set: HashSet<&Qubit> = qubits.iter().collect();
41-
self.frames
42-
.iter()
43-
.filter(|(identifier, _)| {
44-
(names.is_empty() || names.contains(&identifier.name))
45-
&& qubit_set == identifier.qubits.iter().collect()
46-
})
47-
.map(|(id, _)| id)
48-
.collect::<Vec<_>>()
37+
/// Return all frames in the set which match all of these conditions. If a frame _would_ match, but is
38+
/// not present in this [FrameSet], then it is not returned (notably, the [FrameMatchCondition::Specific]
39+
/// match condition).
40+
pub(crate) fn get_matching_keys<'s, 'a>(
41+
&'s self,
42+
condition: FrameMatchCondition<'a>,
43+
) -> HashSet<&'s FrameIdentifier> {
44+
let keys = self.frames.keys();
45+
46+
match condition {
47+
FrameMatchCondition::All => keys.collect(),
48+
FrameMatchCondition::AnyOfNames(names) => {
49+
keys.filter(|&f| names.contains(&f.name)).collect()
50+
}
51+
FrameMatchCondition::AnyOfQubits(qubits) => {
52+
let any_of_set: HashSet<_> = qubits.iter().collect();
53+
54+
keys.filter(|&f| f.qubits.iter().any(|q| any_of_set.contains(q)))
55+
.collect()
56+
}
57+
FrameMatchCondition::ExactQubits(qubits) => {
58+
let exact_set: HashSet<_> = qubits.iter().collect();
59+
60+
keys.filter(|&f| f.qubits.iter().collect::<HashSet<_>>() == exact_set)
61+
.collect()
62+
}
63+
FrameMatchCondition::Specific(frame) => {
64+
// This unusual pattern (fetch key & value by key, discard value) allows us to return
65+
// a reference to `self` rather than `condition`, keeping lifetimes simpler.
66+
if let Some((frame, _)) = self.frames.get_key_value(frame) {
67+
vec![frame].into_iter().collect()
68+
} else {
69+
HashSet::new()
70+
}
71+
}
72+
FrameMatchCondition::And(conditions) => conditions
73+
.into_iter()
74+
.map(|c| self.get_matching_keys(c))
75+
.reduce(|acc, el| acc.into_iter().filter(|&v| el.contains(v)).collect())
76+
.unwrap_or_default(),
77+
}
4978
}
5079

5180
/// Retrieve the attributes of a frame by its identifier.
@@ -86,3 +115,23 @@ impl FrameSet {
86115
.collect()
87116
}
88117
}
118+
119+
pub(crate) enum FrameMatchCondition<'a> {
120+
/// Match all frames in the set
121+
All,
122+
123+
/// Match all frames which share any one of these names
124+
AnyOfNames(&'a [String]),
125+
126+
/// Match all frames which contain any of these qubits
127+
AnyOfQubits(&'a [Qubit]),
128+
129+
/// Match all frames which contain exactly these qubits
130+
ExactQubits(&'a [Qubit]),
131+
132+
/// Return these specific frames, if present in the set
133+
Specific(&'a FrameIdentifier),
134+
135+
/// Return all frames which match all of these conditions
136+
And(Vec<FrameMatchCondition<'a>>),
137+
}

src/program/graph.rs

+16-9
Original file line numberDiff line numberDiff line change
@@ -233,19 +233,26 @@ impl InstructionBlock {
233233
Ok(())
234234
}
235235
InstructionRole::RFControl => {
236-
let frames = match program.get_frames_for_instruction(instruction, true) {
237-
Some(frames) => frames,
238-
None => vec![],
239-
};
240-
241-
// Mark a dependency on the last instruction which executed in the context of each target frame
242-
for frame in frames {
236+
let used_frames = program
237+
.get_frames_for_instruction(instruction, false)
238+
.unwrap_or_default();
239+
let blocked_frames = program
240+
.get_frames_for_instruction(instruction, true)
241+
.unwrap_or_default();
242+
243+
// Take a dependency on any previous instructions to _block_ a frame which this instruction _uses_.
244+
for frame in used_frames {
243245
let previous_node_id = last_instruction_by_frame
244-
.entry(frame.clone())
245-
.or_insert(ScheduledGraphNode::BlockStart);
246+
.get(frame)
247+
.unwrap_or(&ScheduledGraphNode::BlockStart);
246248
add_dependency!(graph, *previous_node_id => node, ExecutionDependency::ReferenceFrame);
249+
}
250+
251+
// We mark all "blocked" frames as such for later instructions to take a dependency on
252+
for frame in blocked_frames {
247253
last_instruction_by_frame.insert(frame.clone(), node);
248254
}
255+
249256
Ok(())
250257
}
251258
InstructionRole::ControlFlow => Err(ScheduleError {

src/program/graphviz_dot.rs

+13
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,8 @@ DEFFRAME 0 \"ro_rx\":
233233
INITIAL-FREQUENCY: 1e6
234234
DEFFRAME 0 \"ro_tx\":
235235
INITIAL-FREQUENCY: 1e6
236+
DEFFRAME 0 1 \"cz\":
237+
INITIAL-FREQUENCY: 1e6
236238
";
237239

238240
let program =
@@ -308,8 +310,19 @@ NONBLOCKING PULSE 0 \"rf\" test(duration: 1e6)
308310
NONBLOCKING PULSE 1 \"rf\" test(duration: 1e6)
309311
"
310312
);
313+
311314
build_dot_format_snapshot_test_case!(fence_all, "FENCE");
312315

316+
build_dot_format_snapshot_test_case!(
317+
fence_wrapper,
318+
"
319+
FENCE
320+
NONBLOCKING PULSE 0 1 \"cz\" test(duration: 1e-6)
321+
NONBLOCKING PULSE 1 \"rf\" test(duration: 1e-6)
322+
FENCE 1
323+
"
324+
);
325+
313326
build_dot_format_snapshot_test_case!(
314327
jump,
315328
"DECLARE ro BIT

0 commit comments

Comments
 (0)