Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix how an instruction's used/blocked frames are calculated #73

Merged
merged 21 commits into from
Jun 1, 2022
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -942,6 +942,18 @@ 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(crate) fn parse(input: &str) -> Result<Self, String> {
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())?;
Ok(instruction)
}
}

#[cfg(test)]
Expand Down
2 changes: 1 addition & 1 deletion src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down
87 changes: 75 additions & 12 deletions src/program/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,57 @@ 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::<Vec<_>>()
/// 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(crate) fn get_matching_keys<'s, 'a>(
&'s self,
condition: FrameMatchCondition<'a>,
) -> HashSet<&'s 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) => {
// 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) => conditions
.into_iter()
.map(|c| self.get_matching_keys(c))
.reduce(|acc, el| acc.into_iter().filter(|&v| el.contains(v)).collect())
genos marked this conversation as resolved.
Show resolved Hide resolved
.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(),
}
}

/// Retrieve the attributes of a frame by its identifier.
Expand Down Expand Up @@ -87,3 +126,27 @@ impl FrameSet {
.collect()
}
}

pub(crate) enum FrameMatchCondition<'a> {
/// Match all frames in the set
All,

/// Match all frames which shares any one of these names
kalzoo marked this conversation as resolved.
Show resolved Hide resolved
AnyOfNames(&'a [String]),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why slices here? You're using vectors below (because of needing to put recursive ones on the heap), and if these get large enough, I imagine a set-based solution would be faster.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing the lifetime stuff would probably simplify downstream code as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes it a little simpler/cheaper to build the filter in the first place, without cloning. I originally had vecs everywhere, then slices, and then came to this and it came out cleanest. It is inconsistent though

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose it makes sense if you see them all as slices but the Vec as just a Box<&[_]> due to the type recursion


/// Match all frames which contain any of these qubits
AnyOfQubits(&'a [Qubit]),

/// Match all frames which contain exactly these qubits
ExactQubits(&'a [Qubit]),

/// Return these specific frames, if present in the set
Specific(&'a FrameIdentifier),

/// Return all frames which match all of these conditions
And(Vec<FrameMatchCondition<'a>>),

/// Return all frames which match any of these conditions
#[allow(dead_code)]
kalzoo marked this conversation as resolved.
Show resolved Hide resolved
Or(Vec<FrameMatchCondition<'a>>),
}
17 changes: 14 additions & 3 deletions src/program/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,19 +234,30 @@ 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) {
kalzoo marked this conversation as resolved.
Show resolved Hide resolved
kalzoo marked this conversation as resolved.
Show resolved Hide resolved
Some(frames) => frames,
None => vec![],
};
let blocked_frames = match program.get_frames_for_instruction(instruction, true)
kalzoo marked this conversation as resolved.
Show resolved Hide resolved
{
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 {
Expand Down
13 changes: 13 additions & 0 deletions src/program/graphviz_dot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down Expand Up @@ -309,8 +311,19 @@ 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
Expand Down
Loading