qubit.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. # A qubit library
  2. # Author: Nikita Ayzikovsky (lament)
  3. # You may use this freely, but it's neither GPL nor public domain (yet)
  4. # Note that everything starts out initialized to 1. This needs to be
  5. # changed if this is to be used for something other than Quantum Brainfuck.
  6. # Other than that, the library is completely generic. Just add your own
  7. # gates and go ahead.
  8. # The things that should be actually used from outside are
  9. # clases Gate and Register.
  10. import random # of course!
  11. def dotproduct(a,b):
  12. """Because I'm too lazy to look for a lin. algebra module"""
  13. return sum([x*y for (x,y) in zip(a,b)])
  14. def mvmult(matrix, vector):
  15. """Because I'm too lazy to look for a lin. algebra module"""
  16. #no error checking for now
  17. result = []
  18. for row in matrix:
  19. result.append(dotproduct(row, vector))
  20. return result
  21. def bit(num, bitnum):
  22. return int((num&(1<<bitnum)) != 0)
  23. def n2bits(n, length):
  24. return [bit(n, x) for x in range(length)]
  25. def bits2n(bitlist):
  26. result = 0
  27. for x in range(len(bitlist)):
  28. result += bitlist[x] * (1<<x)
  29. return result
  30. class Gate:
  31. def __init__(self, num_bits, matrix):
  32. self.num_bits = num_bits
  33. self.N = 1<<num_bits
  34. self.matrix = matrix
  35. def apply(self, register, bitlist):
  36. """Applies this gate to bits from bitlist (size of bitlist should
  37. be N) in the register object given"""
  38. # let's do this the least efficient way possible
  39. # apply the gate to bits in bitlist for each individual combination
  40. # of other bits
  41. # We have register.length total bits, so register.length-N
  42. # bit combinations
  43. new_contents = register.contents[:]
  44. num_bits = register.length
  45. allbits = range(num_bits)
  46. otherbits = [x for x in allbits if x not in bitlist]
  47. # Iterate over each combination of other bits:
  48. for i in range(2**len(otherbits)):
  49. other_bit_values = n2bits(i, len(otherbits))
  50. positions = []
  51. for j in range(self.N):
  52. current_position = [0] * num_bits
  53. for index, value in zip(otherbits, other_bit_values):
  54. current_position[index] = value
  55. for index, value in zip(bitlist,n2bits(j, self.N)):
  56. current_position[index] = value
  57. positions.append(bits2n(current_position))
  58. values = [register.contents[x] for x in positions]
  59. values = mvmult(self.matrix, values)
  60. for x, index in zip(positions, range(self.N)):
  61. new_contents[x] = values[index]
  62. register.contents = new_contents
  63. class Register:
  64. def __init__(self, length):
  65. """Initialize to all 1s as per Quantum Brainfuck specs"""
  66. self.length = length
  67. self.contents = [0] * (2**length)
  68. self.contents[-1] = 1
  69. def add_bit(self):
  70. """Adds a new qubit, containing 1 and not entangled with anything"""
  71. new_contents = []
  72. new_contents = [0] * len(self.contents) + self.contents
  73. self.contents = new_contents
  74. self.length += 1
  75. def observe(self, bit_index):
  76. """Observes the value of the qubit at the given index,
  77. changing that bit to |0> or |1> and updating all probabilities
  78. accordingly. Returns 0 or 1"""
  79. # first, find out the probability that the qubit is set.
  80. prob = 0
  81. for i in range(2 ** self.length):
  82. prob += abs(bit(i, bit_index) * self.contents[i]) ** 2
  83. # prob is now set to the probability of bit set to 1
  84. # now "Observe"
  85. if random.random() <= prob:
  86. bit_value = 1
  87. else:
  88. bit_value = 0
  89. # now that we know the "observed" value, adjust all other
  90. # probabilities to account for it.
  91. if prob == 0 or prob == 1:
  92. # don't need adjustment
  93. return bit_value
  94. adjustment = (1 / prob) ** 0.5
  95. for i in range(2 ** self.length):
  96. if bit(i, bit_index) == bit_value:
  97. self.contents[i] = self.contents[i] * adjustment
  98. else:
  99. self.contents[i] = 0
  100. return bit_value
  101. def set(self, bit_index, bit_value):
  102. """Sets the indexed bit to 'value', which should be 1 or 0"""
  103. for i in range(2 ** self.length):
  104. if bit(i, bit_index) == bit_value:
  105. # take the 'sister' bit combination and add its
  106. # probability to this one
  107. if bit_value:
  108. sister = i & (~ 1<<bit_index)
  109. else:
  110. sister = i | 1<<bit_index
  111. total_prob = self.contents[i] **2 + self.contents[sister]**2
  112. self.contents[i] = math.sqrt(total_prob)
  113. self.contents[sister] = 0
  114. #############################################################
  115. import math
  116. st = 1/math.sqrt(2)
  117. Hadamard = Gate(1, [[st, st],
  118. [st, -st]])
  119. CNOT = Gate(2, [[1, 0, 0, 0],
  120. [0, 1, 0, 0],
  121. [0, 0, 0, 1],
  122. [0, 0, 1, 0]])
  123. CV = Gate(2, [[1, 0, 0, 0],
  124. [0, 1, 0, 0],
  125. [0, 0, 1, 0],
  126. [0, 0, 0, 1j]])
  127. if __name__ == '__main__':
  128. # just testing stuff
  129. r = Register(2)
  130. Hadamard.apply(r, [0])
  131. print r.contents
  132. CNOT.apply(r,[1,0])
  133. print r.contents
  134. r.set(0, 1)
  135. print r.contents