44
55(in-package # :dqvm2)
66
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).
1019
1120(deftype transposition ()
1221 ' (or null (cons alexandria :non-negative-fixnum
1322 alexandria :non-negative-fixnum)))
1423
1524(defclass permutation ()
25+ ()
26+ (:documentation " Base class for permutations." ))
27+
28+ (defclass permutation-general (permutation)
1629 ((number-of-transpositions
17- :initarg :number-of-transpositions
1830 :type alexandria :non-negative-integer
31+ :initarg :number-of-transpositions
1932 :documentation " Number of transpositions defining the permutation." )
2033 (transpositions
21- :initarg :transpositions
2234 :type list
35+ :initarg :transpositions
2336 :reader permutation-transpositions
2437 :documentation " Bijective map determined by transpositions, stored as an association list sorted by CAR." ))
2538 (:default-initargs
2639 :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 ))))
2853
29- (defmethod print-object ((permutation permutation) stream )
54+ (defmethod print-object ((permutation permutation-general ) stream )
3055 (print-unreadable-object (permutation stream :type t :identity t )
3156 (let ((transpositions (permutation-transpositions permutation)))
3257 (format stream " ~:[ ~:A ~;~{ ~A ~^ ~}~] " transpositions transpositions))))
3358
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+
3464(defun-inlinable make-permutation (&optional transpositions)
3565 " Allocate a permutation defined by TRANSPOSITIONS.
3666
@@ -43,11 +73,10 @@ DQVM2> (make-permutation '((2 . 1) (1 . 0)))
4373#<permutation (0 . 2) (1 . 0) (2 . 1) {10086BB8B3}>
4474
4575Note 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*
4777 (type list transpositions))
4878
49- (let ((permutation (make-instance ' permutation))
50- (transpositions* nil )
79+ (let ((transpositions* nil )
5180 (domain nil )
5281 (codomain nil ))
5382
@@ -59,6 +88,8 @@ Note that in the example above, the transposition (0 2) was automatically added.
5988 (error " Malformed permutation. A mapping ~D ↦ ~D already existed."
6089 (first z) (rest z))))))
6190
91+ (declare (inline check-transposition))
92+
6293 (loop :for (a . b) :in transpositions :do
6394 (check-transposition a b)
6495 (unless (= a b)
@@ -69,93 +100,85 @@ Note that in the example above, the transposition (0 2) was automatically added.
69100 (loop :for a :of-type alexandria :non-negative-fixnum
70101 :in (set-difference codomain domain)
71102 :for b :of-type alexandria :non-negative-fixnum
72- :in (nset -difference domain codomain)
103+ :in (set -difference domain codomain)
73104 :unless (= a b) :do
74105 (pushnew (cons a b) transpositions* :test #' equal ))
75106
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)))
127149
128150(defun compose-permutations (&rest permutations)
129151 " Return a new permutation that is the composition of PERMUTATIONS.
130152
131153If 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 )))
154176
155177 (make-permutation transpositions)))
156178
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.
159182
160183Examples
161184--------
@@ -165,36 +188,56 @@ DQVM2> (apply-qubit-permutation (make-permutation '((2 . 0))) #b100)
165188
166189DQVM2> (write (apply-qubit-permutation (make-permutation '((2 . 0))) #b001) :base 2)
167190100
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)
169211 ; ; Alternatively, in-place permutations could be implemented following:
170212 ; ;
171213 ; ; F. Fich, J. Munro, and P. Poblete, “Permuting in Place,” SIAM
172214 ; ; J. Comput., vol. 24, no. 2, pp. 266–278, Apr. 1995.
173215
174- (declare ( optimize ( speed 3 ) ( safety 0 ))
216+ (declare #. qvm:: * optimize-dangerously-fast*
175217 (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))))
198241
199242(defun-inlinable apply-inverse-qubit-permutation (permutation address)
200243 (apply-qubit-permutation (inverse-permutation permutation) address))
@@ -203,8 +246,8 @@ DQVM2> (write (apply-qubit-permutation (make-permutation '((2 . 0))) #b001) :bas
203246 (stream *standard-output* ))
204247 " Print the address permutation induced by PERMUTATION (possibly using up to NUMBER-OF-QUBITS) in STREAM."
205248 (let* ((n (or number-of-qubits
206- (1+ (loop :for transposition :in (permutation-transpositions permutation)
207- :maximizing (max transposition )))))
249+ (1+ (loop :for (a . b) :in (permutation-transpositions permutation)
250+ :maximizing (max a b )))))
208251 (max-value (expt 2 n))
209252 (aux-control-string (format nil " ~~~D D |~~~D ,'0B>"
210253 (ceiling (log max-value 10 )) n))
@@ -213,3 +256,32 @@ DQVM2> (write (apply-qubit-permutation (make-permutation '((2 . 0))) #b001) :bas
213256 (dotimes (i1 max-value (values ))
214257 (let ((i2 (apply-qubit-permutation permutation i1)))
215258 (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