qubit.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811
  1. """Qubits for quantum computing.
  2. Todo:
  3. * Finish implementing measurement logic. This should include POVM.
  4. * Update docstrings.
  5. * Update tests.
  6. """
  7. import math
  8. from sympy.core.add import Add
  9. from sympy.core.mul import Mul
  10. from sympy.core.numbers import Integer
  11. from sympy.core.power import Pow
  12. from sympy.core.singleton import S
  13. from sympy.functions.elementary.complexes import conjugate
  14. from sympy.functions.elementary.exponential import log
  15. from sympy.core.basic import _sympify
  16. from sympy.external.gmpy import SYMPY_INTS
  17. from sympy.matrices import Matrix, zeros
  18. from sympy.printing.pretty.stringpict import prettyForm
  19. from sympy.physics.quantum.hilbert import ComplexSpace
  20. from sympy.physics.quantum.state import Ket, Bra, State
  21. from sympy.physics.quantum.qexpr import QuantumError
  22. from sympy.physics.quantum.represent import represent
  23. from sympy.physics.quantum.matrixutils import (
  24. numpy_ndarray, scipy_sparse_matrix
  25. )
  26. from mpmath.libmp.libintmath import bitcount
  27. __all__ = [
  28. 'Qubit',
  29. 'QubitBra',
  30. 'IntQubit',
  31. 'IntQubitBra',
  32. 'qubit_to_matrix',
  33. 'matrix_to_qubit',
  34. 'matrix_to_density',
  35. 'measure_all',
  36. 'measure_partial',
  37. 'measure_partial_oneshot',
  38. 'measure_all_oneshot'
  39. ]
  40. #-----------------------------------------------------------------------------
  41. # Qubit Classes
  42. #-----------------------------------------------------------------------------
  43. class QubitState(State):
  44. """Base class for Qubit and QubitBra."""
  45. #-------------------------------------------------------------------------
  46. # Initialization/creation
  47. #-------------------------------------------------------------------------
  48. @classmethod
  49. def _eval_args(cls, args):
  50. # If we are passed a QubitState or subclass, we just take its qubit
  51. # values directly.
  52. if len(args) == 1 and isinstance(args[0], QubitState):
  53. return args[0].qubit_values
  54. # Turn strings into tuple of strings
  55. if len(args) == 1 and isinstance(args[0], str):
  56. args = tuple( S.Zero if qb == "0" else S.One for qb in args[0])
  57. else:
  58. args = tuple( S.Zero if qb == "0" else S.One if qb == "1" else qb for qb in args)
  59. args = tuple(_sympify(arg) for arg in args)
  60. # Validate input (must have 0 or 1 input)
  61. for element in args:
  62. if element not in (S.Zero, S.One):
  63. raise ValueError(
  64. "Qubit values must be 0 or 1, got: %r" % element)
  65. return args
  66. @classmethod
  67. def _eval_hilbert_space(cls, args):
  68. return ComplexSpace(2)**len(args)
  69. #-------------------------------------------------------------------------
  70. # Properties
  71. #-------------------------------------------------------------------------
  72. @property
  73. def dimension(self):
  74. """The number of Qubits in the state."""
  75. return len(self.qubit_values)
  76. @property
  77. def nqubits(self):
  78. return self.dimension
  79. @property
  80. def qubit_values(self):
  81. """Returns the values of the qubits as a tuple."""
  82. return self.label
  83. #-------------------------------------------------------------------------
  84. # Special methods
  85. #-------------------------------------------------------------------------
  86. def __len__(self):
  87. return self.dimension
  88. def __getitem__(self, bit):
  89. return self.qubit_values[int(self.dimension - bit - 1)]
  90. #-------------------------------------------------------------------------
  91. # Utility methods
  92. #-------------------------------------------------------------------------
  93. def flip(self, *bits):
  94. """Flip the bit(s) given."""
  95. newargs = list(self.qubit_values)
  96. for i in bits:
  97. bit = int(self.dimension - i - 1)
  98. if newargs[bit] == 1:
  99. newargs[bit] = 0
  100. else:
  101. newargs[bit] = 1
  102. return self.__class__(*tuple(newargs))
  103. class Qubit(QubitState, Ket):
  104. """A multi-qubit ket in the computational (z) basis.
  105. We use the normal convention that the least significant qubit is on the
  106. right, so ``|00001>`` has a 1 in the least significant qubit.
  107. Parameters
  108. ==========
  109. values : list, str
  110. The qubit values as a list of ints ([0,0,0,1,1,]) or a string ('011').
  111. Examples
  112. ========
  113. Create a qubit in a couple of different ways and look at their attributes:
  114. >>> from sympy.physics.quantum.qubit import Qubit
  115. >>> Qubit(0,0,0)
  116. |000>
  117. >>> q = Qubit('0101')
  118. >>> q
  119. |0101>
  120. >>> q.nqubits
  121. 4
  122. >>> len(q)
  123. 4
  124. >>> q.dimension
  125. 4
  126. >>> q.qubit_values
  127. (0, 1, 0, 1)
  128. We can flip the value of an individual qubit:
  129. >>> q.flip(1)
  130. |0111>
  131. We can take the dagger of a Qubit to get a bra:
  132. >>> from sympy.physics.quantum.dagger import Dagger
  133. >>> Dagger(q)
  134. <0101|
  135. >>> type(Dagger(q))
  136. <class 'sympy.physics.quantum.qubit.QubitBra'>
  137. Inner products work as expected:
  138. >>> ip = Dagger(q)*q
  139. >>> ip
  140. <0101|0101>
  141. >>> ip.doit()
  142. 1
  143. """
  144. @classmethod
  145. def dual_class(self):
  146. return QubitBra
  147. def _eval_innerproduct_QubitBra(self, bra, **hints):
  148. if self.label == bra.label:
  149. return S.One
  150. else:
  151. return S.Zero
  152. def _represent_default_basis(self, **options):
  153. return self._represent_ZGate(None, **options)
  154. def _represent_ZGate(self, basis, **options):
  155. """Represent this qubits in the computational basis (ZGate).
  156. """
  157. _format = options.get('format', 'sympy')
  158. n = 1
  159. definite_state = 0
  160. for it in reversed(self.qubit_values):
  161. definite_state += n*it
  162. n = n*2
  163. result = [0]*(2**self.dimension)
  164. result[int(definite_state)] = 1
  165. if _format == 'sympy':
  166. return Matrix(result)
  167. elif _format == 'numpy':
  168. import numpy as np
  169. return np.matrix(result, dtype='complex').transpose()
  170. elif _format == 'scipy.sparse':
  171. from scipy import sparse
  172. return sparse.csr_matrix(result, dtype='complex').transpose()
  173. def _eval_trace(self, bra, **kwargs):
  174. indices = kwargs.get('indices', [])
  175. #sort index list to begin trace from most-significant
  176. #qubit
  177. sorted_idx = list(indices)
  178. if len(sorted_idx) == 0:
  179. sorted_idx = list(range(0, self.nqubits))
  180. sorted_idx.sort()
  181. #trace out for each of index
  182. new_mat = self*bra
  183. for i in range(len(sorted_idx) - 1, -1, -1):
  184. # start from tracing out from leftmost qubit
  185. new_mat = self._reduced_density(new_mat, int(sorted_idx[i]))
  186. if (len(sorted_idx) == self.nqubits):
  187. #in case full trace was requested
  188. return new_mat[0]
  189. else:
  190. return matrix_to_density(new_mat)
  191. def _reduced_density(self, matrix, qubit, **options):
  192. """Compute the reduced density matrix by tracing out one qubit.
  193. The qubit argument should be of type Python int, since it is used
  194. in bit operations
  195. """
  196. def find_index_that_is_projected(j, k, qubit):
  197. bit_mask = 2**qubit - 1
  198. return ((j >> qubit) << (1 + qubit)) + (j & bit_mask) + (k << qubit)
  199. old_matrix = represent(matrix, **options)
  200. old_size = old_matrix.cols
  201. #we expect the old_size to be even
  202. new_size = old_size//2
  203. new_matrix = Matrix().zeros(new_size)
  204. for i in range(new_size):
  205. for j in range(new_size):
  206. for k in range(2):
  207. col = find_index_that_is_projected(j, k, qubit)
  208. row = find_index_that_is_projected(i, k, qubit)
  209. new_matrix[i, j] += old_matrix[row, col]
  210. return new_matrix
  211. class QubitBra(QubitState, Bra):
  212. """A multi-qubit bra in the computational (z) basis.
  213. We use the normal convention that the least significant qubit is on the
  214. right, so ``|00001>`` has a 1 in the least significant qubit.
  215. Parameters
  216. ==========
  217. values : list, str
  218. The qubit values as a list of ints ([0,0,0,1,1,]) or a string ('011').
  219. See also
  220. ========
  221. Qubit: Examples using qubits
  222. """
  223. @classmethod
  224. def dual_class(self):
  225. return Qubit
  226. class IntQubitState(QubitState):
  227. """A base class for qubits that work with binary representations."""
  228. @classmethod
  229. def _eval_args(cls, args, nqubits=None):
  230. # The case of a QubitState instance
  231. if len(args) == 1 and isinstance(args[0], QubitState):
  232. return QubitState._eval_args(args)
  233. # otherwise, args should be integer
  234. elif not all(isinstance(a, (int, Integer)) for a in args):
  235. raise ValueError('values must be integers, got (%s)' % (tuple(type(a) for a in args),))
  236. # use nqubits if specified
  237. if nqubits is not None:
  238. if not isinstance(nqubits, (int, Integer)):
  239. raise ValueError('nqubits must be an integer, got (%s)' % type(nqubits))
  240. if len(args) != 1:
  241. raise ValueError(
  242. 'too many positional arguments (%s). should be (number, nqubits=n)' % (args,))
  243. return cls._eval_args_with_nqubits(args[0], nqubits)
  244. # For a single argument, we construct the binary representation of
  245. # that integer with the minimal number of bits.
  246. if len(args) == 1 and args[0] > 1:
  247. #rvalues is the minimum number of bits needed to express the number
  248. rvalues = reversed(range(bitcount(abs(args[0]))))
  249. qubit_values = [(args[0] >> i) & 1 for i in rvalues]
  250. return QubitState._eval_args(qubit_values)
  251. # For two numbers, the second number is the number of bits
  252. # on which it is expressed, so IntQubit(0,5) == |00000>.
  253. elif len(args) == 2 and args[1] > 1:
  254. return cls._eval_args_with_nqubits(args[0], args[1])
  255. else:
  256. return QubitState._eval_args(args)
  257. @classmethod
  258. def _eval_args_with_nqubits(cls, number, nqubits):
  259. need = bitcount(abs(number))
  260. if nqubits < need:
  261. raise ValueError(
  262. 'cannot represent %s with %s bits' % (number, nqubits))
  263. qubit_values = [(number >> i) & 1 for i in reversed(range(nqubits))]
  264. return QubitState._eval_args(qubit_values)
  265. def as_int(self):
  266. """Return the numerical value of the qubit."""
  267. number = 0
  268. n = 1
  269. for i in reversed(self.qubit_values):
  270. number += n*i
  271. n = n << 1
  272. return number
  273. def _print_label(self, printer, *args):
  274. return str(self.as_int())
  275. def _print_label_pretty(self, printer, *args):
  276. label = self._print_label(printer, *args)
  277. return prettyForm(label)
  278. _print_label_repr = _print_label
  279. _print_label_latex = _print_label
  280. class IntQubit(IntQubitState, Qubit):
  281. """A qubit ket that store integers as binary numbers in qubit values.
  282. The differences between this class and ``Qubit`` are:
  283. * The form of the constructor.
  284. * The qubit values are printed as their corresponding integer, rather
  285. than the raw qubit values. The internal storage format of the qubit
  286. values in the same as ``Qubit``.
  287. Parameters
  288. ==========
  289. values : int, tuple
  290. If a single argument, the integer we want to represent in the qubit
  291. values. This integer will be represented using the fewest possible
  292. number of qubits.
  293. If a pair of integers and the second value is more than one, the first
  294. integer gives the integer to represent in binary form and the second
  295. integer gives the number of qubits to use.
  296. List of zeros and ones is also accepted to generate qubit by bit pattern.
  297. nqubits : int
  298. The integer that represents the number of qubits.
  299. This number should be passed with keyword ``nqubits=N``.
  300. You can use this in order to avoid ambiguity of Qubit-style tuple of bits.
  301. Please see the example below for more details.
  302. Examples
  303. ========
  304. Create a qubit for the integer 5:
  305. >>> from sympy.physics.quantum.qubit import IntQubit
  306. >>> from sympy.physics.quantum.qubit import Qubit
  307. >>> q = IntQubit(5)
  308. >>> q
  309. |5>
  310. We can also create an ``IntQubit`` by passing a ``Qubit`` instance.
  311. >>> q = IntQubit(Qubit('101'))
  312. >>> q
  313. |5>
  314. >>> q.as_int()
  315. 5
  316. >>> q.nqubits
  317. 3
  318. >>> q.qubit_values
  319. (1, 0, 1)
  320. We can go back to the regular qubit form.
  321. >>> Qubit(q)
  322. |101>
  323. Please note that ``IntQubit`` also accepts a ``Qubit``-style list of bits.
  324. So, the code below yields qubits 3, not a single bit ``1``.
  325. >>> IntQubit(1, 1)
  326. |3>
  327. To avoid ambiguity, use ``nqubits`` parameter.
  328. Use of this keyword is recommended especially when you provide the values by variables.
  329. >>> IntQubit(1, nqubits=1)
  330. |1>
  331. >>> a = 1
  332. >>> IntQubit(a, nqubits=1)
  333. |1>
  334. """
  335. @classmethod
  336. def dual_class(self):
  337. return IntQubitBra
  338. def _eval_innerproduct_IntQubitBra(self, bra, **hints):
  339. return Qubit._eval_innerproduct_QubitBra(self, bra)
  340. class IntQubitBra(IntQubitState, QubitBra):
  341. """A qubit bra that store integers as binary numbers in qubit values."""
  342. @classmethod
  343. def dual_class(self):
  344. return IntQubit
  345. #-----------------------------------------------------------------------------
  346. # Qubit <---> Matrix conversion functions
  347. #-----------------------------------------------------------------------------
  348. def matrix_to_qubit(matrix):
  349. """Convert from the matrix repr. to a sum of Qubit objects.
  350. Parameters
  351. ----------
  352. matrix : Matrix, numpy.matrix, scipy.sparse
  353. The matrix to build the Qubit representation of. This works with
  354. SymPy matrices, numpy matrices and scipy.sparse sparse matrices.
  355. Examples
  356. ========
  357. Represent a state and then go back to its qubit form:
  358. >>> from sympy.physics.quantum.qubit import matrix_to_qubit, Qubit
  359. >>> from sympy.physics.quantum.represent import represent
  360. >>> q = Qubit('01')
  361. >>> matrix_to_qubit(represent(q))
  362. |01>
  363. """
  364. # Determine the format based on the type of the input matrix
  365. format = 'sympy'
  366. if isinstance(matrix, numpy_ndarray):
  367. format = 'numpy'
  368. if isinstance(matrix, scipy_sparse_matrix):
  369. format = 'scipy.sparse'
  370. # Make sure it is of correct dimensions for a Qubit-matrix representation.
  371. # This logic should work with sympy, numpy or scipy.sparse matrices.
  372. if matrix.shape[0] == 1:
  373. mlistlen = matrix.shape[1]
  374. nqubits = log(mlistlen, 2)
  375. ket = False
  376. cls = QubitBra
  377. elif matrix.shape[1] == 1:
  378. mlistlen = matrix.shape[0]
  379. nqubits = log(mlistlen, 2)
  380. ket = True
  381. cls = Qubit
  382. else:
  383. raise QuantumError(
  384. 'Matrix must be a row/column vector, got %r' % matrix
  385. )
  386. if not isinstance(nqubits, Integer):
  387. raise QuantumError('Matrix must be a row/column vector of size '
  388. '2**nqubits, got: %r' % matrix)
  389. # Go through each item in matrix, if element is non-zero, make it into a
  390. # Qubit item times the element.
  391. result = 0
  392. for i in range(mlistlen):
  393. if ket:
  394. element = matrix[i, 0]
  395. else:
  396. element = matrix[0, i]
  397. if format in ('numpy', 'scipy.sparse'):
  398. element = complex(element)
  399. if element != 0.0:
  400. # Form Qubit array; 0 in bit-locations where i is 0, 1 in
  401. # bit-locations where i is 1
  402. qubit_array = [int(i & (1 << x) != 0) for x in range(nqubits)]
  403. qubit_array.reverse()
  404. result = result + element*cls(*qubit_array)
  405. # If SymPy simplified by pulling out a constant coefficient, undo that.
  406. if isinstance(result, (Mul, Add, Pow)):
  407. result = result.expand()
  408. return result
  409. def matrix_to_density(mat):
  410. """
  411. Works by finding the eigenvectors and eigenvalues of the matrix.
  412. We know we can decompose rho by doing:
  413. sum(EigenVal*|Eigenvect><Eigenvect|)
  414. """
  415. from sympy.physics.quantum.density import Density
  416. eigen = mat.eigenvects()
  417. args = [[matrix_to_qubit(Matrix(
  418. [vector, ])), x[0]] for x in eigen for vector in x[2] if x[0] != 0]
  419. if (len(args) == 0):
  420. return S.Zero
  421. else:
  422. return Density(*args)
  423. def qubit_to_matrix(qubit, format='sympy'):
  424. """Converts an Add/Mul of Qubit objects into it's matrix representation
  425. This function is the inverse of ``matrix_to_qubit`` and is a shorthand
  426. for ``represent(qubit)``.
  427. """
  428. return represent(qubit, format=format)
  429. #-----------------------------------------------------------------------------
  430. # Measurement
  431. #-----------------------------------------------------------------------------
  432. def measure_all(qubit, format='sympy', normalize=True):
  433. """Perform an ensemble measurement of all qubits.
  434. Parameters
  435. ==========
  436. qubit : Qubit, Add
  437. The qubit to measure. This can be any Qubit or a linear combination
  438. of them.
  439. format : str
  440. The format of the intermediate matrices to use. Possible values are
  441. ('sympy','numpy','scipy.sparse'). Currently only 'sympy' is
  442. implemented.
  443. Returns
  444. =======
  445. result : list
  446. A list that consists of primitive states and their probabilities.
  447. Examples
  448. ========
  449. >>> from sympy.physics.quantum.qubit import Qubit, measure_all
  450. >>> from sympy.physics.quantum.gate import H
  451. >>> from sympy.physics.quantum.qapply import qapply
  452. >>> c = H(0)*H(1)*Qubit('00')
  453. >>> c
  454. H(0)*H(1)*|00>
  455. >>> q = qapply(c)
  456. >>> measure_all(q)
  457. [(|00>, 1/4), (|01>, 1/4), (|10>, 1/4), (|11>, 1/4)]
  458. """
  459. m = qubit_to_matrix(qubit, format)
  460. if format == 'sympy':
  461. results = []
  462. if normalize:
  463. m = m.normalized()
  464. size = max(m.shape) # Max of shape to account for bra or ket
  465. nqubits = int(math.log(size)/math.log(2))
  466. for i in range(size):
  467. if m[i] != 0.0:
  468. results.append(
  469. (Qubit(IntQubit(i, nqubits=nqubits)), m[i]*conjugate(m[i]))
  470. )
  471. return results
  472. else:
  473. raise NotImplementedError(
  474. "This function cannot handle non-SymPy matrix formats yet"
  475. )
  476. def measure_partial(qubit, bits, format='sympy', normalize=True):
  477. """Perform a partial ensemble measure on the specified qubits.
  478. Parameters
  479. ==========
  480. qubits : Qubit
  481. The qubit to measure. This can be any Qubit or a linear combination
  482. of them.
  483. bits : tuple
  484. The qubits to measure.
  485. format : str
  486. The format of the intermediate matrices to use. Possible values are
  487. ('sympy','numpy','scipy.sparse'). Currently only 'sympy' is
  488. implemented.
  489. Returns
  490. =======
  491. result : list
  492. A list that consists of primitive states and their probabilities.
  493. Examples
  494. ========
  495. >>> from sympy.physics.quantum.qubit import Qubit, measure_partial
  496. >>> from sympy.physics.quantum.gate import H
  497. >>> from sympy.physics.quantum.qapply import qapply
  498. >>> c = H(0)*H(1)*Qubit('00')
  499. >>> c
  500. H(0)*H(1)*|00>
  501. >>> q = qapply(c)
  502. >>> measure_partial(q, (0,))
  503. [(sqrt(2)*|00>/2 + sqrt(2)*|10>/2, 1/2), (sqrt(2)*|01>/2 + sqrt(2)*|11>/2, 1/2)]
  504. """
  505. m = qubit_to_matrix(qubit, format)
  506. if isinstance(bits, (SYMPY_INTS, Integer)):
  507. bits = (int(bits),)
  508. if format == 'sympy':
  509. if normalize:
  510. m = m.normalized()
  511. possible_outcomes = _get_possible_outcomes(m, bits)
  512. # Form output from function.
  513. output = []
  514. for outcome in possible_outcomes:
  515. # Calculate probability of finding the specified bits with
  516. # given values.
  517. prob_of_outcome = 0
  518. prob_of_outcome += (outcome.H*outcome)[0]
  519. # If the output has a chance, append it to output with found
  520. # probability.
  521. if prob_of_outcome != 0:
  522. if normalize:
  523. next_matrix = matrix_to_qubit(outcome.normalized())
  524. else:
  525. next_matrix = matrix_to_qubit(outcome)
  526. output.append((
  527. next_matrix,
  528. prob_of_outcome
  529. ))
  530. return output
  531. else:
  532. raise NotImplementedError(
  533. "This function cannot handle non-SymPy matrix formats yet"
  534. )
  535. def measure_partial_oneshot(qubit, bits, format='sympy'):
  536. """Perform a partial oneshot measurement on the specified qubits.
  537. A oneshot measurement is equivalent to performing a measurement on a
  538. quantum system. This type of measurement does not return the probabilities
  539. like an ensemble measurement does, but rather returns *one* of the
  540. possible resulting states. The exact state that is returned is determined
  541. by picking a state randomly according to the ensemble probabilities.
  542. Parameters
  543. ----------
  544. qubits : Qubit
  545. The qubit to measure. This can be any Qubit or a linear combination
  546. of them.
  547. bits : tuple
  548. The qubits to measure.
  549. format : str
  550. The format of the intermediate matrices to use. Possible values are
  551. ('sympy','numpy','scipy.sparse'). Currently only 'sympy' is
  552. implemented.
  553. Returns
  554. -------
  555. result : Qubit
  556. The qubit that the system collapsed to upon measurement.
  557. """
  558. import random
  559. m = qubit_to_matrix(qubit, format)
  560. if format == 'sympy':
  561. m = m.normalized()
  562. possible_outcomes = _get_possible_outcomes(m, bits)
  563. # Form output from function
  564. random_number = random.random()
  565. total_prob = 0
  566. for outcome in possible_outcomes:
  567. # Calculate probability of finding the specified bits
  568. # with given values
  569. total_prob += (outcome.H*outcome)[0]
  570. if total_prob >= random_number:
  571. return matrix_to_qubit(outcome.normalized())
  572. else:
  573. raise NotImplementedError(
  574. "This function cannot handle non-SymPy matrix formats yet"
  575. )
  576. def _get_possible_outcomes(m, bits):
  577. """Get the possible states that can be produced in a measurement.
  578. Parameters
  579. ----------
  580. m : Matrix
  581. The matrix representing the state of the system.
  582. bits : tuple, list
  583. Which bits will be measured.
  584. Returns
  585. -------
  586. result : list
  587. The list of possible states which can occur given this measurement.
  588. These are un-normalized so we can derive the probability of finding
  589. this state by taking the inner product with itself
  590. """
  591. # This is filled with loads of dirty binary tricks...You have been warned
  592. size = max(m.shape) # Max of shape to account for bra or ket
  593. nqubits = int(math.log(size, 2) + .1) # Number of qubits possible
  594. # Make the output states and put in output_matrices, nothing in them now.
  595. # Each state will represent a possible outcome of the measurement
  596. # Thus, output_matrices[0] is the matrix which we get when all measured
  597. # bits return 0. and output_matrices[1] is the matrix for only the 0th
  598. # bit being true
  599. output_matrices = []
  600. for i in range(1 << len(bits)):
  601. output_matrices.append(zeros(2**nqubits, 1))
  602. # Bitmasks will help sort how to determine possible outcomes.
  603. # When the bit mask is and-ed with a matrix-index,
  604. # it will determine which state that index belongs to
  605. bit_masks = []
  606. for bit in bits:
  607. bit_masks.append(1 << bit)
  608. # Make possible outcome states
  609. for i in range(2**nqubits):
  610. trueness = 0 # This tells us to which output_matrix this value belongs
  611. # Find trueness
  612. for j in range(len(bit_masks)):
  613. if i & bit_masks[j]:
  614. trueness += j + 1
  615. # Put the value in the correct output matrix
  616. output_matrices[trueness][i] = m[i]
  617. return output_matrices
  618. def measure_all_oneshot(qubit, format='sympy'):
  619. """Perform a oneshot ensemble measurement on all qubits.
  620. A oneshot measurement is equivalent to performing a measurement on a
  621. quantum system. This type of measurement does not return the probabilities
  622. like an ensemble measurement does, but rather returns *one* of the
  623. possible resulting states. The exact state that is returned is determined
  624. by picking a state randomly according to the ensemble probabilities.
  625. Parameters
  626. ----------
  627. qubits : Qubit
  628. The qubit to measure. This can be any Qubit or a linear combination
  629. of them.
  630. format : str
  631. The format of the intermediate matrices to use. Possible values are
  632. ('sympy','numpy','scipy.sparse'). Currently only 'sympy' is
  633. implemented.
  634. Returns
  635. -------
  636. result : Qubit
  637. The qubit that the system collapsed to upon measurement.
  638. """
  639. import random
  640. m = qubit_to_matrix(qubit)
  641. if format == 'sympy':
  642. m = m.normalized()
  643. random_number = random.random()
  644. total = 0
  645. result = 0
  646. for i in m:
  647. total += i*i.conjugate()
  648. if total > random_number:
  649. break
  650. result += 1
  651. return Qubit(IntQubit(result, int(math.log(max(m.shape), 2) + .1)))
  652. else:
  653. raise NotImplementedError(
  654. "This function cannot handle non-SymPy matrix formats yet"
  655. )