-
Notifications
You must be signed in to change notification settings - Fork 9
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
Extend ProgramStats PR #339
Conversation
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a key new file
@@ -66,15 +68,41 @@ impl InstructionsSource for Program { | |||
} | |||
} | |||
|
|||
impl<'p> InstructionsSource for BasicBlock<'p> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is to fit the existing pattern, although would not be how I'd do it from scratch; see description
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like BasicBlock
is effectively what I was trying to capture with InstructionsSource
anyway.
)?; | ||
|
||
Ok(ScheduledProgram { blocks }) | ||
let control_flow_graph = program.as_control_flow_graph(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note how much cleaner the implementation in as_control_flow_graph
is than this one.
Also, that new implementation tolerates/elides more instructions than this previous one did. Some of those end up in errors within ScheduledBasicBlock
because of lack of calibrations, but DEFCAL
and friends are allowed now where they were once errors.
); | ||
}; | ||
} | ||
BlockTerminator::Unconditional { target } => { | ||
// BasicBlockTerminator::Conditional { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
todo: I want to come back to this and rework BasicBlockTerminator::JumpWhen
and ::JumpUnless
into a single enum similar to this previous Conditional
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should that be considered a future todo
or something to be done as part of #334?
pub fn get_first_basic_block(&self) -> Option<BasicBlock> { | ||
self.as_control_flow_graph().blocks.into_iter().next() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like sort of a surprising thing to do: construct the entire graph, then discard most of it. Do you think this functionality will be needed outside of tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah this is not something we want to ship, but I do think it's quite useful as a convenience function to let the user treat the program like a block and get the metrics they want from it. Your new TryFrom
implementation is just as good though
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems reasonable.
What consumers are there that will need to be updated for the breaking change?
@@ -66,15 +68,41 @@ impl InstructionsSource for Program { | |||
} | |||
} | |||
|
|||
impl<'p> InstructionsSource for BasicBlock<'p> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like BasicBlock
is effectively what I was trying to capture with InstructionsSource
anyway.
qubits: HashSet<&'a Qubit>, | ||
} | ||
|
||
impl<'a, 'b> From<&'a InstructionBlock<'b>> for InstructionBlockWithQubitSet<'a, 'b> { | ||
fn from(block: &'a InstructionBlock<'b>) -> Self { | ||
impl<'a, 'b> From<&'a ScheduledBasicBlock<'b>> for InstructionBlockWithQubitSet<'a, 'b> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, InstructionBlockWithQubitSet
was just a hack to let InstructionBlock
work as an instruction-source.
); | ||
}; | ||
} | ||
BlockTerminator::Unconditional { target } => { | ||
// BasicBlockTerminator::Conditional { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should that be considered a future todo
or something to be done as part of #334?
Only internal ones, to my knowledge. I can validate that before we merge #334 |
Extends #336 with:
program
module. Note that all the shiny new stuff is now atprogram::analysis
. I'd like to re-export structs from that level rather than make submodules private, for user benefit. A user shouldn't have to burrow more than 2 levels deep here, anything deeper is an implementation detail.ControlFlowGraph
andBasicBlock
which are neatly constructed from (and which borrow) aProgram
ScheduledProgram
withInstructionBlock
s toScheduledControlFlowGraph
withScheduledBasicBlock
sNone
. Blocks are stored in a Vec rather than an index map. Blocks which do not end with a terminator nowContinue
into the block of succeeding index rather thanJump
ing there. This is more intuitive behavior.ScheduledProgram::from_program
wasquil-rs
and the next consumer...but in principle, this is the right thing to do.From<BasicBlock> for ProgramStats
to follow the existing pattern in feat!: #334: program stats #336What's not done here (and can be done if merged into #336):
pub
interface and correctly mark PR as breakingProposal for way forward on stats
metrics
oranalysis
sounds more correct thanstats
to me; I hear the standard statistics likemean
,median
, etc when I hearstats
The following metrics make sense for their respective targets:
Program via ControlFlowGraph
true
, elsefalse
BasicBlock
Program
was key because of how and when it was called; I'd expect this to be called exactly once in the lifetime ofProgramStats
ExecutionGraph wrapping BasicBlock
I might rename this to something more specific like
QubitGraph
ScheduledBasicBlock
None of the above
body_instructions().len()
isSummary
So, given the above proposal, I don't know that traits or wrapper structs are worth the complexity (except for
ExecutionGraph
). Each respective type can just implement its methods directly because even the same method might have different return types (e.g.Program#depth
vsBasicBlock#depth
will have different failure modes and thus errors). To get a program's depth, the user might callExecutionGraph::from(self.get_only_basic_block()?).depth()
. I don't yet have a neat picture for how the user could efficiently calculateprogram.depth(); program.expected_fidelity()
because that needs, in the case of multiple blocks without DCF, to construct anExecutionGraph
around eachBasicBlock
and persist that between calls.