state.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. """
  2. Quantum Register Object
  3. """
  4. import qcgpu
  5. from qcgpu.backend import Backend
  6. import pyopencl as cl
  7. import numpy as np
  8. class State:
  9. """A class for representing quantum registers.
  10. The State class is the QCGPU representation of a quantum
  11. register / state vector.
  12. This class is what should be used to perform the simulations,
  13. and has method for things such as applying gates, measurements,
  14. getting probabilities and such.
  15. As QCGPU uses OpenCL, you may be queried about which OpenCL device
  16. to use. This will only happen when running things such a python repl,
  17. or running a script using QCGPU from the command line. Otherwise, a
  18. device will be chosen heuristically.
  19. When the register is created, it will be left in the state
  20. .. math::
  21. \\lvert 000 \\dots 0 \\rangle
  22. With the given number of qubits.
  23. Args:
  24. num_qubits (int): The number of qubits to create in the register.
  25. This must be greater then zero.
  26. Returns:
  27. State: A representation of the quantum register.
  28. Examples
  29. >>> qcgpu.State(3)
  30. Choose platform:
  31. [0] <pyopencl.Platform 'NVIDIA CUDA' at 0x2f22390>
  32. Choice [0]:0
  33. Set the environment variable PYOPENCL_CTX='0' to avoid being asked again.
  34. [[array(1.+0.j, dtype=complex64)]
  35. [array(0.+0.j, dtype=complex64)]
  36. [array(0.+0.j, dtype=complex64)]
  37. [array(0.+0.j, dtype=complex64)]]
  38. """
  39. def __init__(self, num_qubits):
  40. if not isinstance(num_qubits, int):
  41. raise ValueError("num_qubits must be an int")
  42. if num_qubits <= 0:
  43. raise ValueError("num_qubits must be a positive integer")
  44. #: The number of qubits that are in the register
  45. self.num_qubits = num_qubits
  46. self.backend = Backend(num_qubits)
  47. def apply_gate(self, gate, target):
  48. """Applies a single qubit unitary gate to the register.
  49. Args:
  50. gate (~qcgpu.Gate): The gate to be applied to the register
  51. target (int): The index of the qubit in the register that the gate
  52. is to be applied to.
  53. """
  54. if not isinstance(target, int) or target < 0:
  55. print(target)
  56. raise ValueError("target must be an int >= 0")
  57. # TODO: Check that gate is correct
  58. self.backend.apply_gate(gate, target)
  59. def apply_all(self, gate):
  60. # TODO: Check that gate is correct
  61. for i in range(self.num_qubits):
  62. self.apply_gate(gate, i)
  63. def apply_controlled_gate(self, gate, control, target):
  64. if not isinstance(target, int) or target < 0:
  65. raise ValueError("target must be an int >= 0")
  66. if not isinstance(control, int) or control < 0:
  67. raise ValueError("control must be an int >= 0")
  68. # TODO: Check that gate is correct
  69. self.backend.apply_controlled_gate(gate, control, target)
  70. def apply_controlled_controlled_gate(self, gate, control, control2, target):
  71. if not isinstance(target, int) or target < 0:
  72. raise ValueError("target must be an int >= 0")
  73. if not isinstance(control, int) or control < 0:
  74. raise ValueError("control must be an int >= 0")
  75. if not isinstance(control2, int) or control2 < 0:
  76. raise ValueError("control must be an int >= 0")
  77. # TODO: Check that gate is correct
  78. self.backend.apply_controlled_controlled_gate(gate, control, control2, target)
  79. def measure_qubit(self, target, samples=1):
  80. return self.backend.measure_qubit(target, samples)
  81. def measure_collapse(self, target):
  82. return self.backend.measure_collapse(target)
  83. def measure(self, samples=1):
  84. return self.backend.measure(samples)
  85. def measure_first(self, num=1, samples=1):
  86. return self.backend.measure_first(num, samples)
  87. def amplitudes(self):
  88. return self.backend.amplitudes()[0]
  89. def probabilities(self):
  90. return self.backend.probabilities()[0]
  91. def __repr__(self):
  92. """A string representation of the state"""
  93. # TODO: Finish this method
  94. return np.array_str(self.backend.buffer)
  95. # Gates
  96. def h(self, target):
  97. self.apply_gate(qcgpu.gate.h(), target)
  98. def x(self, target):
  99. self.apply_gate(qcgpu.gate.x(), target)
  100. def y(self, target):
  101. self.apply_gate(qcgpu.gate.y(), target)
  102. def z(self, target):
  103. self.apply_gate(qcgpu.gate.z(), target)
  104. def s(self, target):
  105. self.apply_gate(qcgpu.gate.s(), target)
  106. def t(self, target):
  107. self.apply_gate(qcgpu.gate.t(), target)
  108. def sqrt_x(self, target):
  109. self.apply_gate(qcgpu.gate.sqrt_x(), target)
  110. def cx(self, control, target):
  111. self.apply_controlled_gate(qcgpu.gate.x(), control, target)
  112. def cnot(self, control, target):
  113. self.apply_controlled_gate(qcgpu.gate.x(), control, target)
  114. def toffoli(self, control, control2, target):
  115. self.apply_controlled_controlled_gate(qcgpu.gate.x(), control, control2, target)
  116. def u(self, target, theta, phi, lda):
  117. a = np.exp(-1j * (phi + lda) / 2) * np.cos(theta / 2)
  118. b = - np.exp(-1j * (phi - lda) / 2) * np.sin(theta / 2)
  119. c = np.exp(1j * (phi - lda) / 2) * np.sin(theta / 2)
  120. d = np.exp(1j * (phi + lda) / 2) * np.cos(theta / 2)
  121. gate_matrix = np.array([
  122. [a, b],
  123. [c, d]
  124. ])
  125. self.apply_gate(qcgpu.Gate(gate_matrix), target)
  126. def u1(self, target, lda):
  127. self.u(target, 0, 0, lda)
  128. def u2(self, target, phi, lda):
  129. self.u(target, np.pi / 2, phi, lda)
  130. def u3(self, target, theta, phi, lda):
  131. self.u(target, theta, phi, lda)
  132. def cu(self, control, target, theta, phi, lda):
  133. a = np.exp(-1j * (phi + lda) / 2) * np.cos(theta / 2)
  134. b = - np.exp(-1j * (phi - lda) / 2) * np.sin(theta / 2)
  135. c = np.exp(1j * (phi - lda) / 2) * np.sin(theta / 2)
  136. d = np.exp(1j * (phi + lda) / 2) * np.cos(theta / 2)
  137. gate_matrix = np.array([
  138. [a, b],
  139. [c, d]
  140. ])
  141. self.apply_controlled_gate(qcgpu.Gate(gate_matrix), control, target)
  142. def cu1(self, control, target, lda):
  143. self.cu(control, target, 0, 0, lda)
  144. def cu2(self, control, target, phi, lda):
  145. self.cu(control, target, np.pi / 2, phi, lda)
  146. def cu3(self, control, target, theta, phi, lda):
  147. self.cu(control, target, theta, phi, lda)