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

Feature: "DEFEXPI" #498

Merged
merged 35 commits into from
Dec 18, 2019
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
7a6339d
checkpoint: parsing and printing support for AS PAULI-SUM
ecpeterson Sep 28, 2019
0f3e493
dorky matrix exponential
ecpeterson Sep 30, 2019
c08786b
misguided? first attempt at gates from pauli-sum definitions
ecpeterson Sep 30, 2019
f495350
add missing error
ecpeterson Sep 30, 2019
6339517
move pauli-term->matrix nearer to definition of pauli-term
ecpeterson Sep 30, 2019
b184575
track symbols correctly for parameter functions
ecpeterson Sep 30, 2019
cc5c4e8
fix rotation error ; fix bare variable error
ecpeterson Sep 30, 2019
f8e2df1
kill stray debug line
ecpeterson Oct 1, 2019
0e43aae
attic optimal-2q.lisp
ecpeterson Oct 1, 2019
ebd1f13
attempt at adding 2Q hamiltonian canonicalization --- NOT READY FOR P…
ecpeterson Oct 2, 2019
f1cb6d7
permit explicit wildcard bindings in define-compiler
ecpeterson Oct 5, 2019
88572d0
rework custom gate definition anon-gates
ecpeterson Oct 5, 2019
fb5062d
checkpoint: diagonal compiler sorta works
ecpeterson Oct 5, 2019
7b2f035
allow custom gate anon-gates to have parameter lists
ecpeterson Oct 5, 2019
3a11de8
kill needless newline
ecpeterson Oct 5, 2019
9b5a0f0
sort out delayed-expression tricks, mostly
ecpeterson Oct 5, 2019
6ccb83c
change name to linear-paulis
ecpeterson Oct 5, 2019
fa33101
fix diagonal compiler --- REQUIRES MAGICL BRANCH
ecpeterson Oct 14, 2019
83b73f8
knock out two XXXs
ecpeterson Nov 15, 2019
7f84746
install global compilers
ecpeterson Nov 15, 2019
8a75d8f
remember to disallow constant terms
ecpeterson Nov 18, 2019
1d915ef
fix ASD misdefinition
ecpeterson Dec 9, 2019
9cd4327
nix pauli-gate in build-gate.lisp
ecpeterson Dec 9, 2019
783b708
respond to PR feedback
ecpeterson Dec 10, 2019
5df4c3f
PR response
ecpeterson Dec 10, 2019
78ef2ad
improve readability of parametric-diagonal-compiler
ecpeterson Dec 11, 2019
3fdca2c
denote RECOGNIZE-UCR on the global compiler registry
ecpeterson Dec 12, 2019
65c85d4
respond to PR feedback
ecpeterson Dec 17, 2019
aea984d
pick nit
ecpeterson Dec 17, 2019
6b1a4b4
now pick robert's nits
ecpeterson Dec 17, 2019
0289c7f
avoid unnecessary p-p-c calls
ecpeterson Dec 17, 2019
de27a6e
skip emitting needless gates in p-d-c
ecpeterson Dec 17, 2019
eb67698
fix bug in emitting ZI gate
ecpeterson Dec 17, 2019
c9f80de
add test for parametric pauli compiler
ecpeterson Dec 17, 2019
f2427c4
kill stray magic number
ecpeterson Dec 18, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion cl-quil.asd
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,9 @@
(:file "state-prep")
(:file "translators")
(:file "modifiers")
(:file "linear-paulis")
;; attic'd files / pedagogical purposes only
(:file "optimal-2q")
ecpeterson marked this conversation as resolved.
Show resolved Hide resolved
;; attic'd file / pedagogical purposes only
(:static-file "cs-compile")))
(:module "analysis"
:serial t
Expand Down
36 changes: 35 additions & 1 deletion src/ast.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,21 @@ If no exit rewiring is found, return NIL."
:reader permutation-gate-definition-permutation))
(:documentation "A gate definition whose entries can be represented by a permutation of natural numbers."))

(defclass pauli-sum-gate-definition (gate-definition)
ecpeterson marked this conversation as resolved.
Show resolved Hide resolved
((terms :initarg :terms
:reader pauli-sum-gate-definition-terms
:documentation "List of PAULI-TERMs comprising the sum.")
(parameters :initarg :parameters
:reader pauli-sum-gate-definition-parameters
:documentation "Ordered list of parameter names to be supplied to the definition, which can appear in arithmetical expressions weighting the definition's Pauli terms.")
(arguments :initarg :arguments
:reader pauli-sum-gate-definition-arguments
:documentation "Ordered list of formal arguments appearing in the definition's Pauli terms."))
(:documentation "Represents a gate definition as the exponential of a weighted sum of Pauli matrices."))

(defmethod gate-definition-qubits-needed ((gate pauli-sum-gate-definition))
(length (pauli-sum-gate-definition-arguments gate)))

(defmethod gate-definition-qubits-needed ((gate permutation-gate-definition))
(ilog2 (length (permutation-gate-definition-permutation gate))))

Expand Down Expand Up @@ -1528,7 +1543,26 @@ For example,
(format stream ":~%")
(print-instruction-sequence (circuit-definition-body defn)
:stream stream
:prefix " ")))
:prefix " "))

(:method ((gate pauli-sum-gate-definition) (stream stream))
(format stream "DEFGATE ~A~@[(~{%~A~^, ~})~]~{ ~A~} AS PAULI-SUM:~%"
(gate-definition-name gate)
(mapcar #'string (pauli-sum-gate-definition-parameters gate))
(mapcar #'formal-name (pauli-sum-gate-definition-arguments gate)))
(dolist (pauli-term (pauli-sum-gate-definition-terms gate))
(with-slots (pauli-word prefactor arguments) pauli-term
(format stream " ~a(" pauli-word)
(typecase prefactor
(number
(format stream "~a" prefactor))
(cons
ecpeterson marked this conversation as resolved.
Show resolved Hide resolved
(print-instruction (make-delayed-expression nil nil prefactor) stream)))
(format stream ")")
(dolist (arg arguments)
(format stream " ")
(print-instruction arg stream))
(terpri stream)))))

(defmethod print-object ((object instruction) stream)
(print-unreadable-object (object stream :type nil :identity nil)
Expand Down
30 changes: 24 additions & 6 deletions src/build-gate.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,32 @@ EXAMPLE: The Quil line \"CPHASE(pi) 2 3\" corresponds to the S-expression (build

(define-global-counter **anonymous-gate-counter** get-anonymous-gate-counter)

(defun anon-gate (operator matrix qubit &rest qubits)
(defun anon-gate (operator-or-gate gate-or-parameters qubit &rest qubits)
Copy link
Member

Choose a reason for hiding this comment

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

@_@

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 agree, and I'd take suggestions as to how to name these two cases so that the functions can be split apart. The two use cases are:

(anon-gate "DUMMY-NAME" matrix qn ... q0)
(anon-gate parametric-gate-defn parameter-list qn ... q0)

"Variant of BUILD-GATE for constructing anonymous gate applications."
ecpeterson marked this conversation as resolved.
Show resolved Hide resolved
(check-type operator string)
(check-type matrix magicl:matrix)
(push qubit qubits)
(let ((name (format nil "~A-~A" operator (get-anonymous-gate-counter))))
(let* ((name
(etypecase operator-or-gate
(string
(format nil "~A-~A" operator-or-gate (get-anonymous-gate-counter)))
(gate
(format nil "~A-~A" (gate-name operator-or-gate) (get-anonymous-gate-counter)))))
(gate
(cond
appleby marked this conversation as resolved.
Show resolved Hide resolved
((typep operator-or-gate 'gate)
operator-or-gate)
((typep gate-or-parameters 'magicl:matrix)
(make-instance 'simple-gate :matrix gate-or-parameters :name name))
(t
(error "Cannot find gate definition."))))
(parameters
(typecase gate-or-parameters
(cons gate-or-parameters)
(otherwise nil))))
(make-instance 'gate-application
:operator (named-operator name)
:gate (make-instance 'simple-gate :matrix matrix :name name)
:arguments (mapcar #'%capture-arg qubits))))
:gate gate
:arguments (mapcar #'%capture-arg qubits)
:parameters parameters)))

(defun repeatedly-fork (op n)
(loop :repeat n
Expand All @@ -74,6 +90,8 @@ EXAMPLE: The Quil line \"CPHASE(pi) 2 3\" corresponds to the S-expression (build
(apply #'build-gate (repeatedly-fork (named-operator roll-name) (length qubits))
params qubit qubits))

(defun pauli-gate (operator &rest pauli-))
ecpeterson marked this conversation as resolved.
Show resolved Hide resolved

;;; functions for dealing with mixed constant vs delayed-expression types

(defun param-binary-op (op arg1 arg2)
Expand Down
2 changes: 2 additions & 0 deletions src/chip/chip-specification.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,8 @@ used to specify CHIP-SPEC."
(constantly 'state-prep-4q-compiler)
(constantly 'state-prep-trampolining-compiler)
(constantly 'recognize-ucr)
(constantly 'parametric-pauli-compiler)
(constantly 'parametric-diagonal-compiler)
(constantly 'nearest-circuit-of-depth-0)
(lambda (chip-spec arch)
(declare (ignore chip-spec))
Expand Down
252 changes: 252 additions & 0 deletions src/compilers/linear-paulis.lisp
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
;;;; linear-paulis.lisp
;;;;
;;;; Author: Eric Peterson
;;;;
;;;; This file contains routines for the parametric compilation of gates defined
;;;; by time-independent Hamiltonians via expression as a
;;;; PAULI-SUM-GATE.

(in-package #:cl-quil)

;; WARNING: this consumes stack space like (* tree-depth fan-out)
(defun tree-substitute (big-tree substitution-table)
(cond
((listp big-tree)
(mapcar (a:rcurry #'tree-substitute substitution-table) big-tree))
((delayed-expression-p big-tree)
(make-delayed-expression
(delayed-expression-params big-tree)
(delayed-expression-lambda-params big-tree)
(tree-substitute (delayed-expression-expression big-tree) substitution-table)))
((assoc big-tree substitution-table)
(cdr (assoc big-tree substitution-table)))
(t
big-tree)))

(defun array-argmax (arr)
ecpeterson marked this conversation as resolved.
Show resolved Hide resolved
ecpeterson marked this conversation as resolved.
Show resolved Hide resolved
(loop :with pos := 0
:with current-max := (aref arr 0)
:for j :from 0
:for item :across arr
:when (< current-max item)
:do (setf current-max item
pos j)
:finally (return pos)))

(define-compiler parametric-diagonal-compiler
((instr _
:where (and (typep (gate-application-gate instr) 'pauli-sum-gate)
(every (lambda (term) (every (lambda (letter) (or (eql letter #\Z) (eql letter #\I)))
ecpeterson marked this conversation as resolved.
Show resolved Hide resolved
(pauli-term-pauli-word term)))
(pauli-sum-gate-terms (gate-application-gate instr))))))
"Decomposes a diagonal Pauli gate by a single step."
(with-slots (arguments parameters terms arity dimension) (gate-application-gate instr)
(let ((nonlocal-terms nil))
;; first, deal with the words with a zero Zs / one Z: they're local gates
ecpeterson marked this conversation as resolved.
Show resolved Hide resolved
(dolist (term terms)
(multiple-value-bind (Z-count Z-position)
(loop :for letter :across (pauli-term-pauli-word term)
:for j :from 0
:with pos := 0
:with count := 0
:when (eql #\Z letter)
:do (setf count (1+ count)
pos j)
:finally (return (values count pos)))
ecpeterson marked this conversation as resolved.
Show resolved Hide resolved
(case Z-count
(0
nil)
(1
(inst "RZ"
(list (param-* 2.0d0
(tree-substitute (pauli-term-prefactor term)
(mapcar (lambda (ep ap)
(typecase ap
(delayed-expression
(cons ep (delayed-expression-expression ap)))
(otherwise
(cons ep ap))))
parameters (application-parameters instr)))))
(nth (position (nth Z-position (pauli-term-arguments term))
arguments :test #'equalp)
(application-arguments instr))))
(otherwise
(push term nonlocal-terms)))))
(let ((votes (make-array (length arguments) :initial-element 0))
vote
control-qubit)
;; we can break nonlocal terms into two collections: those with Zs in
;; some spot and those without Zs in that spot. the result will be to
;; conjugate the first collection by CNOT, which flips those Zs to Is.
;; since we can only emit local gates, almost all such Zs will have to
;; be eliminated, and so we'll want to pick the position so that this
;; group is as large as possible.
(dolist (term nonlocal-terms)
(loop :for letter :across (pauli-term-pauli-word term)
:for argument :in (pauli-term-arguments term)
:when (eql #\Z letter)
:do (incf (aref votes (position argument arguments :test #'equalp)))))
(setf vote (array-argmax votes)
control-qubit (nth vote (application-arguments instr)))
;; now we re-sort the nonlocal terms into two buckets: those with a Z in
;; the voted-upon location, and those without
(let* ((Is nil) (Zs nil))
ecpeterson marked this conversation as resolved.
Show resolved Hide resolved
(dolist (term nonlocal-terms)
(let ((Z-pos (loop :for letter :across (pauli-term-pauli-word term)
:for arg :in (pauli-term-arguments term)
:for j :from 0
:when (and (eql #\Z letter)
ecpeterson marked this conversation as resolved.
Show resolved Hide resolved
(eql vote (position arg arguments :test #'equalp)))
:do (return j))))
(cond
ecpeterson marked this conversation as resolved.
Show resolved Hide resolved
(Z-pos
(push (list term Z-pos) Zs))
notmgsk marked this conversation as resolved.
Show resolved Hide resolved
(t
(push term Is)))))
;; emit the Is
(unless (endp Is)
ecpeterson marked this conversation as resolved.
Show resolved Hide resolved
(let ((I-gate (make-instance 'pauli-sum-gate
:arguments arguments
:parameters parameters
:arity arity
:dimension dimension
:name "Is"
:terms Is)))
(inst* I-gate
(application-parameters instr)
(application-arguments instr))))
;; emit the Zs
(labels
ecpeterson marked this conversation as resolved.
Show resolved Hide resolved
((collect-by-target (term-pairs)
(when (endp term-pairs)
(return-from collect-by-target nil))
(let ((subvotes (make-array (length arguments))))
(loop :for (term Z-pos) :in term-pairs
:do (loop :for letter :across (pauli-term-pauli-word term)
:for j :from 0
:for arg :in (pauli-term-arguments term)
:when (and (not (eql j Z-pos))
(eql #\Z letter))
:do (incf (aref subvotes (position arg arguments :test #'equalp)))))
(let* ((subvote-position (array-argmax subvotes))
(subvote-formal (nth subvote-position arguments))
(subvote-literal (nth subvote-position (application-arguments instr)))
ZZs ZIs)
(dolist (term-pair term-pairs)
(let ((term (car term-pair)))
(cond
((position subvote-formal (pauli-term-arguments term) :test #'equalp)
(push term-pair ZZs))
(t
(push term-pair ZIs)))))
(let* ((ZZ-terms
(loop :for (term Z-pos) :in ZZs
:collect (make-pauli-term
:prefactor (pauli-term-prefactor term)
:arguments (pauli-term-arguments term)
:pauli-word (coerce (loop :for letter :across (pauli-term-pauli-word term)
:for j :from 0
:if (eql j Z-pos)
:collect #\I
:else
:collect letter)
'string))))
(Z-gate (make-instance 'pauli-sum-gate
:arguments arguments
:parameters parameters
:arity arity
:dimension dimension
:terms ZZ-terms
:name "ZZ-GATE")))
(inst "CNOT" () control-qubit subvote-literal)
(inst* Z-gate
(application-parameters instr)
(application-arguments instr))
(inst "CNOT" () control-qubit subvote-literal))
(collect-by-target ZIs)))))
(collect-by-target Zs)))))))


;; TODO: also write an orthogonal gate compiler somewhere? approx.lisp will take
;; care of it in the 2Q case, at least.

(define-compiler parametric-pauli-compiler
((instr _ :where (and (typep (gate-application-gate instr) 'pauli-sum-gate)
(= 1 (length (application-parameters instr)))
(not (typep (first (application-parameters instr)) 'constant)))))
"Decomposes a gate described by the exponential of a time-independent Hamiltonian into static orthogonal and parametric diagonal components."
(let ((gate (gate-application-gate instr)))
(with-slots (arguments parameters terms dimension) gate

(labels ((crawl-parameter (p)
(typecase p
(list
(case (first p)
(-
(unless (= 2 (length p))
(give-up-compilation))
(crawl-parameter (second p)))
(*
(unless (= 3 (length p))
(give-up-compilation))
(let ((total (+ (crawl-parameter (second p))
(crawl-parameter (third p)))))
(unless (<= total 1)
(give-up-compilation))
total))
(otherwise
(give-up-compilation))))
(number
0)
(symbol
1)
(otherwise
(give-up-compilation)))))
(dolist (term terms)
ecpeterson marked this conversation as resolved.
Show resolved Hide resolved
(unless (= 1 (crawl-parameter (pauli-term-prefactor term)))
(give-up-compilation))))

;; instantiate the Hamiltonian
(let ((H (magicl:make-zero-matrix dimension dimension)))
(dolist (term terms)
(setf H (m+ H (pauli-term->matrix term arguments (list 1d0) parameters))))
;; orthogonally diagonalize it: H = O D O^T
(multiple-value-bind (diagonal O) (magicl:hermitian-eig H)
(let ((O (orthonormalize-matrix O)))
ecpeterson marked this conversation as resolved.
Show resolved Hide resolved
;; convert diagonal into a sum of Z paulis
(let ((pauli-prefactors (make-array dimension :initial-element 0d0))
terms diagonal-gate)
(loop :for d :in diagonal
:for i :from 0
:do (dotimes (j dimension)
(incf (aref pauli-prefactors j)
(if (evenp (logcount (logand i j)))
(/ d dimension)
(/ (- d) dimension)))))
(setf terms (loop :for prefactor :across pauli-prefactors
:for j :from 0
:unless (double= 0d0 prefactor)
:collect (let ((term-arguments
(loop :for i :below (length arguments)
:for arg :in arguments
:when (logbitp (- (length arguments) i 1) j)
:collect arg)))
(make-pauli-term
:prefactor (param-* (realpart prefactor)
(make-delayed-expression
nil nil (first parameters)))
:arguments term-arguments
:pauli-word (coerce (make-array (length term-arguments)
:initial-element #\Z)
'string))))
diagonal-gate (make-instance 'pauli-sum-gate
:arguments arguments
:parameters parameters
:terms terms
:arity 1
:dimension dimension
:name (string (gensym "DIAG-PAULI-"))))
;; emit the instructions
(inst* "RIGHT-O-T" (magicl:conjugate-transpose O) (application-arguments instr))
(inst* diagonal-gate (application-parameters instr) (application-arguments instr))
(inst* "LEFT-O" O (application-arguments instr)))))))))
5 changes: 4 additions & 1 deletion src/compilers/ucr-recognize.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
(define-compiler recognize-ucr ((instr
:where (anonymous-gate-application-p instr)))
"Checks whether an anonymous gate is a UCRY or UCRZ instruction, in which case it relabels it as such."
(let* ((matrix (gate-matrix instr))
(let* ((matrix (handler-case (gate-matrix instr)
(unknown-gate-parameter (c)
(declare (ignore c))
(give-up-compilation))))
(dimension (magicl:matrix-rows matrix))
(log-dimension (length (application-arguments instr)))
angles)
Expand Down
Loading