4
4
5
5
(in-package # :dqvm2)
6
6
7
- ; ;; A simple implementation of a permutation data structure.
8
-
9
- ; ;; Note that (make-permutation) and NIL both represent the identity.
7
+ ; ;; Permutation classes for permuting sets of qubits.
8
+ ; ;;
9
+ ; ;; The value NIL represents the identity permutation. General permutations
10
+ ; ;; are embodied by the PERMUTATION-GENERAL class. Permutations involving a
11
+ ; ;; single transposition swapping 0 with another qubit are represented by the
12
+ ; ;; PERMUTATION-TRANSPOSITION class.
13
+ ; ;;
14
+ ; ;; The generic function APPLY-QUBIT-PERMUTATION does the heavy lifting of
15
+ ; ;; permuting addresses and the class hierarchy laid out here allows us to
16
+ ; ;; accomplish significant speed-ups (applying a PERMUTATION-TRANSPOSITION is
17
+ ; ;; more than three times faster than the equivalent application of a
18
+ ; ;; PERMUTATION-GENERAL object).
10
19
11
20
(deftype transposition ()
12
21
' (or null (cons alexandria :non-negative-fixnum
13
22
alexandria :non-negative-fixnum)))
14
23
15
24
(defclass permutation ()
25
+ ()
26
+ (:documentation " Base class for permutations." ))
27
+
28
+ (defclass permutation-general (permutation)
16
29
((number-of-transpositions
17
- :initarg :number-of-transpositions
18
30
:type alexandria :non-negative-integer
31
+ :initarg :number-of-transpositions
19
32
:documentation " Number of transpositions defining the permutation." )
20
33
(transpositions
21
- :initarg :transpositions
22
34
:type list
35
+ :initarg :transpositions
23
36
:reader permutation-transpositions
24
37
:documentation " Bijective map determined by transpositions, stored as an association list sorted by CAR." ))
25
38
(:default-initargs
26
39
:transpositions nil )
27
- (:documentation " Permutation acting on sets of qubit indices." ))
40
+ (:documentation " Arbitrary permutation acting on sets of qubit indices." ))
41
+
42
+ (defclass permutation-transposition ()
43
+ ((tau
44
+ :type (unsigned-byte 6 ) ; Implies a maximum of 2⁶ = 64 qubits.
45
+ :initarg :tau
46
+ :initform (error-missing-initform :tau )
47
+ :documentation " Positive value of τ in π = (0 τ)." ))
48
+ (:documentation " Specialized permutation involving a single transposition of the form π = (0 τ) where τ ≠ 0." ))
49
+
50
+ (defmethod permutation-transpositions ((permutation permutation-transposition))
51
+ (let ((tau (slot-value permutation ' tau)))
52
+ (list (cons 0 tau) (cons tau 0 ))))
28
53
29
- (defmethod print-object ((permutation permutation) stream )
54
+ (defmethod print-object ((permutation permutation-general ) stream )
30
55
(print-unreadable-object (permutation stream :type t :identity t )
31
56
(let ((transpositions (permutation-transpositions permutation)))
32
57
(format stream " ~:[ ~:A ~;~{ ~A ~^ ~}~] " transpositions transpositions))))
33
58
59
+ (defmethod print-object ((permutation permutation-transposition) stream )
60
+ (print-unreadable-object (permutation stream :type t :identity t )
61
+ (let ((tau (slot-value permutation ' tau)))
62
+ (format stream " (0 . ~D ) (~D . 0)" tau tau))))
63
+
34
64
(defun-inlinable make-permutation (&optional transpositions)
35
65
" Allocate a permutation defined by TRANSPOSITIONS.
36
66
@@ -43,11 +73,10 @@ DQVM2> (make-permutation '((2 . 1) (1 . 0)))
43
73
#<permutation (0 . 2) (1 . 0) (2 . 1) {10086BB8B3}>
44
74
45
75
Note that in the example above, the transposition (0 2) was automatically added."
46
- (declare ( optimize ( speed 3 ) ( safety 0 ))
76
+ (declare #. qvm:: * optimize-dangerously-fast*
47
77
(type list transpositions))
48
78
49
- (let ((permutation (make-instance ' permutation))
50
- (transpositions* nil )
79
+ (let ((transpositions* nil )
51
80
(domain nil )
52
81
(codomain nil ))
53
82
@@ -59,6 +88,8 @@ Note that in the example above, the transposition (0 2) was automatically added.
59
88
(error " Malformed permutation. A mapping ~D ↦ ~D already existed."
60
89
(first z) (rest z))))))
61
90
91
+ (declare (inline check-transposition))
92
+
62
93
(loop :for (a . b) :in transpositions :do
63
94
(check-transposition a b)
64
95
(unless (= a b)
@@ -69,93 +100,85 @@ Note that in the example above, the transposition (0 2) was automatically added.
69
100
(loop :for a :of-type alexandria :non-negative-fixnum
70
101
:in (set-difference codomain domain)
71
102
:for b :of-type alexandria :non-negative-fixnum
72
- :in (nset -difference domain codomain)
103
+ :in (set -difference domain codomain)
73
104
:unless (= a b) :do
74
105
(pushnew (cons a b) transpositions* :test #' equal ))
75
106
76
- (setf (slot-value permutation ' number-of-transpositions) (length transpositions*)
77
- (slot-value permutation ' transpositions) (sort transpositions* #' < :key #' first ))
78
-
79
- permutation))
80
-
81
- (defun-inlinable inverse-permutation (permutation)
82
- " Return the inverse of PERMUTATION."
83
- (declare (optimize (speed 3 ) (safety 0 )))
84
- (when permutation
85
-
86
- (let ((inverse-permutation (make-instance ' permutation))
87
- (transpositions (permutation-transpositions permutation)))
88
-
89
- (setf (slot-value inverse-permutation ' transpositions) (loop :for (a . b) :in transpositions :collect (cons b a))
90
- (slot-value inverse-permutation ' number-of-transpositions) (slot-value permutation ' number-of-transpositions))
91
-
92
- inverse-permutation)))
93
-
94
- (defun is-identity-permutation-p (permutation)
95
- " Return T if PERMUTATION is the identity, NIL otherwise."
96
- (if (or (null permutation) (null (permutation-transpositions permutation)))
97
- t
98
- nil ))
99
-
100
- (defun-inlinable apply-permutation (permutation item)
101
- " Apply PERMUTATION to ITEM.
102
-
103
- Examples
104
- --------
105
-
106
- DQVM2> (apply-permutation (make-permutation) 42)
107
- 42
108
-
109
- DQVM2> (apply-permutation (make-permutation '((2 . 0))) 2)
110
- 0
111
-
112
- DQVM2> (apply-permutation (make-permutation '((2 . 1) (1 . 0))) 2)
113
- 1"
114
- (declare (optimize (speed 3 ) (safety 0 ))
115
- (type (or null permutation) permutation)
116
- (type alexandria :non-negative-fixnum item))
117
- (the alexandria :non-negative-fixnum
118
- (if permutation
119
- (alexandria :if-let ((transposition (assoc item (permutation-transpositions permutation))))
120
- (rest transposition)
121
- item)
122
- item)))
123
-
124
- (defun-inlinable apply-inverse-permutation (permutation item)
125
- " Apply PERMUTATION⁻¹ to ITEM."
126
- (apply-permutation (inverse-permutation permutation) item))
107
+ (cond
108
+ ((and (null domain) (null codomain)) nil )
109
+ ((and (= 1 (length domain))
110
+ (zerop (min (the qvm :amplitude-address (first domain))
111
+ (the qvm :amplitude-address (first codomain)))))
112
+ (make-instance ' permutation-transposition
113
+ :tau (max (the qvm :amplitude-address (first domain))
114
+ (the qvm :amplitude-address (first codomain)))))
115
+ ((and (= 2 (length domain))
116
+ (null (set-difference domain codomain))
117
+ (zerop (the qvm :amplitude-address (apply #' min domain))))
118
+ (make-instance ' permutation-transposition :tau (apply #' max domain)))
119
+ (t
120
+ (make-instance ' permutation-general :number-of-transpositions (length transpositions*)
121
+ :transpositions (sort transpositions* #' < :key #' first ))))))
122
+
123
+ (defgeneric inverse-permutation (permutation)
124
+ (:documentation " Return the inverse of PERMUTATION." )
125
+ (declare #. qvm::*optimize-dangerously-fast* ))
126
+
127
+ (defmethod inverse-permutation ((permutation (eql nil )))
128
+ nil )
129
+
130
+ (defmethod inverse-permutation ((permutation permutation-transposition))
131
+ permutation)
132
+
133
+ (defmethod inverse-permutation ((permutation permutation-general))
134
+ (make-instance ' permutation-general
135
+ :transpositions (loop :for (a . b) :in (permutation-transpositions permutation) :collect (cons b a))
136
+ :number-of-transpositions (slot-value permutation ' number-of-transpositions)))
137
+
138
+ (defgeneric is-identity-permutation-p (permutation)
139
+ (:documentation " Return T if PERMUTATION is the identity, NIL otherwise." ))
140
+
141
+ (defmethod is-identity-permutation-p ((permutation (eql nil )))
142
+ t )
143
+
144
+ (defmethod is-identity-permutation-p ((permutation permutation-transposition))
145
+ nil ) ; By construction PERMUTATION-TRANSPOSITION objects cannot be the identity.
146
+
147
+ (defmethod is-identity-permutation-p ((permutation permutation-general))
148
+ (null (permutation-transpositions permutation)))
127
149
128
150
(defun compose-permutations (&rest permutations)
129
151
" Return a new permutation that is the composition of PERMUTATIONS.
130
152
131
153
If PERMUTATIONS is the list π₁, π₂, ..., πₛ, then the result is the composition π₁ ∘ π₂ ∘ ... ∘ πₛ. In other words, the composition starts from right to left as in standard mathematical notation."
132
- (let (transpositions)
133
-
134
- ( let (domain)
135
- ; ; Aggregate the domain of the composed permutation to get a list of
136
- ; ; all possible relevant inputs.
137
- (loop :for permutation :in permutations :when permutation :do
138
- (loop :for transposition :of-type transposition :in (permutation-transpositions permutation) :do
139
- (let ((a (first transposition)))
140
- (declare (type alexandria :non-negative-fixnum a))
141
- (pushnew a domain))))
142
-
143
- ; ; Now map each domain element to obtain transpositions.
144
- (loop :with codomain := (coerce domain ' vector)
145
- :for permutation :in (nreverse permutations) :when permutation :do
146
- (loop :for i :from 0 :for b :across codomain :do
147
- (setf (aref codomain i)
148
- (apply-permutation permutation (aref codomain i))))
149
- :finally
150
- (loop :for a :of-type alexandria :non-negative-fixnum :in domain
151
- :for b :of-type alexandria :non-negative-fixnum :across codomain
152
- :unless (= a b) :do
153
- (pushnew (cons a b) transpositions :test #' equal ) )))
154
+ (let (( transpositions nil )
155
+ (domain nil ))
156
+
157
+ ; ; Aggregate the domain of the composed permutation to get a list of
158
+ ; ; all possible relevant inputs.
159
+ (loop :for permutation :in permutations :when permutation :do
160
+ (loop :for transposition :of-type transposition :in (permutation-transpositions permutation) :do
161
+ (let ((a (first transposition)))
162
+ (declare (type alexandria :non-negative-fixnum a))
163
+ (pushnew a domain))))
164
+
165
+ ; ; Now map each domain element to obtain transpositions.
166
+ (loop :with codomain := (coerce domain ' vector)
167
+ :for permutation :in (nreverse permutations) :when permutation :do
168
+ (loop :for i :from 0 :for b :across codomain :do
169
+ (setf (aref codomain i)
170
+ (apply-permutation permutation (aref codomain i))))
171
+ :finally
172
+ (loop :for a :of-type alexandria :non-negative-fixnum :in domain
173
+ :for b :of-type alexandria :non-negative-fixnum :across codomain
174
+ :unless (= a b) :do
175
+ (pushnew (cons a b) transpositions :test #' equal )))
154
176
155
177
(make-permutation transpositions)))
156
178
157
- (defun-inlinable apply-qubit-permutation (permutation address)
158
- " Apply PERMUTATION to an index ADDRESS within a wavefunction.
179
+ (defgeneric apply-qubit-permutation (permutation address)
180
+ (:documentation
181
+ " Apply PERMUTATION to an index ADDRESS within a wavefunction.
159
182
160
183
Examples
161
184
--------
@@ -165,36 +188,56 @@ DQVM2> (apply-qubit-permutation (make-permutation '((2 . 0))) #b100)
165
188
166
189
DQVM2> (write (apply-qubit-permutation (make-permutation '((2 . 0))) #b001) :base 2)
167
190
100
168
- 4"
191
+ 4" )
192
+ (declare #. qvm::*optimize-dangerously-fast* ))
193
+
194
+ (defmethod apply-qubit-permutation ((permutation (eql nil )) address)
195
+ address)
196
+
197
+ (defmethod apply-qubit-permutation ((permutation permutation-transposition) address)
198
+ (declare #. qvm::*optimize-dangerously-fast*
199
+ (type (or null permutation) permutation)
200
+ ; ; (type qvm:amplitude-address address)
201
+ (type (unsigned-byte 64 ) address) ; Imposed maximum number of qubits.
202
+ (values qvm :amplitude-address))
203
+
204
+ (let ((tau (slot-value permutation ' tau)))
205
+ (declare (type (unsigned-byte 6 ) tau))
206
+
207
+ (rotatef (ldb (byte 1 0 ) address) (ldb (byte 1 tau) address))
208
+ address))
209
+
210
+ (defmethod apply-qubit-permutation ((permutation permutation) address)
169
211
; ; Alternatively, in-place permutations could be implemented following:
170
212
; ;
171
213
; ; F. Fich, J. Munro, and P. Poblete, “Permuting in Place,” SIAM
172
214
; ; J. Comput., vol. 24, no. 2, pp. 266–278, Apr. 1995.
173
215
174
- (declare ( optimize ( speed 3 ) ( safety 0 ))
216
+ (declare #. qvm:: * optimize-dangerously-fast*
175
217
(type (or null permutation) permutation)
176
- (type qvm :amplitude-address address))
177
-
178
- (the qvm :amplitude-address
179
- (if permutation
180
- (let* ((transpositions (slot-value permutation ' transpositions))
181
- (number-of-transpositions (slot-value permutation ' number-of-transpositions))
182
- (bit-vector (make-array number-of-transpositions :element-type ' bit)))
183
- ; ; (declare (dynamic-extent bit-vector))
184
-
185
- (loop :for index :from 0
186
- :for transposition :in transpositions :do
187
- (setf (bit bit-vector index) (ldb (byte 1 (first transposition))
188
- address)))
189
-
190
- (loop :for index :from 0
191
- :for transposition :of-type transposition :in transpositions :do
192
- (setf address (dpb (bit bit-vector index)
193
- ; ; (byte 1 (the (unsigned-byte 6) (rest transposition))) ; Enable this for speed (assumes a maximum of 64 qubits).
194
- (byte 1 (rest transposition))
195
- address))
196
- :finally (return address)))
197
- address)))
218
+ ; ; (type qvm:amplitude-address address)
219
+ (type (unsigned-byte 64 ) address) ; Imposed maximum number of qubits.
220
+ (values qvm :amplitude-address))
221
+
222
+ (let* ((transpositions (slot-value permutation ' transpositions))
223
+ (number-of-transpositions (slot-value permutation ' number-of-transpositions))
224
+ (bit-vector (make-array number-of-transpositions :element-type ' bit)))
225
+ (declare (type (integer 0 128 ) number-of-transpositions)
226
+ (dynamic-extent bit-vector ))
227
+
228
+ (loop :for index :from 0
229
+ :for transposition :in transpositions :do
230
+ (setf (bit bit-vector index) (ldb (byte 1 (first transposition))
231
+ address)))
232
+
233
+ (loop :for index :from 0
234
+ :for transposition :of-type transposition :in transpositions :do
235
+ (setf address (the qvm :amplitude-address
236
+ (dpb (bit bit-vector index)
237
+ (byte 1 (the (unsigned-byte 6 ) (rest transposition))) ; Enable this for speed (assumes a maximum of 64 qubits).
238
+ ; ; (byte 1 (rest transposition))
239
+ address)))
240
+ :finally (return address))))
198
241
199
242
(defun-inlinable apply-inverse-qubit-permutation (permutation address)
200
243
(apply-qubit-permutation (inverse-permutation permutation) address))
@@ -213,3 +256,32 @@ DQVM2> (write (apply-qubit-permutation (make-permutation '((2 . 0))) #b001) :bas
213
256
(dotimes (i1 max-value (values ))
214
257
(let ((i2 (apply-qubit-permutation permutation i1)))
215
258
(format stream control-string i1 i1 i2 i2)))))
259
+
260
+ (defun-inlinable apply-permutation (permutation item)
261
+ " Apply PERMUTATION to ITEM.
262
+
263
+ Examples
264
+ --------
265
+
266
+ DQVM2> (apply-permutation (make-permutation) 42)
267
+ 42
268
+
269
+ DQVM2> (apply-permutation (make-permutation '((2 . 0))) 2)
270
+ 0
271
+
272
+ DQVM2> (apply-permutation (make-permutation '((2 . 1) (1 . 0))) 2)
273
+ 1"
274
+ (declare #. qvm::*optimize-dangerously-fast*
275
+ (type (or null permutation) permutation)
276
+ (type alexandria :non-negative-fixnum item)
277
+ (values alexandria :non-negative-fixnum))
278
+
279
+ (if permutation
280
+ (alexandria :if-let ((transposition (assoc item (permutation-transpositions permutation))))
281
+ (rest transposition)
282
+ item)
283
+ item))
284
+
285
+ (defun-inlinable apply-inverse-permutation (permutation item)
286
+ " Apply PERMUTATION⁻¹ to ITEM."
287
+ (apply-permutation (inverse-permutation permutation) item))
0 commit comments