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

Track runtime variables in control-flow builders #10977

Merged
merged 4 commits into from
Nov 30, 2023

Conversation

jakelishman
Copy link
Member

Summary

This adds support for all the new classical runtime variables through
the control-flow builder interface. In particular, most usefully it
automatically manages the scoping rules for new declarations and inner
variable accesses, and ensures that its built scopes automatically close
over any variables used within them (including making sure nested scopes
do the same thing).

The builder API is factored out a little into an explicit interface
object, with QuantumCircuit getting an explicit implementation of
that. This is done because the number of separate API methods we would
have needed to pass around / infer was getting overly large, and this
allows us to just use standard virtual dispatch to automatically do the
right thing.

Python doesn't have a way to have an object implement an
interface other than by structural (duck) typing, so to avoid name
leakage and collisions, we instead make QuantumCircuit's
implementation a friend class that handles the inner state on its
behalf.

Not everything control-flow-builder related is factored out into the API
because it didn't seem overly useful to do this, especially when the
overridden behaviour would just have been to throw exceptions.

Details and comments

Depends on #10963 and #10974

Close #10938

@jakelishman jakelishman added the mod: circuit Related to the core of the `QuantumCircuit` class or the circuit library label Oct 5, 2023
@jakelishman jakelishman added this to the 0.45.0 milestone Oct 5, 2023
@jakelishman jakelishman requested a review from a team as a code owner October 5, 2023 03:02
@qiskit-bot
Copy link
Collaborator

One or more of the the following people are requested to review this:

  • @Qiskit/terra-core

@coveralls
Copy link

coveralls commented Nov 25, 2023

Pull Request Test Coverage Report for Build 7051272536

Warning: This coverage report may be inaccurate.

We've detected an issue with your CI configuration that might affect the accuracy of this pull request's coverage report.
To ensure accuracy in future PRs, please see these guidelines.
A quick fix for this PR: rebase it; your next report should be accurate.

  • 182 of 188 (96.81%) changed or added relevant lines in 2 files are covered.
  • 25 unchanged lines in 3 files lost coverage.
  • Overall coverage decreased (-0.03%) to 87.434%

Changes Missing Coverage Covered Lines Changed/Added Lines %
qiskit/circuit/controlflow/builder.py 73 76 96.05%
qiskit/circuit/quantumcircuit.py 109 112 97.32%
Files with Coverage Reduction New Missed Lines %
crates/qasm2/src/lex.rs 6 91.92%
qiskit/circuit/controlflow/builder.py 7 92.06%
crates/qasm2/src/parse.rs 12 97.13%
Totals Coverage Status
Change from base Build 7047396072: -0.03%
Covered Lines: 59906
Relevant Lines: 68516

💛 - Coveralls

This adds support for all the new classical runtime variables through
the control-flow builder interface.  In particular, most usefully it
automatically manages the scoping rules for new declarations and inner
variable accesses, and ensures that its built scopes automatically close
over any variables used within them (including making sure nested scopes
do the same thing).

The builder API is factored out a little into an explicit interface
object, with `QuantumCircuit` getting an explicit implementation of
that.  This is done because the number of separate API methods we would
have needed to pass around / infer was getting overly large, and this
allows us to just use standard virtual dispatch to automatically do the
right thing.

Python doesn't have a way to have an object implement an
interface other than by structural (duck) typing, so to avoid name
leakage and collisions, we instead make `QuantumCircuit`'s
implementation a friend class that handles the inner state on its
behalf.

Not everything control-flow-builder related is factored out into the API
because it didn't seem overly useful to do this, especially when the
overridden behaviour would just have been to throw exceptions.
Copy link
Member

@mtreinish mtreinish left a comment

Choose a reason for hiding this comment

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

Overall this LGTM the changes are pretty sensible and straightforward once you wrap your head around the new interface for dealing with scoping. I had a couple of comments inline. The only thing that I found a bit hard to follow was referring to the current scope variable as api everywhere. I know what the intent there is, but I'd probably just call it scope, circuit_scope, or current_scope to make it a bit more explicit about what the variable is. But it's probably ok to stick with api everywhere I guess since I could follow it. The only really actionable comment is a tiny docs nit.

qiskit/circuit/controlflow/builder.py Outdated Show resolved Hide resolved
qiskit/circuit/controlflow/builder.py Outdated Show resolved Hide resolved
forbidden_message: If a string is given here, a :exc:`.CircuitError` will be raised on
any attempts to append instructions to the scope with this message. This is used by
pseudo scopes where the state machine of the builder scopes has changed into a
position where no instructions should be accepted, such as when inside a ``switch``
but outside any cases.
"""
self.instructions: List[CircuitInstruction] = []
self._instructions: List[CircuitInstruction] = []
Copy link
Member

Choose a reason for hiding this comment

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

This will probably conflict with: #11214 which I think is changing this to CircuitData.

Copy link
Member Author

Choose a reason for hiding this comment

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

It will conflict, but in a way that's super easy to fix and honestly, #11214 unifies the types of the instructions circuit scope object in a much more pleasant way.

qiskit/circuit/quantumcircuit.py Outdated Show resolved Hide resolved
Comment on lines 1389 to 1398
broadcast_iter = (
operation.broadcast_arguments(expanded_qargs, expanded_cargs)
if isinstance(operation, Instruction)
else Instruction.broadcast_arguments(operation, expanded_qargs, expanded_cargs)
)
for qarg, carg in broadcast_iter:
self._check_dups(qarg)
instruction = CircuitInstruction(operation, qarg, carg)
api.append(instruction)
instructions._add_ref(api.instructions, len(api.instructions) - 1)
Copy link
Member

Choose a reason for hiding this comment

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

FWIW, this is kind of an unrelated change, it's more compact and easier to read. But really the only change was from appender -> api.append which took me a bit longer to see because of the other refactoring.

Copy link
Member Author

Choose a reason for hiding this comment

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

The broadcast_iter is the only unrelated change. Originally this was only collapsing appender into api.append (since we already have the api object), but since #10827 the api.instructions field is dispatched as well, so it was growing to quite a few dispatched names.

jakelishman and others added 3 commits November 30, 2023 18:41
Copy link
Member

@mtreinish mtreinish left a comment

Choose a reason for hiding this comment

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

This LGTM, thanks for the quick update

@mtreinish mtreinish added this pull request to the merge queue Nov 30, 2023
Merged via the queue into Qiskit:main with commit a79e879 Nov 30, 2023
14 checks passed
@jakelishman jakelishman deleted the var/cf-builder branch November 30, 2023 22:31
jakelishman added a commit to jakelishman/qiskit-terra that referenced this pull request Jan 29, 2024
This commit is a roll-up reversion of the following commits (PR):

* This reverts commit a79e879 (Qiskit#10977)
* This reverts commit ba161e9 (Qiskit#10974)
* This reverts commit 50e8137 (Qiskit#10944)

This is being done to clear the 1.0 branch of memory-owning `expr.Var`
variables and `Store` instructions.  It should be re-applied once 1.0 is
released.

The individual reverts had conflicts, since various other large-scale
changes include wrap-ups of the changes to be reverted.  This reversion
should have handled all that, and other places where standalone `Var`
nodes were referenced (such as in "See also" sections), even if not
used.
jakelishman added a commit to jakelishman/qiskit-terra that referenced this pull request Jan 29, 2024
This commit is a roll-up reversion of the following commits (PR):

* commit a79e879 (Qiskit#10977)
* commit ba161e9 (Qiskit#10974)
* commit 50e8137 (Qiskit#10944)

This is being done to clear the 1.0 branch of memory-owning `expr.Var`
variables and `Store` instructions.  It should be re-applied once 1.0 is
released.

This wasn't done by individual `revert` operations, because there were
also significant structural changes introduced in those PRs that were
very valid and should be maintained.  Cross-references to `Var` nodes
from other functions have been removed for now.

Making `Var` and `types.Type` hashable is maintained, as is the
`Var.standalone` function, in order to prepare the ground for the
inclusion of proper `Var` nodes in a minor release.
github-merge-queue bot pushed a commit that referenced this pull request Jan 29, 2024
This commit is a roll-up reversion of the following commits (PR):

* commit a79e879 (#10977)
* commit ba161e9 (#10974)
* commit 50e8137 (#10944)

This is being done to clear the 1.0 branch of memory-owning `expr.Var`
variables and `Store` instructions.  It should be re-applied once 1.0 is
released.

This wasn't done by individual `revert` operations, because there were
also significant structural changes introduced in those PRs that were
very valid and should be maintained.  Cross-references to `Var` nodes
from other functions have been removed for now.

Making `Var` and `types.Type` hashable is maintained, as is the
`Var.standalone` function, in order to prepare the ground for the
inclusion of proper `Var` nodes in a minor release.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
mod: circuit Related to the core of the `QuantumCircuit` class or the circuit library
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add support for closing over variables to control-flow builders
4 participants