Skip to content

Commit

Permalink
addresser cleanup re: PR feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
Erik Davis committed Apr 21, 2020
1 parent cf9c567 commit ad86953
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 39 deletions.
18 changes: 9 additions & 9 deletions benchmarking/rewiring-analysis.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -395,8 +395,8 @@
(make-assignments
((*random-state* (seed-random-state))
(quil::*compressor-passes* 0))
(quil::*addresser-swap-search-type*
quil::*addresser-move-to-rewiring-swap-search-type*)
(quil::*addresser-gates-swap-search-type*
quil::*addresser-rewiring-swap-search-type*)
:control (:greedy-qubit :greedy-qubit)
:path (:greedy-path :greedy-path)
:a* (:a* :a*)
Expand All @@ -407,8 +407,8 @@
((*random-state* (seed-random-state))
(quil::*compressor-passes* 0))
(quil::*initial-rewiring-default-type*
quil::*addresser-swap-search-type*
quil::*addresser-move-to-rewiring-swap-search-type*
quil::*addresser-gates-swap-search-type*
quil::*addresser-rewiring-swap-search-type*
quil::*addresser-a*-swap-search-heuristic-scale*)
:control (:random :greedy-qubit :greedy-qubit 1d0)
:naive (:naive :greedy-qubit :greedy-qubit 1d0)
Expand All @@ -426,8 +426,8 @@
((*random-state* (seed-random-state))
(quil::*compressor-passes* 0))
(quil::*initial-rewiring-default-type*
quil::*addresser-swap-search-type*
quil::*addresser-move-to-rewiring-swap-search-type*
quil::*addresser-gates-swap-search-type*
quil::*addresser-rewiring-swap-search-type*
quil::*addresser-a*-swap-search-heuristic-scale*)
:control (:random :greedy-qubit :greedy-qubit 1d0)
:path (:partial :greedy-path :greedy-path 1d0)
Expand All @@ -442,7 +442,7 @@
(make-assignments
((*random-state* (seed-random-state))
(quil::*compressor-passes* 0)
(quil::*addresser-swap-search-type* :greedy-qubit))
(quil::*addresser-gates-swap-search-type* :greedy-qubit))
(quil::*initial-rewiring-default-type*)
:control (:random)
:naive (:naive)
Expand All @@ -455,8 +455,8 @@
((*random-state* (seed-random-state))
(quil::*compressor-passes* 0))
(quil::*initial-rewiring-default-type*
quil::*addresser-swap-search-type*
quil::*addresser-move-to-rewiring-swap-search-type*
quil::*addresser-gates-swap-search-type*
quil::*addresser-rewiring-swap-search-type*
quil::*addresser-a*-swap-search-heuristic-scale*
quil::*addresser-a*-distance-metric*
)
Expand Down
7 changes: 6 additions & 1 deletion src/addresser/1q-queues.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
;;; costly.
;;;
;;; This is done via a system of "1Q Queues", which stash the 1Q operations
;;; during addressing and then are flushed at appropriate times.
;;; during addressing and then are flushed at appropriate times. Routines for
;;; managing these queues are found here, but are mostly called by the
;;; addressing procedures in addresser-common.lisp.

(defun find-ending-1q-line (chip-sched qubit)
"Find the ending location of QUBIT, following the swaps in CHIP-SCHED."
Expand Down Expand Up @@ -112,6 +114,7 @@ following swaps."

(defun flush-1q-instructions-after-wiring (state qubit)
"Flush the 1Q queue for QUBIT after potentially assigning it to a physical location."
(check-type state addresser-state)
(with-slots (working-l2p chip-sched qubit-cc 1q-queues) state
(let ((physical (apply-rewiring-l2p working-l2p qubit)))
(unless physical
Expand All @@ -126,6 +129,7 @@ following swaps."

(defun partially-flush-1Q-queues (state resources)
"Flush any part of any 1Q queue that touches a given set of non-quantum RESOURCES."
(check-type state addresser-state)
(with-slots (chip-spec 1q-queues) state
;; for each qubit line
(dotimes (qubit (chip-spec-n-qubits chip-spec))
Expand All @@ -148,6 +152,7 @@ following swaps."

(defun add-instruction-to-1Q-queue (state instr)
"Add the 1Q instruction to the queues."
(check-type state addresser-state)
(assert (= 1 (length (application-arguments instr)))
nil
"Attempted to add instruction ~/quil:instruction-fmt/ to 1Q queues."
Expand Down
97 changes: 97 additions & 0 deletions src/addresser/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
## Overview 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). The main entry point is `DO-GREEDY-ADDRESSING`.

At the core of the addresser are two data structures:
- a "logical to physical rewiring" (cf. `rewiring.lisp`), which determines what
physical qubit a logical qubit is presently assigned to
- a "logical schedule" (cf. `logical-schedule.lisp`), 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 not have an assigment to a physical qubit (and hence an assignment must
be made), or
- might 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.

## Cost Heuristics

Right now there are two sorts of costs: a duration-based cost
(cf. `temporal-addresser.lisp`) and a fidelity-based cost
(cf. `fidelity-addreser.lisp`). New cost heuristics may be implemented by
subclassing `ADDRESSER-STATE` (cf. `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 a 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.

The search type is represented as a keyword of type `ADDRESSER-SEARCH-TYPE` in
`addresser-common.lisp`, corresponding to three implementations
- `:GREEDY-PATH` in `path-heuristic.lisp`,
- `:GREEDY-QUBIT` in `qubit-heuristic.lisp`,
- `:A*`in `astar-rewiring-search.lisp`.

The default type may be customized (cf. `addresser-common.lisp`):
- `*addresser-gates-swap-search-type*` specifies the default type for `SELECT-SWAP-FOR-GATES`,
- `*addresser-rewiring-swap-search-type*` specifies the default type for `SELECT-SWAPS-FOR-REWIRING`.


## Additional Customization

The flag `*ADDRESSER-USE-1Q-QUEUES*` specifies whether 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).
15 changes: 7 additions & 8 deletions src/addresser/addresser-common.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
;;; different methods will invoke these guts after binding particular behaviors
;;; to various hooks.
;;;
;;; See DO-GREEDY-TEMPORAL-ADDRESSING below for the main entry point.
;;; See DO-GREEDY-ADDRESSING below for the main entry point.

(defvar *addresser-max-swap-sequence-length* 1000
"Controls the maximum number of swaps that can occur in a row.")
Expand Down Expand Up @@ -93,10 +93,10 @@ Returns a list of link indices, along with an updated list of rewirings tried.")
;;; - path-heuristic (for GREEDY-PATH)
(deftype addresser-search-type () '(member :a* :greedy-qubit :greedy-path))

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

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

;;; A pseudoinstruction class used to send directives to the addresser
Expand Down Expand Up @@ -128,7 +128,7 @@ Returns two values: a list of links, and an updated list of rewirings tried."
state
(format-noise "SELECT-SWAP-LINKS: entering SWAP selection phase.")
(let ((gates-in-waiting (weighted-future-gates state)))
(select-swaps-for-gates *addresser-swap-search-type*
(select-swaps-for-gates *addresser-gates-swap-search-type*
working-l2p
gates-in-waiting
state
Expand All @@ -153,7 +153,7 @@ Returns two values: a list of links, and an updated list of rewirings tried."
:do (assert (> *addresser-max-swap-sequence-length* (length rewirings-tried)) ()
"Too many rewirings tried: ~a" (length rewirings-tried))
:do (let ((links (select-swaps-for-rewiring
*addresser-move-to-rewiring-swap-search-type*
*addresser-rewiring-swap-search-type*
rewiring target-rewiring addresser-state rewirings-tried)))
(dolist (link-index links)
(embed-swap link-index
Expand Down Expand Up @@ -588,9 +588,8 @@ If DRY-RUN, this returns T as soon as it finds an instruction it can handle."
(dolist (instr instrs unassigned-qubits)
(dolist (qubit (cl-quil.resource::resource-qubits-list
(instruction-resources instr)))
(unless (or (apply-rewiring-l2p rewiring qubit)
(member qubit unassigned-qubits))
(push qubit unassigned-qubits))))))
(unless (apply-rewiring-l2p rewiring qubit)
(pushnew qubit unassigned-qubits))))))

(defun try-to-assign-qubits (state instrs)
"Attempt to assign a logical qubit from INSTRS to a physical qubit, as managed by the addresser state STATE."
Expand Down
32 changes: 14 additions & 18 deletions src/addresser/addresser-state.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,27 @@
:documentation "The initial logical-to-physical rewiring."
:type rewiring)
(working-l2p :accessor addresser-state-working-l2p
:initarg :working-l2p
:documentation "The working / current logical-to-physical rewiring. NOTE: This get mutated a _lot_."
:type rewiring)
(qq-distances :accessor addresser-state-qq-distances
:initarg :qq-distances
:documentation "Precomputed SWAP penalties between separated qubits."
:type array)
:type (array real (* *)))
(qubit-cc :accessor addresser-state-qubit-cc
:initarg :qubit-cc
:documentation "The connected component where newly-assigned qubits will live."
:type list)
(lschedule :accessor addresser-state-logical-schedule
:initarg :lschedule
:documentation "The logical schedule of not-yet-processed instructions."
:type logical-schedule)
(chip-sched :accessor addresser-state-chip-schedule
:initarg :chip-sched
:documentation "The outgoing schedule of processed instructions."
:type chip-schedule)
(chip-spec :accessor addresser-state-chip-specification
:initarg :chip-spec
:documentation "The CHIP-SPECIFICATION governing native-ness."
:type chip-specification)
(1q-queues :accessor addresser-state-1q-queues
:initarg :1q-queues
:documentation "The family of queues where not-yet-scheduled 1Q instructions live, while we get them out of the way to process 2Q instructions.")))
:documentation "The family of queues where not-yet-scheduled 1Q instructions live, while we get them out of the way to process 2Q instructions."))
(:documentation "Common state to be manipulated by addressing routines. Implementations may often opt for more, but this represents the minimum that is expectred."))

(defmethod initialize-instance :after ((instance addresser-state)
&rest initargs
Expand Down Expand Up @@ -67,9 +62,9 @@

(defmacro with-rotatef (places &body body)
"Evaluate BODY under a rotation of PLACES, then restore these to their original values."
`(prog2
(rotatef ,@places)
(progn ,@body)
`(unwind-protect
(progn (rotatef ,@places)
,@body)
(rotatef ,@(reverse places))))

;;; Current addresser types (of which there are two: the temporal addresser and
Expand All @@ -84,7 +79,7 @@
;;; like.

(defgeneric weighted-future-gates (state)
(:documentation "Collect gates from the logical schedule, along with weights which indicate how far into the future the gate occurs.
(:documentation "Collect gates from the logical schedule, along with weights which indicate how far into the future the gate occurs. The precise interpretation of this depends on the implementation of COST-FUNCTION, but generally it is expected that a larger weight indicates that the gate occurs 'farther' away in the future.
Returns a hash mapping gates from the logical schedule to numeric values."))

Expand All @@ -109,15 +104,16 @@ INSTR is the \"active instruction\".
;;; Since sometimes costs are compound objects which encode various values, they
;;; must implement the following:

(defgeneric cost-< (val1 val2)
(defgeneric cost-< (x y)
(:documentation "Generic comparison function for heuristic values.")
(:method ((val1 number) (val2 number))
(< (- val1 +double-comparison-threshold-strict+) val2)))
(:method ((x real) (y real))
(< (- x +double-comparison-threshold-strict+) y)))

(defgeneric cost-= (val1 val2)
(defgeneric cost-= (x y)
(:documentation "Generic equality function for heuristic values.")
(:method ((val1 number) (val2 number))
(double= val1 val2)))
(:method ((x real) (y real))
(< (abs (- x y))
+double-comparison-threshold-strict+)))

(defgeneric cost-flatten (cost)
(:documentation "Flattens COST to a REAL. Preserves cost ordering whenever only one of GATE-WEIGHTS or INSTR was provided to COST-FUNCTION.")
Expand Down
2 changes: 1 addition & 1 deletion tests/addresser-tests.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
(in-package #:cl-quil-tests)

(deftest test-rewiring-shuffle ()
(dolist (quil::*addresser-swap-search-type* '(:a* :greedy-qubit :greedy-path))
(dolist (quil::*addresser-gates-swap-search-type* '(:a* :greedy-qubit :greedy-path))
(not-signals error
(let ((text "CNOT 2 0"))
(is (quil::operator=
Expand Down
4 changes: 2 additions & 2 deletions tests/compiler-hook-tests.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ CNOT 0 1")
(%parsed-program-to-logical-matrix-rewiring-test pp cpp))))
;; then, the block-to-block rewiring methods.
;; i'm too lazy to check correctness, but we're at least exercising the pathway.
(dolist (quil::*addresser-move-to-rewiring-swap-search-type* '(:greedy-path :greedy-qubit :a*))
(format t "~& Testing addresser move type ~A~%" quil::*addresser-move-to-rewiring-swap-search-type*)
(dolist (quil::*addresser-rewiring-swap-search-type* '(:greedy-path :greedy-qubit :a*))
(format t "~& Testing addresser move type ~A~%" quil::*addresser-rewiring-swap-search-type*)
(finish-output)
(let* ((pp (quil::parse-quil "
LABEL @a
Expand Down

0 comments on commit ad86953

Please sign in to comment.