File: quantum_computing.mac

package info (click to toggle)
maxima 5.47.0-9
  • links: PTS
  • area: main
  • in suites: forky, sid
  • size: 193,104 kB
  • sloc: lisp: 434,678; fortran: 14,665; tcl: 10,990; sh: 4,577; makefile: 2,763; ansic: 447; java: 328; python: 262; perl: 201; xml: 60; awk: 28; sed: 15; javascript: 2
file content (350 lines) | stat: -rw-r--r-- 12,281 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
/*
   Quantum Computing Simulator for Maxima
   Copyright (C) 2021 Jaime E. Villate

   Time-stamp: "2022-03-19 13:52:00 villate"

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License
   as published by the Free Software Foundation; either version 2
   of the License, or (at your option) any later version.
   
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
   MA  02110-1301, USA
*/
   
/*
   linsert
   Inserts an expression or list "e" into a list "list" at position "pos".
   The list could be empty and "pos" must be an integer between 1 and the
   length of "list" plus 1.
*/
linsert(e, list, pos) := block(
  if not(listp(e)) then e: [e],
  append(rest(list, pos-length(list)-1), e, rest(list, pos-1)))$
   
/*
   lreplace
   If e is a list of length n, the elements in the positions pos, pos+1,
   ..., pos+n-1 of the list "list" are replaced by e, or the first elements
   of e if the end of "list" is reached.
   If e is an expression, the element in position "pos" of "list" is
   replaced by that expression.
   "pos" must be an integer between 1 and the length of "list".
*/
lreplace(e, list, pos) := block([r: copylist(list)],
  if not(listp(e)) then e: [e],
  for i:0 thru min(length(e)-1, length(list)-pos) do r[pos+i]: e[i+1],
  r)$
   
/* binlist
   Should be called with one or two arguments, binlist(k) or binlist(k,n),
   where k and n are natural numbers. With only one argument, a list of
   binary digits is returned, which are the representation of k in binary.
   With two arguments, zeros are included at the beginning in order
   to return a list with exactly n binary digits. Notice that for the result
   to represent a possible state of m qubits, n should be equal to 2^m and
   k should be between 0 and 2^m-1.
*/
binlist([n]):= block([m:n[1], q, l:[], k],
  do([m,q]: divide(m, 2), l: cons(q,l), if m=0
    then (
      if length(n)>1
      then (
        k: length(l),
        for i:1 thru n[2]-k do l:cons(0,l)),
      return(l))))$

/* binlist2dec
   Given a list with n binary digits, it returns the decimal number it
   represents.
*/
binlist2dec(l) := block([m:l[1]],
  for i:2 thru length(l) do m:m*2+l[i],
  m)$

/* tprod
   Returns the tensor product of n matrices or n lists.
*/
tprod([ops]) := block(
  if listp(ops[1])
  then list_matrix_entries(apply(tprod,map(matrix,ops)))
  else lreduce(kronecker_product, ops))$

/* normalize
   Returns the normalized version of a quantum state given as a list q.
*/
normalize(q) := block([p:0],
  for b in q do p: p+cabs(b)^2,
  for i:1 thru length(q) do q[i]: rootscontract(radcan(q[i]/sqrt(p))),
  q)$

/* qubits
   Returns a list with 2^n elements, representing the state of n qubits.
   The argument can be a single natural number n, or n binary digits.
   If given only one natural number, greater than 1, it returns the ground
   state for a system of n qubits.
   If given n binary digits (n can be 1), it returns the state of a system
   of n qubits with those binary values.
*/
qubits([b]) := block([l],
  if (length(b) = 1) and (first(b) > 1)
  then cons(1, makelist(0,2^first(b)-1))
  else (
    l: makelist(0,2^length(b)),
    l[binlist2dec(b)+1]: 1,
    l))$

/* gate
   Applies a matrix U, acting on mu qubits on the list q corresponding
   to a state of mq qubits (mq >= mu).
   When U acts on several qubits (mu > 1), and no more arguments are
   given, the matrix will act on qubits 1, 2, ..., mu. A third
   argument can be given, a integer from 1 through mq+1-mu, in which
   case the matrix will act on qubits i through i+mu-1.
   When U acts on only one qubit (mu = 1), and no more arguments are
   given, the matrix will act in all of the qubits. If more arguments
   are given, they should be different integer numbers between 1 and
   nq, and the matrix will act in the qubits corresponding to those
   numbers.
   U can be a symbol representing one of the predefined matrices.
   It modifies the list q and returns that new list.
*/
gate(U,q,[bits]) := block([nq: length(q), mq, nu, mu, ln, lk, j, l, aux],
  if symbolp(U) then U: qmatrix[U],
  nu: length(U[1]),
  mq: round(float(log(nq)/log(2))),
  mu: round(float(log(nu)/log(2))),
  if length(bits) = 0
  then (
    if mu > 1
    then bits:[1]
    else bits: makelist(j, j, 1, mq)),
  for i in bits do (
    aux: makelist(0,nq),
    for n:1 thru nq do (
      ln: binlist(n-1, mq),
      j: binlist2dec(rest(rest(ln, i-1), mu+i-mq-1)) + 1,
      for k:1 thru nu do (
        lk: binlist(k-1, mu),
        l: binlist2dec(lreplace(lk, ln, i)) + 1,
        aux[n]: aux[n] + U[j][k]*q[l])),
    for n:1 thru nq do q[n]: aux[n]),
  q)$

/* gate_matrix
   Creates the matrix corresponding to the action of a single qbit matrix U
   acting on one of several of the qubits in a state of n qubits.
   The first argument must be a 2 by 2 matrix or a symbol representing one
   of the predefined matrices.
   There must be a second argument, a natural number, corresponding to the
   total number of qbits.
   If no more arguments are given, the matrix will act in all of the qubits.
   If there are more arguments, they should be one or more different
   natural numbers, between 1 and n, and the matrix will act in the qubits
   corresponding to those numbers.
   It returns a 2^n by 2^n matrix.
*/
gate_matrix(U,n,[bits]) := block([lU],
  if symbolp(U) then U: qmatrix[U],
  if length(bits) = 0
  then apply(tprod, makelist(U, n))
  else (
    lU: makelist(ident(2), n),
    for i in bits do lU[i]: U,
    apply(tprod, lU)))$

/* controlled
   Applies a matrix U, acting on mu qubits, on qubits i through i+mu-1
   on the state q of a system of mq qubits (mq > mu), when the value of
   the c'th qubit in q equals 1.
   i should be an intenger between 1 and mq+1-mu.
   c should be an integer within 1 and mq, excluding the qubits to be
   modified (i through i+mu-1)
   U can be a symbol representing one of the predefined matrices.
   It modifies the list q and returns that new list.
*/
controlled(U,q,c,i) := block([nq: length(q), mq, nu, mu, ln, lk, j, l, aux],
  if symbolp(U) then U: qmatrix[U],
  aux: makelist(0,nq),
  nu: length(U[1]),
  mq: round(float(log(nq)/log(2))),
  mu: round(float(log(nu)/log(2))),
  for n:1 thru nq do (
    ln: binlist(n-1, mq),
    if (ln[c] = 1)
    then (
      j: binlist2dec(rest(rest(ln, i-1), mu+i-mq-1)) + 1,
      for k:1 thru nu do (
        lk: binlist(k-1, mu),
        l: binlist2dec(lreplace(lk, ln, i)) + 1,
        aux[n]: aux[n] + U[j][k]*q[l]))
    else (
      aux[n]: q[n])),
  for n:1 thru nq do q[n]: aux[n],
  q)$

/* CNOT
   Changes the value of the j'th qubit, in a state q of m qubits, when
   the value of the i'th qubit equals 1.
   It modifies the list q and returns its modified value.
*/

CNOT(q,i,j) := block([lst: [], n: length(q), d, m, k],
  m: round(float(log(length(q))/log(2))),
  d: 2^m/2^j,
  if (m > 2)
  then (
    for b:0 thru n/4-1 do (
     lst: binlist(b, m-2),
      if (i < j)
      then lst: linsert(0, linsert(1,lst,i), j)
      else lst: linsert(1, linsert(0,lst,j), i),
      k: binlist2dec(lst)+1,
      [q[k],q[k+d]]: [q[k+d],q[k]]))
  else (
    [q[4-i],q[4-i+d]]: [q[4-i+d],q[4-i]]),
  q)$

/* qswap
   Interchanges the states of bits i and j in the qubits state q.
   It modifies the list q and returns its modified value.
*/
   
qswap(q, i, j) := (CNOT(q, i, j), CNOT(q, j, i), CNOT(q, i, j))$

/* toffoli
   Changes the value of the k'th qubit, in a state q of m qubits, when
   the value of the i'th snf j'th qubitd equal 1.
   It modifies the list q and returns its modified value.
*/

toffoli(q,i,j,k) := block([lst: [], n: length(q), c, d, m],
  m: round(float(log(length(q))/log(2))),
  d: 2^m/2^k,
  if (m > 3)
  then (
    for b:0 thru n/8-1 do (
     lst: binlist(b, m-3),
      if (i < j) 
      then (
        if (i < k)
        then (
          if (j < k)
          then lst: linsert(0, linsert(1, linsert(1,lst,i), j), k)
          else lst: linsert(1, linsert(0, linsert(1,lst,i), k), j))
        else lst: linsert(1, linsert(0, linsert(1,lst,j), k), i))
      else if (i < k)
      then lst: linsert(0, linsert(1, linsert(1,lst,j), i), k)
      else (
        if (j < k)
        then lst: linsert(1, linsert(1, linsert(0,lst,k), i), j)
        else lst: linsert(1, linsert(1, linsert(0,lst,k), j), i)),
      c: binlist2dec(lst)+1,
      [q[c],q[c+d]]: [q[c+d],q[c]]))
  else (
    [q[8-d],q[8]]: [q[8],q[8-d]]),
  q)$

/* qmeasure
   Measures the value of one or more qubits in a system of n qubits with
   state q.
   It requires 1 or more arguments. The first argument must be the state q.
   If no more arguments are given, all the n qubits will be measured.
   If more arguments are given, they should be different natural numbers
   between 1 and n, and the state of those qubits will be measured.
   It returns a list with the values of the qubits measured (either 0 or 1),
   in the same order that the were requested or in ascending order if no
   second argument was given.
   It modifies the list q, reflecting the collapse of the quantum state
   after the measurement.
*/
qmeasure(q,[bits]) := block([r, p, l:[], bl, n, m, v, result: [],
  test:true],
  n: length(q),
  m: round(float(log(n)/log(2))),
  if length(bits) = 0 then bits: makelist(j, j, 1, m),
  for i in bits do (
    r: random(1.0),
    p: 0,
    for j:1 thru n do (
      if not(test) then return(),
      if constantp(q[j])
      then (
        bl: binlist(j-1,m),
        if bl[i] = 0 then p: p+cabs(q[j])^2)
      else test:false),
    if test
    then (
      if r<p then v:0 else (v:1, p: 1-p),
      for j: 1 thru n do (
        bl:binlist(j-1,m),
        if bl[i]=v
        then (
          if atom(p)
          then q[j]: q[j]/sqrt(p)
          else q[j]: rootscontract(radcan(q[j]/sqrt(p))))
        else q[j]: 0),
      result: endcons(v, result))),
  result)$

/* qdisplay
   Represents the state q of a system of n qubits as a linear combination
   of the computational states with n binary digits.
   It returns an expression including strings and symbols.
*/
qdisplay(q):=block([n, m, s, g:1, v, sg: ["-","",""], test:true],
  n:length(q),
  m:round(float(log(n)/log(2))),
  for e in q do
  /* Find the GCD only where the non-zero components are expressions
  **** Currently disabled ****
  if ((imagpart(e) = 0) and atom(e) and (sign(e) # 'zero)) then test: false,
  if test
  then g: 1/lreduce('gcd, q)
  else g: 1, */
  if (g = 1) and (length(q) > 1) then s: "" else s: "(",
  for i:1 thru n do (
    if (g = 1)
    then v: q[i]
    else v: q[i]*g,
    if ((imagpart(v) # 0) or (sign(v) # 'zero))
    then (
      if (numberp(v) or ((imagpart(v) = 0) and atom(v) and (sign(v) # 'pnz)))
      then (
        if ((imagpart(v^2-1) = 0) and (sign(v^2-1) = 'zero))
        then s:concat(s, sg[round((v+3)/2)], printf(false,"|~v,'0b>",m,i-1))
        elseif (sign(v) = 'neg)
        then s:concat(s, sg[3], printf(false,"~a|~v,'0b>",v,m,i-1))
        else s:concat(s, sg[2], printf(false,"~a|~v,'0b>",v,m,i-1)))
      elseif atom(v)
      then s: concat(s, sg[2], printf(false,"~a|~v,'0b>",v,m,i-1))
      else s: concat(s, sg[2], printf(false,"(~a)|~v,'0b>",v,m,i-1)),
      sg: [" -"," +"," "])),
  if (g # 1)
  then (
    if atom(g)
    then s: concat(s, printf(false,")/~a",g))
    else s: concat(s, printf(false,")/(~a)",g))),
  s)$

/*
   Predefined matrices.
*/
qmatrix[I]: matrix([1,0],[0,1])$
qmatrix[X]: matrix([0,1],[1,0])$
qmatrix[Y]: matrix([0,-%i],[%i,0])$
qmatrix[Z]: matrix([1,0],[0,-1])$
qmatrix[H]: matrix([1,1],[1,-1])/sqrt(2)$
qmatrix[S]: matrix([1,0],[0,%i])$
qmatrix[T]: matrix([1,0],[0,exp(%i*%pi/4)])$
Rx(ang) :=  matrix([cos(ang/2),-%i*sin(ang/2)],[%i*sin(ang/2),cos(ang/2)])$
Ry(ang) :=  matrix([cos(ang/2),-sin(ang/2)],[sin(ang/2),cos(ang/2)])$
Rz(ang) :=  matrix([cos(ang/2)-%i*sin(ang/2),0],[0,cos(ang/2)+%i*sin(ang/2)])$