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

Addresser Refactor and Cleanup #617

Merged
merged 21 commits into from
Apr 21, 2020
Merged

Conversation

braised-babbage
Copy link
Contributor

@braised-babbage braised-babbage commented Mar 25, 2020

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:

  • a "logical to physical rewiring", which determines what physical qubit a logical qubit is presently assigned to
  • a "logical schedule", which represents the source instructions as a partially ordered set, with resource conflicts determining whether one instruction may occur before another.
    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 subclassing ADDRESSER-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 occur
  • BUILD-WORST-COST, which is basically a stand-in for most-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, and
  • SELECT-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, and astar-rewiring-search.lisp). Defaults are specified by this snippet from addresser-common.lisp:

;;; Search routines are implemented in
;;;   - astar-rewiring-search.lisp (for A*)
;;;   - qubit-heuristic.lisp (for GREEDY-QUBIT)
;;;   - path-heuristic (for GREEDY-PATH)
(deftype addresser-search-type () '(member :a* :greedy-qubit :greedy-path))

(defvar *addresser-swap-search-type* ':greedy-qubit
  "The type of swap search the addresser should use.")

(defvar *addresser-move-to-rewiring-swap-search-type* ':a*
  "The type of swap search the addresser should use when doing move-to-rewiring.")

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).

Copy link
Contributor

@appleby appleby left a comment

Choose a reason for hiding this comment

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

I salute you

src/addresser/addresser-common.lisp Outdated Show resolved Hide resolved
src/addresser/addresser-common.lisp Show resolved Hide resolved
src/addresser/astar-rewiring-search.lisp Outdated Show resolved Hide resolved
@braised-babbage braised-babbage changed the title Touch up the addresser Addresser Cleanup Mar 26, 2020
@braised-babbage braised-babbage changed the title Addresser Cleanup Addresser Refactor and Cleanup Mar 26, 2020
@braised-babbage
Copy link
Contributor Author

calling in the big guns @ecpeterson

src/addresser/fidelity-addresser.lisp Outdated Show resolved Hide resolved
src/addresser/fidelity-addresser.lisp Outdated Show resolved Hide resolved
src/compilation-methods.lisp Outdated Show resolved Hide resolved
Copy link
Contributor

@ecpeterson ecpeterson left a 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.

src/addresser/logical-schedule.lisp Show resolved Hide resolved
src/addresser/astar-rewiring-search.lisp Outdated Show resolved Hide resolved
src/addresser/astar-rewiring-search.lisp Outdated Show resolved Hide resolved
src/addresser/fidelity-addresser.lisp Outdated Show resolved Hide resolved
src/addresser/fidelity-addresser.lisp Show resolved Hide resolved
src/compilation-methods.lisp Outdated Show resolved Hide resolved
src/addresser/addresser-common.lisp Show resolved Hide resolved
src/addresser/addresser-common.lisp Outdated Show resolved Hide resolved
src/addresser/addresser-common.lisp Show resolved Hide resolved
src/addresser/addresser-common.lisp Show resolved Hide resolved
@braised-babbage
Copy link
Contributor Author

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 addresser-common.lisp


;;; Search routines are implemented in
;;;   - astar-rewiring-search.lisp (for A*)
;;;   - qubit-heuristic.lisp (for GREEDY-QUBIT)
;;;   - path-heuristic (for GREEDY-PATH)
(deftype addresser-search-type () '(member :a* :greedy-qubit :greedy-path))

(defvar *addresser-swap-search-type* ':greedy-qubit
  "The type of swap search the addresser should use.")

(defvar *addresser-move-to-rewiring-swap-search-type* ':a*
  "The type of swap search the addresser should use when doing move-to-rewiring.")

@appleby
Copy link
Contributor

appleby commented Apr 3, 2020

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. 0020q-0000654-johannes.quil is still a slowpoke, but does eventually complete.

benchmark prev this PR
0005q-0000160.quil ?? ~1.5s
0008q-0000418-qaoa-8-5.quil ?? ~5.0s
0020q-0000654-johannes.quil ?? ~18 min

@braised-babbage braised-babbage marked this pull request as ready for review April 14, 2020 22:29
@braised-babbage braised-babbage requested a review from a team as a code owner April 14, 2020 22:29
@braised-babbage
Copy link
Contributor Author

Aside from the usual tests passing, as a sanity check I ran the ibm_qx_mapping benchmarks (restricted to those qasm files < 10kB in size, due to time constraints). Relative to what is on master, the difference in gate depth and compilation time averaged out to < 2% (< 5% for compilation time) respectively. Considering that the the cost functions take random weights, I would imagine this meets roughly my goal of "not changing any behavior."

I was a bit surprised by @appleby 's remark about 0005q-0000160.quil and company, since I don't know where the performance gain would come from. Considering the current master branch, on my system I see both 0005q-0000160.quil and 0008q-0000418-qaoa-8-5.quil finishing in seconds.

@ecpeterson
Copy link
Contributor

"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.

Copy link
Contributor

@ecpeterson ecpeterson left a 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.

src/addresser/README.md Outdated Show resolved Hide resolved
src/addresser/README.md Outdated Show resolved Hide resolved
(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
Copy link
Member

Choose a reason for hiding this comment

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

for the existence of

Copy link
Contributor Author

Choose a reason for hiding this comment

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

gratuitous

src/addresser/1q-queues.lisp Show resolved Hide resolved
src/addresser/README.md Outdated Show resolved Hide resolved
src/addresser/addresser-common.lisp Show resolved Hide resolved
@@ -0,0 +1,101 @@
## Overview and Motivation
Copy link
Member

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?

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 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-common.lisp Show resolved Hide resolved
src/addresser/addresser-state.lisp Outdated Show resolved Hide resolved
src/addresser/addresser-state.lisp Outdated Show resolved Hide resolved
(defgeneric cost-= (val1 val2)
(:documentation "Generic equality function for heuristic values.")
(:method ((val1 number) (val2 number))
(double= val1 val2)))
Copy link
Member

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?

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'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).

Copy link
Contributor Author

@braised-babbage braised-babbage Apr 21, 2020

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.

src/addresser/addresser-state.lisp Outdated Show resolved Hide resolved
src/addresser/addresser-state.lisp Outdated Show resolved Hide resolved
@stylewarning stylewarning self-requested a review April 21, 2020 20:20
@braised-babbage braised-babbage merged commit 286247d into master Apr 21, 2020
@notmgsk notmgsk mentioned this pull request Apr 22, 2020
@stylewarning stylewarning deleted the feature/touch-up-the-addresser branch April 24, 2020 17:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants