-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
new unroll for-loops transpilation pass #9670
new unroll for-loops transpilation pass #9670
Conversation
Thank you for opening a new pull request. Before your PR can be merged it will first need to pass continuous integration tests and be reviewed. Sometimes the review process can be slow, so please be patient. While you're waiting, please feel free to review other open PRs. While only a subset of people are authorized to approve pull requests for merging, everyone is encouraged to review open pull requests. Doing reviews helps reduce the burden on the core team and helps make the project's code better for everyone. One or more of the the following people are requested to review this:
|
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 starts getting into questions that I'm not sure we need to be answering at the Qiskit level; it's a purely classical action, and we really don't have the context to know whether it's "better" at this level (given it's been put in optimisations). Unrolling loops might be better if it avoids a tight classical jump from needing to be synced, but equally it could easily blow out program memory on certain controllers. I'm not really sure it's something we should be trying to do at the Qiskit level in the guise of optimisation.
That said, it might not hurt for us to include it as a simple pass for backends that don't support for_loop
; we can use it as part of the translation process for those, or even just expose it like we do ConvertCifToIfElse
(or whatever it's called) to allow backends an easier upgrade route in their custom stages.
I agree. I'm not advocating for having it in any default pipeline. |
Maybe the pass can be configurable for the amount of maximum repetitions that is allow to unroll given the size of the indexset to be known)? |
done, with |
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.
Naming nitpicks: can we place this pass in passes.utils
instead (next to convert_conditions_to_if_ops.py
)? It feels wrong to call it an "optimisation" to me in Qiskit land - we're not a classical compiler nor should we be right now (if ever?), and I think this pass's place is more of a translation utility.
|
||
@classmethod | ||
def body_contains_continue_or_break(cls, circuit): |
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.
There's no real reason for this to be a class method (doesn't need to access cls
, and this transpiler pass isn't designed/safe for subclassing) - it could just be a stand alone function, but it's not a big deal.
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.
done in b33e1fa
"""UnrollForLoops transpilation pass unrolls for-loops when possible. Things like | ||
`for x in {0, 3, 4} {rx(x) qr[1];}` will turn into `rx(0) qr[1]; rx(3) qr[1]; rx(4) qr[1];`. | ||
""" | ||
|
||
def __init__(self, max_target_depth=-1): | ||
"""UnrollForLoops transpilation pass unrolls for-loops when possible. Things like | ||
`for x in {0, 3, 4} {rx(x) qr[1];}` will turn into `rx(0) qr[1]; rx(3) qr[1]; rx(4) qr[1];`. |
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.
No need to repeat documentation between the class docstring and __init__
- both get concatenated into the docs output. That said, we need to ensure that qiskit.transpiler.passes.<whichever category>
and qiskit.transpiler.passes
both import this into their namespace, and the qiskit.transpiler.passes
docstring initiates documentation of the class.
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.
Indeed! fixed in fbbcdb7
# skip unrolling if it results in bigger than max_target_depth | ||
if self.max_target_depth > 0 and len(indexset) * body.depth() > self.max_target_depth: | ||
continue | ||
|
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.
QuantumCircuit.depth
doesn't treat control-flow specially, i.e. a single containing for
loop looks like it contributes just 1 to the depth.
I think that's probably actually the right choice here, but I think it's worth mentioning: if an inner for loop is not unrolled because of the max-depth constraint (during the depth-first nature of control_flow.trivial_recurse
), then a containing for
loop is still eligible for unrolling using this metric. I think this behaviour is the right choice, I just think it's worth pointing out. We may even want a test to enforce it.
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.
Co-authored-by: Jake Lishman <[email protected]>
…-ref/unroll-forloops
oh! while showing this to my team, I noticed that parameterless loops ( |
Makes sense to me, done in 41312ad |
Co-authored-by: Jake Lishman <[email protected]>
…iskit-terra into feature/no-ref/unroll-forloops
Co-authored-by: Jake Lishman <[email protected]>
…iskit-terra into feature/no-ref/unroll-forloops
fixed in 9422e4f and It should be ready to go! |
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 matches my recollection as well. |
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.
Ok, that's good enough for me.
* dynamic circuit optimization: unroll for loops * exceptions * check inside conditional blocks * docs * reno * Update qiskit/transpiler/passes/optimization/unroll_forloops.py Co-authored-by: Jake Lishman <[email protected]> * parameterless support * moved to utils * no classmethod, but function * docstring and __init__ * Update qiskit/transpiler/passes/optimization/unroll_forloops.py Co-authored-by: Jake Lishman <[email protected]> * Update test/python/transpiler/test_unroll_forloops.py Co-authored-by: Jake Lishman <[email protected]> * nested for-loops test * docstring note * new test with c_if * Remove commented-out code --------- Co-authored-by: Jake Lishman <[email protected]>
* dynamic circuit optimization: unroll for loops * exceptions * check inside conditional blocks * docs * reno * Update qiskit/transpiler/passes/optimization/unroll_forloops.py Co-authored-by: Jake Lishman <[email protected]> * parameterless support * moved to utils * no classmethod, but function * docstring and __init__ * Update qiskit/transpiler/passes/optimization/unroll_forloops.py Co-authored-by: Jake Lishman <[email protected]> * Update test/python/transpiler/test_unroll_forloops.py Co-authored-by: Jake Lishman <[email protected]> * nested for-loops test * docstring note * new test with c_if * Remove commented-out code --------- Co-authored-by: Jake Lishman <[email protected]>
Summary
This transpilation pass unrolls for loops when possible. Things like
for x in {0, 3, 4} {rx(x) qr[1];}
will turn intorx(0) qr[1]; rx(3) qr[1]; rx(4) qr[1];
.Details and comments
Loop unrolling is a common optimization which is particularly easy in Qiskit for-loops. There are some exceptions though: when there is a
ContinueLoopOp
or aBreakLoopOp
, which they might be inside aIfElseOp
.