density.py 9.5 KB

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