Skip to content

Commit 623a976

Browse files
authored
add depolarizing stabilizer QVM and error-only QVM (quil-lang#301)
add depolarizing stabilizer QVM and error-only QVM
1 parent e4abbf9 commit 623a976

File tree

9 files changed

+747
-34
lines changed

9 files changed

+747
-34
lines changed

qvm-tests.asd

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,5 @@
4242
(:file "basic-noise-qvm-tests")
4343
(:file "unitary-tests")
4444
(:file "parallel-tests")
45-
(:file "qvm-avx-intrinsics" :if-feature (:and :qvm-intrinsics :avx2))))
45+
(:file "qvm-avx-intrinsics" :if-feature (:and :qvm-intrinsics :avx2))
46+
(:file "error-qvm-tests")))

qvm.asd

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,9 @@
9292
(:file "density-qvm")
9393
(:file "noisy-qvm")
9494
(:file "depolarizing-noise")
95-
(:file "unitary-qvm")))
95+
(:file "unitary-qvm")
96+
(:module "error"
97+
:serial t
98+
:components ((:file "package")
99+
(:file "fowler-noise")
100+
(:file "error-qvm")))))

src/depolarizing-noise.lisp

Lines changed: 125 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -30,44 +30,45 @@
3030
:accessor probability-measure-z
3131
:documentation "Probability of a Pauli Z gate happening before a measurement."))
3232
(:documentation "A quantum virtual machine with parametric depolarizing noise.")
33-
(:default-initargs :x 0.0
34-
:y 0.0
35-
:z 0.0
36-
:measure-x 0.0
37-
:measure-y 0.0
38-
:measure-z 0.0))
33+
(:default-initargs :x 0.0d0
34+
:y 0.0d0
35+
:z 0.0d0
36+
:measure-x 0.0d0
37+
:measure-y 0.0d0
38+
:measure-z 0.0d0))
3939

40-
(defun add-depolarizing-noise (qvm qubits px py pz)
41-
"Apply depolarizing noise to the list QUBITS of the QVM with the following probabilities:
40+
(defgeneric add-depolarizing-noise (qvm qubits px py pz)
41+
(:documentation
42+
"Apply depolarizing noise to the list QUBITS of the QVM with the following probabilities:
4243
4344
* Probability of an X-gate PX,
4445
* Probability of a Y-gate PY, and
4546
* Probability of a Z-gate PZ.
4647
47-
It should be that PX + PY + PZ <= 1.
48-
"
49-
(assert (<= (+ px py pz) 1))
50-
(let ((X (quil:gate-definition-to-gate (quil:lookup-standard-gate "X")))
51-
(Y (quil:gate-definition-to-gate (quil:lookup-standard-gate "Y")))
52-
(Z (quil:gate-definition-to-gate (quil:lookup-standard-gate "Z")))
53-
(sum (+ px py pz)))
54-
(probabilistically sum
55-
(setf px (/ px sum)
56-
py (/ py sum)
57-
pz (/ pz sum))
58-
(let ((r (random 1.0))
59-
(pure-state (state qvm)))
60-
(when (< r px)
61-
(apply-gate-to-state X pure-state qubits)
62-
(return-from add-depolarizing-noise))
63-
(decf r px)
64-
(when (< r py)
65-
(apply-gate-to-state Y pure-state qubits)
66-
(return-from add-depolarizing-noise))
67-
(decf r py)
68-
(when (< r pz)
69-
(apply-gate-to-state Z pure-state qubits)
70-
(return-from add-depolarizing-noise))))))
48+
It should be that PX + PY + PZ <= 1.")
49+
(:method ((qvm pure-state-qvm) qubits px py pz)
50+
(assert (<= (+ px py pz) 1))
51+
(let ((X (quil:gate-definition-to-gate (quil:lookup-standard-gate "X")))
52+
(Y (quil:gate-definition-to-gate (quil:lookup-standard-gate "Y")))
53+
(Z (quil:gate-definition-to-gate (quil:lookup-standard-gate "Z")))
54+
(sum (+ px py pz)))
55+
(probabilistically sum
56+
(setf px (/ px sum)
57+
py (/ py sum)
58+
pz (/ pz sum))
59+
(let ((r (random 1.0d0))
60+
(pure-state (state qvm)))
61+
(when (< r px)
62+
(apply-gate-to-state X pure-state qubits)
63+
(return-from add-depolarizing-noise))
64+
(decf r px)
65+
(when (< r py)
66+
(apply-gate-to-state Y pure-state qubits)
67+
(return-from add-depolarizing-noise))
68+
(decf r py)
69+
(when (< r pz)
70+
(apply-gate-to-state Z pure-state qubits)
71+
(return-from add-depolarizing-noise)))))))
7172

7273
;;; Noise gets added to only the qubits being changed.
7374
(defmethod transition :after ((qvm depolarizing-qvm) (instr quil:application))
@@ -100,3 +101,95 @@ It should be that PX + PY + PZ <= 1.
100101
;;; Don't compile things for the depolarizing-qvm.
101102
(defmethod compile-loaded-program ((qvm depolarizing-qvm))
102103
qvm)
104+
105+
;;;
106+
;;; for want of a better idea, we replicate most of the above for stabilizer QVMs
107+
;;;
108+
109+
(defclass depolarizing-stabilizer-qvm (stabilizer-qvm)
110+
((probability-gate-x
111+
:initarg :x
112+
:accessor probability-gate-x
113+
:documentation "Probability of a Pauli X gate happening after a gate application or reset.")
114+
(probability-gate-y
115+
:initarg :y
116+
:accessor probability-gate-y
117+
:documentation "Probability of a Pauli Y gate happening after a gate application or reset.")
118+
(probability-gate-z
119+
:initarg :z
120+
:accessor probability-gate-z
121+
:documentation "Probability of a Pauli Z gate happening after a gate application or reset.")
122+
(probability-measure-x
123+
:initarg :measure-x
124+
:accessor probability-measure-x
125+
:documentation "Probability of a Pauli X gate happening before a measurement.")
126+
(probability-measure-y
127+
:initarg :measure-y
128+
:accessor probability-measure-y
129+
:documentation "Probability of a Pauli Y gate happening before a measurement.")
130+
(probability-measure-z
131+
:initarg :measure-z
132+
:accessor probability-measure-z
133+
:documentation "Probability of a Pauli Z gate happening before a measurement."))
134+
(:documentation "A quantum virtual machine with parametric depolarizing noise.")
135+
(:default-initargs :x 0.0d0
136+
:y 0.0d0
137+
:z 0.0d0
138+
:measure-x 0.0d0
139+
:measure-y 0.0d0
140+
:measure-z 0.0d0))
141+
142+
(defmethod transition :after ((qvm depolarizing-stabilizer-qvm) (instr cl-quil:application))
143+
(dolist (arg (cl-quil:application-arguments instr))
144+
(when (typep arg 'cl-quil:qubit)
145+
(let ((instr-qubits (quil:qubit-index arg)))
146+
(add-depolarizing-noise qvm (list instr-qubits)
147+
(probability-gate-x qvm)
148+
(probability-gate-y qvm)
149+
(probability-gate-z qvm))))))
150+
151+
(defmethod transition :after ((qvm depolarizing-stabilizer-qvm) (instr cl-quil:reset))
152+
(declare (ignore instr))
153+
(dotimes (q (number-of-qubits qvm))
154+
(add-depolarizing-noise qvm (list q)
155+
(probability-gate-x qvm)
156+
(probability-gate-y qvm)
157+
(probability-gate-z qvm))))
158+
159+
(defmethod transition :before ((qvm depolarizing-stabilizer-qvm) (instr cl-quil:measurement))
160+
(let ((q (cl-quil:qubit-index (cl-quil:measurement-qubit instr))))
161+
(add-depolarizing-noise qvm (list q)
162+
(probability-measure-x qvm)
163+
(probability-measure-y qvm)
164+
(probability-measure-z qvm))))
165+
166+
(defmethod compile-loaded-program ((qvm depolarizing-stabilizer-qvm))
167+
qvm)
168+
169+
(defmethod add-depolarizing-noise ((qvm stabilizer-qvm) qubits px py pz)
170+
(flet ((apply-gate (instr)
171+
(let ((clifford (gate-application-to-clifford instr)))
172+
(apply (compile-clifford clifford)
173+
(stabilizer-qvm-tableau qvm)
174+
qubits))))
175+
(assert (<= (+ px py pz) 1))
176+
(let ((X (apply #'quil::build-gate "X" () qubits))
177+
(Y (apply #'quil::build-gate "Y" () qubits))
178+
(Z (apply #'quil::build-gate "Z" () qubits))
179+
(sum (+ px py pz)))
180+
(probabilistically sum
181+
(setf px (/ px sum)
182+
py (/ py sum)
183+
pz (/ pz sum))
184+
(let ((r (random 1.0d0)))
185+
(when (< r px)
186+
(apply-gate X)
187+
(return-from add-depolarizing-noise))
188+
(decf r px)
189+
(when (< r py)
190+
(apply-gate Y)
191+
(return-from add-depolarizing-noise))
192+
(decf r py)
193+
(when (< r pz)
194+
(apply-gate Z)
195+
(return-from add-depolarizing-noise)))))))

src/error/README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# `qvm.error`
2+
3+
This subpackage implements two classes:
4+
5+
1. `fowler-qvm`: An abstract class which decomposes depolarizing noise into five
6+
subchannels which are applied following (1) single-qubit identity gates,
7+
(2) non-identity single-qubit gates, (3) two-qubit gates, (4) reset
8+
operators, and (5) measurement operators. These can be selective enabled,
9+
permitting one to study the relative effects of these noise sources on
10+
measurement circuits commonly found in parity check measurement circuits, as
11+
proposed in Section VII.B of [Fowler et al](https://arxiv.org/abs/1208.0928).
12+
This class is extended by two subclasses, `fowler-pure-state-qvm` and
13+
`fowler-stabilizer-qvm`, which back this noise model by a `pure-state-qvm`
14+
and a `stabilizer-qvm` respectively.
15+
2. `error-qvm`: A separate subclass of `fowler-qvm` backed by "Pauli
16+
propagation". Rather than tracking anything like the full state of a quantum
17+
circuit, a Pauli propagation model rather assumes that the circuit being
18+
executed is meant to measure stabilizer checks (so, in particular, would
19+
return a constant value of "no error" in the absence of noise), injects Pauli
20+
flip noise according to some noise model, and tracks how the flips propagate
21+
through the circuit to effect an eventual measurement. This limitation is
22+
severe, but it means that the measurement results can be computed in space
23+
and time which are _linear_ in the qubit count, a marked improvement over
24+
even the stabilizer-/tableau-based QVM.

src/error/error-qvm.lisp

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
;;;; error-qvm.lisp
2+
;;;;
3+
;;;; This file implements an efficient QVM which tracks the propagation of Pauli
4+
;;;; errors through a CNOT-dihedral circuit which, in the absence of errors, is
5+
;;;; always meant to MEASURE zeroes. For example, surface code cycles for the
6+
;;;; usual toric code have this property.
7+
8+
(in-package #:qvm.error)
9+
10+
(defclass error-qvm (fowler-qvm classical-memory-mixin)
11+
((X-vector
12+
:accessor error-qvm-X-vector
13+
:type simple-bit-vector
14+
:documentation "A bit-vector which tracks whether this qubit has experienced an X-error.")
15+
(Z-vector
16+
:accessor error-qvm-Z-vector
17+
:type simple-bit-vector
18+
:documentation "A bit-vector which tracks whether this qubit has experienced a Z-error.")
19+
(num-qubits
20+
:type unsigned-byte
21+
:reader error-qvm-num-qubits
22+
:initarg :num-qubits))
23+
(:documentation "A noisy QVM that can efficiently the propagation of Pauli errors through gates drawn from the CNOT-dihedral group of order 8. See FOWLER-QVM for a description of the noise model and parameters."))
24+
25+
(defun make-error-qvm (num-qubits
26+
&key
27+
(classical-memory-model quil::**empty-memory-model**)
28+
(noise-probability 0.0d0)
29+
(noise-class 0))
30+
"Constructs a fresh instance of a specialized QVM which efficiently simulates Pauli error propagation through CNOT-dihedral circuits.
31+
32+
See ERROR-QVM for a description of the noise model and noise keyword arguments."
33+
(check-type num-qubits unsigned-byte)
34+
(check-type classical-memory-model quil:memory-model)
35+
(let* ((subsystem (make-instance 'classical-memory-subsystem
36+
:classical-memory-model
37+
classical-memory-model))
38+
(qvm (make-instance 'error-qvm
39+
:num-qubits num-qubits
40+
:classical-memory-subsystem subsystem
41+
:noise-probability noise-probability
42+
:noise-class noise-class)))
43+
(setf (error-qvm-X-vector qvm) (make-array num-qubits :element-type 'bit :initial-element 0)
44+
(error-qvm-Z-vector qvm) (make-array num-qubits :element-type 'bit :initial-element 0))
45+
qvm))
46+
47+
(defmethod number-of-qubits ((qvm error-qvm))
48+
(error-qvm-num-qubits qvm))
49+
50+
;;;;;;;;;;;;;;;;;;;;;;;;; TRANSITION Methods ;;;;;;;;;;;;;;;;;;;;;;;;;
51+
52+
(defmethod measure ((qvm error-qvm) q)
53+
(values qvm (aref (error-qvm-X-vector qvm) q)))
54+
55+
(defmethod transition ((qvm error-qvm) (instr quil:reset))
56+
(let ((num-qubits (number-of-qubits qvm)))
57+
(setf (error-qvm-X-vector qvm) (make-array num-qubits :element-type 'bit :initial-element 0)
58+
(error-qvm-Z-vector qvm) (make-array num-qubits :element-type 'bit :initial-element 0))
59+
(incf (qvm::pc qvm))
60+
qvm))
61+
62+
(defmethod transition ((qvm error-qvm) (instr quil:reset-qubit))
63+
(let ((index (quil:qubit-index (quil:reset-qubit-target instr))))
64+
(setf (aref (error-qvm-X-vector qvm) index) 0
65+
(aref (error-qvm-Z-vector qvm) index) 0))
66+
(incf (qvm::pc qvm))
67+
qvm)
68+
69+
(defmethod transition ((qvm error-qvm) (instr quil:measure))
70+
(incf (qvm::pc qvm))
71+
(qvm::measure-and-store qvm
72+
(quil:qubit-index (quil:measurement-qubit instr))
73+
(quil:measure-address instr)))
74+
75+
(defmethod transition ((qvm error-qvm) (instr quil:measure-discard))
76+
(incf (qvm::pc qvm))
77+
(measure qvm (quil:qubit-index (quil:measurement-qubit instr))))
78+
79+
(defmethod transition ((qvm error-qvm) (instr quil:gate-application))
80+
(check-type (quil:application-operator instr) quil:named-operator)
81+
(adt:with-data (quil:named-operator name) (quil:application-operator instr)
82+
(cond
83+
;; intentional toggles do nothing
84+
((or (string= "X" name)
85+
(string= "Y" name)
86+
(string= "Z" name)
87+
(string= "I" name))
88+
nil)
89+
;; hadamards trade X and Z
90+
((string= "H" name)
91+
(let* ((index (quil:qubit-index (first (quil:application-arguments instr))))
92+
(X (aref (error-qvm-X-vector qvm) index))
93+
(Z (aref (error-qvm-Z-vector qvm) index)))
94+
(setf (aref (error-qvm-X-vector qvm) index) Z
95+
(aref (error-qvm-Z-vector qvm) index) X)))
96+
;; CNOTs either transmit or replicate pauli errors.
97+
;; Xs and Zs replicate when present on control and target respectively
98+
((string= "CNOT" name)
99+
(let* ((control (quil:qubit-index (first (quil:application-arguments instr))))
100+
(target (quil:qubit-index (second (quil:application-arguments instr))))
101+
(Xc (aref (error-qvm-X-vector qvm) control))
102+
(Xt (aref (error-qvm-X-vector qvm) target))
103+
(Zc (aref (error-qvm-Z-vector qvm) control))
104+
(Zt (aref (error-qvm-Z-vector qvm) target)))
105+
(when (plusp Xc)
106+
(setf (aref (error-qvm-X-vector qvm) target)
107+
(logxor Xt #b1)))
108+
(when (plusp Zt)
109+
(setf (aref (error-qvm-Z-vector qvm) control)
110+
(logxor Zc #b1)))))
111+
(t
112+
(error "Gate ~a is not CNOT-dihedral." instr))))
113+
(incf (qvm::pc qvm))
114+
qvm)
115+
116+
;;;;;;;;;;;;;;;;;;;;;;;;; FOWLER-QVM Methods ;;;;;;;;;;;;;;;;;;;;;;;;;
117+
118+
(defmethod copy-fowler-qvm ((qvm error-qvm))
119+
(let ((new-qvm
120+
(make-instance 'error-qvm
121+
:noise-class (fowler-qvm-noise-class qvm)
122+
:noise-probability (fowler-qvm-noise-probability qvm)
123+
:num-qubits (error-qvm-num-qubits qvm)
124+
:gate-definitions (qvm::gate-definitions qvm)
125+
:wait-function (qvm::wait-function qvm)
126+
:classical-memory-subsystem (qvm::classical-memory-subsystem qvm))))
127+
(setf (error-qvm-X-vector new-qvm) (copy-seq (error-qvm-X-vector qvm))
128+
(error-qvm-Z-vector new-qvm) (copy-seq (error-qvm-Z-vector qvm)))
129+
new-qvm))
130+
131+
(defmethod %fast-apply ((qvm error-qvm) instr-name qubit)
132+
(cond
133+
((string= "X" instr-name)
134+
(setf (aref (error-qvm-X-vector qvm) qubit)
135+
(logxor #b1 (aref (error-qvm-X-vector qvm) qubit))))
136+
((string= "Z" instr-name)
137+
(setf (aref (error-qvm-Z-vector qvm) qubit)
138+
(logxor #b1 (aref (error-qvm-Z-vector qvm) qubit))))
139+
((string= "Y" instr-name)
140+
(setf (aref (error-qvm-X-vector qvm) qubit)
141+
(logxor #b1 (aref (error-qvm-X-vector qvm) qubit))
142+
(aref (error-qvm-Z-vector qvm) qubit)
143+
(logxor #b1 (aref (error-qvm-Z-vector qvm) qubit))))
144+
(t
145+
(error "Bad instruction in %FAST-APPLY: ~a" instr-name))))

0 commit comments

Comments
 (0)