density.py 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. from itertools import product
  2. from sympy.core.add import Add
  3. from sympy.core.containers import Tuple
  4. from sympy.core.function import expand
  5. from sympy.core.mul import Mul
  6. from sympy.core.singleton import S
  7. from sympy.functions.elementary.exponential import log
  8. from sympy.matrices.dense import MutableDenseMatrix as Matrix
  9. from sympy.printing.pretty.stringpict import prettyForm
  10. from sympy.physics.quantum.dagger import Dagger
  11. from sympy.physics.quantum.operator import HermitianOperator
  12. from sympy.physics.quantum.represent import represent
  13. from sympy.physics.quantum.matrixutils import numpy_ndarray, scipy_sparse_matrix, to_numpy
  14. from sympy.physics.quantum.trace import Tr
  15. class Density(HermitianOperator):
  16. """Density operator for representing mixed states.
  17. TODO: Density operator support for Qubits
  18. Parameters
  19. ==========
  20. values : tuples/lists
  21. Each tuple/list should be of form (state, prob) or [state,prob]
  22. Examples
  23. ========
  24. Create a density operator with 2 states represented by Kets.
  25. >>> from sympy.physics.quantum.state import Ket
  26. >>> from sympy.physics.quantum.density import Density
  27. >>> d = Density([Ket(0), 0.5], [Ket(1),0.5])
  28. >>> d
  29. Density((|0>, 0.5),(|1>, 0.5))
  30. """
  31. @classmethod
  32. def _eval_args(cls, args):
  33. # call this to qsympify the args
  34. args = super()._eval_args(args)
  35. for arg in args:
  36. # Check if arg is a tuple
  37. if not (isinstance(arg, Tuple) and len(arg) == 2):
  38. raise ValueError("Each argument should be of form [state,prob]"
  39. " or ( state, prob )")
  40. return args
  41. def states(self):
  42. """Return list of all states.
  43. Examples
  44. ========
  45. >>> from sympy.physics.quantum.state import Ket
  46. >>> from sympy.physics.quantum.density import Density
  47. >>> d = Density([Ket(0), 0.5], [Ket(1),0.5])
  48. >>> d.states()
  49. (|0>, |1>)
  50. """
  51. return Tuple(*[arg[0] for arg in self.args])
  52. def probs(self):
  53. """Return list of all probabilities.
  54. Examples
  55. ========
  56. >>> from sympy.physics.quantum.state import Ket
  57. >>> from sympy.physics.quantum.density import Density
  58. >>> d = Density([Ket(0), 0.5], [Ket(1),0.5])
  59. >>> d.probs()
  60. (0.5, 0.5)
  61. """
  62. return Tuple(*[arg[1] for arg in self.args])
  63. def get_state(self, index):
  64. """Return specific state by index.
  65. Parameters
  66. ==========
  67. index : index of state to be returned
  68. Examples
  69. ========
  70. >>> from sympy.physics.quantum.state import Ket
  71. >>> from sympy.physics.quantum.density import Density
  72. >>> d = Density([Ket(0), 0.5], [Ket(1),0.5])
  73. >>> d.states()[1]
  74. |1>
  75. """
  76. state = self.args[index][0]
  77. return state
  78. def get_prob(self, index):
  79. """Return probability of specific state by index.
  80. Parameters
  81. ===========
  82. index : index of states whose probability is returned.
  83. Examples
  84. ========
  85. >>> from sympy.physics.quantum.state import Ket
  86. >>> from sympy.physics.quantum.density import Density
  87. >>> d = Density([Ket(0), 0.5], [Ket(1),0.5])
  88. >>> d.probs()[1]
  89. 0.500000000000000
  90. """
  91. prob = self.args[index][1]
  92. return prob
  93. def apply_op(self, op):
  94. """op will operate on each individual state.
  95. Parameters
  96. ==========
  97. op : Operator
  98. Examples
  99. ========
  100. >>> from sympy.physics.quantum.state import Ket
  101. >>> from sympy.physics.quantum.density import Density
  102. >>> from sympy.physics.quantum.operator import Operator
  103. >>> A = Operator('A')
  104. >>> d = Density([Ket(0), 0.5], [Ket(1),0.5])
  105. >>> d.apply_op(A)
  106. Density((A*|0>, 0.5),(A*|1>, 0.5))
  107. """
  108. new_args = [(op*state, prob) for (state, prob) in self.args]
  109. return Density(*new_args)
  110. def doit(self, **hints):
  111. """Expand the density operator into an outer product format.
  112. Examples
  113. ========
  114. >>> from sympy.physics.quantum.state import Ket
  115. >>> from sympy.physics.quantum.density import Density
  116. >>> from sympy.physics.quantum.operator import Operator
  117. >>> A = Operator('A')
  118. >>> d = Density([Ket(0), 0.5], [Ket(1),0.5])
  119. >>> d.doit()
  120. 0.5*|0><0| + 0.5*|1><1|
  121. """
  122. terms = []
  123. for (state, prob) in self.args:
  124. state = state.expand() # needed to break up (a+b)*c
  125. if (isinstance(state, Add)):
  126. for arg in product(state.args, repeat=2):
  127. terms.append(prob*self._generate_outer_prod(arg[0],
  128. arg[1]))
  129. else:
  130. terms.append(prob*self._generate_outer_prod(state, state))
  131. return Add(*terms)
  132. def _generate_outer_prod(self, arg1, arg2):
  133. c_part1, nc_part1 = arg1.args_cnc()
  134. c_part2, nc_part2 = arg2.args_cnc()
  135. if (len(nc_part1) == 0 or len(nc_part2) == 0):
  136. raise ValueError('Atleast one-pair of'
  137. ' Non-commutative instance required'
  138. ' for outer product.')
  139. # We were able to remove some tensor product simplifications that
  140. # used to be here as those transformations are not automatically
  141. # applied by transforms.py.
  142. op = Mul(*nc_part1)*Dagger(Mul(*nc_part2))
  143. return Mul(*c_part1)*Mul(*c_part2) * op
  144. def _represent(self, **options):
  145. return represent(self.doit(), **options)
  146. def _print_operator_name_latex(self, printer, *args):
  147. return r'\rho'
  148. def _print_operator_name_pretty(self, printer, *args):
  149. return prettyForm('\N{GREEK SMALL LETTER RHO}')
  150. def _eval_trace(self, **kwargs):
  151. indices = kwargs.get('indices', [])
  152. return Tr(self.doit(), indices).doit()
  153. def entropy(self):
  154. """ Compute the entropy of a density matrix.
  155. Refer to density.entropy() method for examples.
  156. """
  157. return entropy(self)
  158. def entropy(density):
  159. """Compute the entropy of a matrix/density object.
  160. This computes -Tr(density*ln(density)) using the eigenvalue decomposition
  161. of density, which is given as either a Density instance or a matrix
  162. (numpy.ndarray, sympy.Matrix or scipy.sparse).
  163. Parameters
  164. ==========
  165. density : density matrix of type Density, SymPy matrix,
  166. scipy.sparse or numpy.ndarray
  167. Examples
  168. ========
  169. >>> from sympy.physics.quantum.density import Density, entropy
  170. >>> from sympy.physics.quantum.spin import JzKet
  171. >>> from sympy import S
  172. >>> up = JzKet(S(1)/2,S(1)/2)
  173. >>> down = JzKet(S(1)/2,-S(1)/2)
  174. >>> d = Density((up,S(1)/2),(down,S(1)/2))
  175. >>> entropy(d)
  176. log(2)/2
  177. """
  178. if isinstance(density, Density):
  179. density = represent(density) # represent in Matrix
  180. if isinstance(density, scipy_sparse_matrix):
  181. density = to_numpy(density)
  182. if isinstance(density, Matrix):
  183. eigvals = density.eigenvals().keys()
  184. return expand(-sum(e*log(e) for e in eigvals))
  185. elif isinstance(density, numpy_ndarray):
  186. import numpy as np
  187. eigvals = np.linalg.eigvals(density)
  188. return -np.sum(eigvals*np.log(eigvals))
  189. else:
  190. raise ValueError(
  191. "numpy.ndarray, scipy.sparse or SymPy matrix expected")
  192. def fidelity(state1, state2):
  193. """ Computes the fidelity [1]_ between two quantum states
  194. The arguments provided to this function should be a square matrix or a
  195. Density object. If it is a square matrix, it is assumed to be diagonalizable.
  196. Parameters
  197. ==========
  198. state1, state2 : a density matrix or Matrix
  199. Examples
  200. ========
  201. >>> from sympy import S, sqrt
  202. >>> from sympy.physics.quantum.dagger import Dagger
  203. >>> from sympy.physics.quantum.spin import JzKet
  204. >>> from sympy.physics.quantum.density import fidelity
  205. >>> from sympy.physics.quantum.represent import represent
  206. >>>
  207. >>> up = JzKet(S(1)/2,S(1)/2)
  208. >>> down = JzKet(S(1)/2,-S(1)/2)
  209. >>> amp = 1/sqrt(2)
  210. >>> updown = (amp*up) + (amp*down)
  211. >>>
  212. >>> # represent turns Kets into matrices
  213. >>> up_dm = represent(up*Dagger(up))
  214. >>> down_dm = represent(down*Dagger(down))
  215. >>> updown_dm = represent(updown*Dagger(updown))
  216. >>>
  217. >>> fidelity(up_dm, up_dm)
  218. 1
  219. >>> fidelity(up_dm, down_dm) #orthogonal states
  220. 0
  221. >>> fidelity(up_dm, updown_dm).evalf().round(3)
  222. 0.707
  223. References
  224. ==========
  225. .. [1] https://en.wikipedia.org/wiki/Fidelity_of_quantum_states
  226. """
  227. state1 = represent(state1) if isinstance(state1, Density) else state1
  228. state2 = represent(state2) if isinstance(state2, Density) else state2
  229. if not isinstance(state1, Matrix) or not isinstance(state2, Matrix):
  230. raise ValueError("state1 and state2 must be of type Density or Matrix "
  231. "received type=%s for state1 and type=%s for state2" %
  232. (type(state1), type(state2)))
  233. if state1.shape != state2.shape and state1.is_square:
  234. raise ValueError("The dimensions of both args should be equal and the "
  235. "matrix obtained should be a square matrix")
  236. sqrt_state1 = state1**S.Half
  237. return Tr((sqrt_state1*state2*sqrt_state1)**S.Half).doit()