-
Notifications
You must be signed in to change notification settings - Fork 74
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
Addresser Refactor and Cleanup #617
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.
I salute you
calling in the big guns @ecpeterson |
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.
On the whole, it seems fine. I left some comments as things to chew on. Here's one further comment that didn't belong at any particular point in the source:
- Are keywords like
':a*
collected anywhere? As these definitions spread further out, it might be good to aggregate them into an enumerative type.
Thanks for checking this out. For the keywords, I have this little section in
|
Nice little bonus: this PR appears to have resolved #531, or at least greatly reduced the runtimes. Two of the three rewiring benchmarks that previously took "forever" now finish in < 5s.
|
a813b31
to
628d5c4
Compare
Aside from the usual tests passing, as a sanity check I ran the I was a bit surprised by @appleby 's remark about |
"Performance gain" may well mean only "avoids deadlock", and changes that have subtle-to-no effect on the rest of the code's performance may still dislodge whatever wrench for these particular examples. I'm not too shocked. |
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.
My approval doesn't mean much to GitHub these days, but here it is.
(e.g. with non-real cost values), but the above reflects the bare minimum needed | ||
to add a new cost heuristic. | ||
|
||
As a note, the reason for `WEIGHTED-FUTURE-GATES` is to allow parts of the |
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.
for the existence of
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.
gratuitous
@@ -0,0 +1,101 @@ | |||
## Overview and Motivation |
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.
Maybe add a "tutorial" or instructions for adding a new heuristic?
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.
I think this is a good idea in general, and I would be happy to do so, but not for this PR (mostly due to time constraints on my end). As it stands we're at least halfway there: now that the temporal and fidelity heuristics are on equal footing, developers can imagine what the addition of a third cost heuristic might look like. Similarly, the existing search heuristics serve as examples for implementation of new search heuristics. (I recognize that this is still quite burdensome for an outsider to grok.)
src/addresser/addresser-state.lisp
Outdated
(defgeneric cost-= (val1 val2) | ||
(:documentation "Generic equality function for heuristic values.") | ||
(:method ((val1 number) (val2 number)) | ||
(double= val1 val2))) |
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.
don't these need to be coerced to doubles?
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.
I'm just going to specialize these cost methods on double-float
. IMO narrowing conversions for equality and inequality checks are a bit worrisome (not saying that they can't have a place, but that place shouldn't exist by accident).
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.
... nevermind. We pretty often do cost arithmetic with mixed rational
and double-float
types. For now I will preserve the current semantics, which does not require casting to doubles (double=
assumes nothing of the sort). But since we might actually one day want to upgrade double=
to something which does assume that its arguments are doubles, I have remove that call from this site.
d87e110
to
ad86953
Compare
The following is intended to clean up and clarify the basic addresser API. It is not intended to change any behavior.
Background and Motivation
The quilc addresser is responsible for translating source programs, which may involve arbitrary "logical qubits" interacting in arbitrary ways (e.g. 5q gates), to programs which conform to the topological constraints of the hardware (e.g. using only physical qubits, and with 2Q gates only on live hardware links). At the core of the addresser are two data structures:
The addresser walks the logical scheduler in topological order, updating the logical to physical rewiring as need be, and emitting instructions (to a "chip schedule").
The addresser handles gates on > 2 qubits by first translating them with
APPLY-TRANSLATION-COMPILERS
. This is relatively straightforward.The main difficulty in addressing is managing the logical to physical rewiring. At any given moment, logical qubits might i) not have an assigment to a physical qubit (and hence an assignment must be made), or ii) be assigned to physical qubits which are not adjacent (in which case something must be done in order for a gate involving them to be executed).
The main technique for dealing with the second problem is to introduce SWAP operations, which can be used to shuffle the quantum state around in a way that reflects an update to the logical to physical assignment. This is a challenging task, because it can incur a sizeable overhead in the 2Q cost of the compiled program. The quilc addresser makes a lot of effort to assign qubits and select swaps intelligently.
The approach taken by quilc is to apply a few heuristics, which can be tuned or adapted as we see fit. These are split along two axes. The first axis is how one measures the "cost" of an instruction or set of instructions. The second is how one finds "good" swap operations.
One of the main goals of this PR is to clarify the relationship between these heuristics, and to allow for the (relatively easy) addition of new ones.
Cost Heuristics
Right now there are two sorts of costs: a duration-based cost (
temporal-addresser.lisp
) and a fidelity-based cost (fidelity-addreser.lisp
). New cost heuristics may be implemented by subclassingADDRESSER-STATE
(addresser-state.lisp
), and then implementing methods on a few associated generics:COST-FUNCTION
, which computes the cost of the current logical schedule.WEIGHTED-FUTURE-GATES
, which constructs a mapping from gate in the current logical schedule to a value indicating "how far" into the future they will occurBUILD-WORST-COST
, which is basically a stand-in formost-positive-fixnum
if the heuristic uses a custom or compound cost value.In practice, both the temporal and fidelity addresser go a bit further than this (e.g. with non-real cost values), but the above reflects the bare minimum needed to add a new cost heuristic.
As a note, the reason for
WEIGHTED-FUTURE-GATES
is to allow parts of the addresser to effectively do a look-ahead. For example, in swap selection, choosing swaps which look good "right now" might be a poor strategy if the next few instructions force additional swaps to be inserted. As it stands, both the temporal and fidelity cost functions incorporate this look-ahead information.Search Heuristics
When the addresser decides that swaps are needed, there are usually a large number of candidates. In order to select the best, it now relies on the following two generics
SELECT-SWAPS-FOR-GATES
, used by the addresser to select swaps so that it may assign logical gates to physical qubits, andSELECT-SWAPS-FOR-REWIRING
, used by the addresser to select swaps in order to update the current rewiring to a desired rewiring.These represent the two main pieces of functionality needed to add a new search heuristic to the addresser. Currently they dispatch on the search type (a keyword symbol) and take the full addresser state as an argument.
We currently have three implementations (in
path-heuristic.lisp
,qubit-heuristic.lisp
, andastar-rewiring-search.lisp
). Defaults are specified by this snippet fromaddresser-common.lisp
:Additional Customization
I added an additional flag
*ADDRESSER-USE-1Q-QUEUES*
which allows one (when true) to stash 1Q gates away so that they will not influence qubit allocation, swap selection, and so on. Previously this was hardcoded as default behavior for the temporal cost heuristic but not for the fidelity cost heuristic (this was because the fidelity heuristic can actually place 1Q gates in intelligent manner, on good qubits, whereas the duration heuristic cannot, and so for the duration heuristic all effort is made to not get in the way of 2Q addressing).