backend.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. import os
  2. import random
  3. import numpy as np
  4. import pyopencl as cl
  5. import pyopencl.array as pycl_array
  6. from pyopencl.reduction import ReductionKernel
  7. # Get the OpenCL kernel
  8. kernel_path = os.path.join(
  9. os.path.dirname(__file__),
  10. "kernels/brute-force.cl"
  11. )
  12. kernel = open(kernel_path, "r").read()
  13. class Backend:
  14. """
  15. A class for the OpenCL backend to the simulator.
  16. This class shouldn't be used directly, as many of the
  17. methods don't have the same input checking as the State
  18. class.
  19. """
  20. def __init__(self, num_qubits, dtype=np.complex64):
  21. """
  22. Initialize a new OpenCL Backend
  23. Takes an argument of the number of qubits to use
  24. in the register, and returns the backend.
  25. """
  26. self.num_qubits = num_qubits
  27. self.dtype = dtype
  28. self.context = cl.create_some_context()
  29. self.queue = cl.CommandQueue(self.context)
  30. self.program = cl.Program(self.context, kernel).build()
  31. # Buffer for the state vector
  32. self.buffer = pycl_array.to_device(
  33. self.queue,
  34. np.eye(2**num_qubits, 1, dtype=dtype)
  35. )
  36. def apply_gate(self, gate, target):
  37. """Applies a gate to the quantum register"""
  38. self.program.apply_gate(
  39. self.queue,
  40. [int(self.buffer.shape[0] / 2)],
  41. None,
  42. self.buffer.data,
  43. np.int32(target),
  44. self.dtype(gate.a),
  45. self.dtype(gate.b),
  46. self.dtype(gate.c),
  47. self.dtype(gate.d)
  48. )
  49. def apply_controlled_gate(self, gate, control, target):
  50. """Applies a controlled gate to the quantum register"""
  51. self.program.apply_controlled_gate(
  52. self.queue,
  53. [int(self.buffer.shape[0] / 2)],
  54. None,
  55. self.buffer.data,
  56. np.int32(control),
  57. np.int32(target),
  58. self.dtype(gate.a),
  59. self.dtype(gate.b),
  60. self.dtype(gate.c),
  61. self.dtype(gate.d)
  62. )
  63. def qubit_probability(self, target):
  64. """Get the probability of a single qubit begin measured as '0'"""
  65. preamble = """
  66. #include <pyopencl-complex.h>
  67. float probability(int target, int i, cfloat_t amp) {
  68. if ((i & (1 << target )) != 0) {
  69. return 0;
  70. }
  71. // return 6.0;
  72. float abs = cfloat_abs(amp);
  73. return abs * abs;
  74. }
  75. """
  76. kernel = ReductionKernel(
  77. self.context,
  78. np.float,
  79. neutral = "0",
  80. reduce_expr="a + b",
  81. map_expr="probability(target, i, amps[i])",
  82. arguments="__global cfloat_t *amps, __global int target",
  83. preamble=preamble
  84. )
  85. return kernel(self.buffer, target).get()
  86. def measure_qubit(self, target, samples):
  87. probability_of_0 = self.qubit_probability(target)
  88. total = 0
  89. for i in range(samples):
  90. outcome = 1 if random.random() > probability_of_0 else 0
  91. total = total + outcome
  92. return total
  93. def amplitudes(self):
  94. """Gets the probability amplitudes"""
  95. return self.buffer.get()
  96. def probabilities(self):
  97. """Gets the squared absolute value of each of the amplitudes"""
  98. out = pycl_array.to_device(
  99. self.queue,
  100. np.zeros(2**self.num_qubits, dtype=np.float32)
  101. )
  102. self.program.calculate_probabilities(
  103. self.queue,
  104. self.buffer.shape,
  105. None,
  106. self.buffer.data,
  107. out.data
  108. )
  109. return out.get()
  110. def release(self):
  111. self.buffer.base_data.release()