qapply.py 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. """Logic for applying operators to states.
  2. Todo:
  3. * Sometimes the final result needs to be expanded, we should do this by hand.
  4. """
  5. from sympy.concrete import Sum
  6. from sympy.core.add import Add
  7. from sympy.core.kind import NumberKind
  8. from sympy.core.mul import Mul
  9. from sympy.core.power import Pow
  10. from sympy.core.singleton import S
  11. from sympy.core.sympify import sympify, _sympify
  12. from sympy.physics.quantum.anticommutator import AntiCommutator
  13. from sympy.physics.quantum.commutator import Commutator
  14. from sympy.physics.quantum.dagger import Dagger
  15. from sympy.physics.quantum.innerproduct import InnerProduct
  16. from sympy.physics.quantum.operator import OuterProduct, Operator
  17. from sympy.physics.quantum.state import State, KetBase, BraBase, Wavefunction
  18. from sympy.physics.quantum.tensorproduct import TensorProduct
  19. __all__ = [
  20. 'qapply'
  21. ]
  22. #-----------------------------------------------------------------------------
  23. # Main code
  24. #-----------------------------------------------------------------------------
  25. def ip_doit_func(e):
  26. """Transform the inner products in an expression by calling ``.doit()``."""
  27. return e.replace(InnerProduct, lambda *args: InnerProduct(*args).doit())
  28. def sum_doit_func(e):
  29. """Transform the sums in an expression by calling ``.doit()``."""
  30. return e.replace(Sum, lambda *args: Sum(*args).doit())
  31. def qapply(e, **options):
  32. """Apply operators to states in a quantum expression.
  33. Parameters
  34. ==========
  35. e : Expr
  36. The expression containing operators and states. This expression tree
  37. will be walked to find operators acting on states symbolically.
  38. options : dict
  39. A dict of key/value pairs that determine how the operator actions
  40. are carried out.
  41. The following options are valid:
  42. * ``dagger``: try to apply Dagger operators to the left
  43. (default: False).
  44. * ``ip_doit``: call ``.doit()`` in inner products when they are
  45. encountered (default: True).
  46. * ``sum_doit``: call ``.doit()`` on sums when they are encountered
  47. (default: False). This is helpful for collapsing sums over Kronecker
  48. delta's that are created when calling ``qapply``.
  49. Returns
  50. =======
  51. e : Expr
  52. The original expression, but with the operators applied to states.
  53. Examples
  54. ========
  55. >>> from sympy.physics.quantum import qapply, Ket, Bra
  56. >>> b = Bra('b')
  57. >>> k = Ket('k')
  58. >>> A = k * b
  59. >>> A
  60. |k><b|
  61. >>> qapply(A * b.dual / (b * b.dual))
  62. |k>
  63. >>> qapply(k.dual * A / (k.dual * k))
  64. <b|
  65. """
  66. from sympy.physics.quantum.density import Density
  67. dagger = options.get('dagger', False)
  68. sum_doit = options.get('sum_doit', False)
  69. ip_doit = options.get('ip_doit', True)
  70. e = _sympify(e)
  71. # Using the kind API here helps us to narrow what types of expressions
  72. # we call ``ip_doit_func`` on.
  73. if e.kind == NumberKind:
  74. return ip_doit_func(e) if ip_doit else e
  75. # This may be a bit aggressive but ensures that everything gets expanded
  76. # to its simplest form before trying to apply operators. This includes
  77. # things like (A+B+C)*|a> and A*(|a>+|b>) and all Commutators and
  78. # TensorProducts. The only problem with this is that if we can't apply
  79. # all the Operators, we have just expanded everything.
  80. # TODO: don't expand the scalars in front of each Mul.
  81. e = e.expand(commutator=True, tensorproduct=True)
  82. # If we just have a raw ket, return it.
  83. if isinstance(e, KetBase):
  84. return e
  85. # We have an Add(a, b, c, ...) and compute
  86. # Add(qapply(a), qapply(b), ...)
  87. elif isinstance(e, Add):
  88. result = 0
  89. for arg in e.args:
  90. result += qapply(arg, **options)
  91. return result.expand()
  92. # For a Density operator call qapply on its state
  93. elif isinstance(e, Density):
  94. new_args = [(qapply(state, **options), prob) for (state,
  95. prob) in e.args]
  96. return Density(*new_args)
  97. # For a raw TensorProduct, call qapply on its args.
  98. elif isinstance(e, TensorProduct):
  99. return TensorProduct(*[qapply(t, **options) for t in e.args])
  100. # For a Sum, call qapply on its function.
  101. elif isinstance(e, Sum):
  102. result = Sum(qapply(e.function, **options), *e.limits)
  103. result = sum_doit_func(result) if sum_doit else result
  104. return result
  105. # For a Pow, call qapply on its base.
  106. elif isinstance(e, Pow):
  107. return qapply(e.base, **options)**e.exp
  108. # We have a Mul where there might be actual operators to apply to kets.
  109. elif isinstance(e, Mul):
  110. c_part, nc_part = e.args_cnc()
  111. c_mul = Mul(*c_part)
  112. nc_mul = Mul(*nc_part)
  113. if not nc_part: # If we only have a commuting part, just return it.
  114. result = c_mul
  115. elif isinstance(nc_mul, Mul):
  116. result = c_mul*qapply_Mul(nc_mul, **options)
  117. else:
  118. result = c_mul*qapply(nc_mul, **options)
  119. if result == e and dagger:
  120. result = Dagger(qapply_Mul(Dagger(e), **options))
  121. result = ip_doit_func(result) if ip_doit else result
  122. result = sum_doit_func(result) if sum_doit else result
  123. return result
  124. # In all other cases (State, Operator, Pow, Commutator, InnerProduct,
  125. # OuterProduct) we won't ever have operators to apply to kets.
  126. else:
  127. return e
  128. def qapply_Mul(e, **options):
  129. args = list(e.args)
  130. extra = S.One
  131. result = None
  132. # If we only have 0 or 1 args, we have nothing to do and return.
  133. if len(args) <= 1 or not isinstance(e, Mul):
  134. return e
  135. rhs = args.pop()
  136. lhs = args.pop()
  137. # Make sure we have two non-commutative objects before proceeding.
  138. if (not isinstance(rhs, Wavefunction) and sympify(rhs).is_commutative) or \
  139. (not isinstance(lhs, Wavefunction) and sympify(lhs).is_commutative):
  140. return e
  141. # For a Pow with an integer exponent, apply one of them and reduce the
  142. # exponent by one.
  143. if isinstance(lhs, Pow) and lhs.exp.is_Integer:
  144. args.append(lhs.base**(lhs.exp - 1))
  145. lhs = lhs.base
  146. # Pull OuterProduct apart
  147. if isinstance(lhs, OuterProduct):
  148. args.append(lhs.ket)
  149. lhs = lhs.bra
  150. if isinstance(rhs, OuterProduct):
  151. extra = rhs.bra # Append to the right of the result
  152. rhs = rhs.ket
  153. # Call .doit() on Commutator/AntiCommutator.
  154. if isinstance(lhs, (Commutator, AntiCommutator)):
  155. comm = lhs.doit()
  156. if isinstance(comm, Add):
  157. return qapply(
  158. e.func(*(args + [comm.args[0], rhs])) +
  159. e.func(*(args + [comm.args[1], rhs])),
  160. **options
  161. )*extra
  162. else:
  163. return qapply(e.func(*args)*comm*rhs, **options)*extra
  164. # Apply tensor products of operators to states
  165. if isinstance(lhs, TensorProduct) and all(isinstance(arg, (Operator, State, Mul, Pow)) or arg == 1 for arg in lhs.args) and \
  166. isinstance(rhs, TensorProduct) and all(isinstance(arg, (Operator, State, Mul, Pow)) or arg == 1 for arg in rhs.args) and \
  167. len(lhs.args) == len(rhs.args):
  168. result = TensorProduct(*[qapply(lhs.args[n]*rhs.args[n], **options) for n in range(len(lhs.args))]).expand(tensorproduct=True)
  169. return qapply_Mul(e.func(*args), **options)*result*extra
  170. # For Sums, move the Sum to the right.
  171. if isinstance(rhs, Sum):
  172. if isinstance(lhs, Sum):
  173. if set(lhs.variables).intersection(set(rhs.variables)):
  174. raise ValueError('Duplicated dummy indices in separate sums in qapply.')
  175. limits = lhs.limits + rhs.limits
  176. result = Sum(qapply(lhs.function*rhs.function, **options), *limits)
  177. return qapply_Mul(e.func(*args)*result, **options)
  178. else:
  179. result = Sum(qapply(lhs*rhs.function, **options), *rhs.limits)
  180. return qapply_Mul(e.func(*args)*result, **options)
  181. if isinstance(lhs, Sum):
  182. result = Sum(qapply(lhs.function*rhs, **options), *lhs.limits)
  183. return qapply_Mul(e.func(*args)*result, **options)
  184. # Now try to actually apply the operator and build an inner product.
  185. _apply = getattr(lhs, '_apply_operator', None)
  186. if _apply is not None:
  187. try:
  188. result = _apply(rhs, **options)
  189. except NotImplementedError:
  190. result = None
  191. else:
  192. result = None
  193. if result is None:
  194. _apply_right = getattr(rhs, '_apply_from_right_to', None)
  195. if _apply_right is not None:
  196. try:
  197. result = _apply_right(lhs, **options)
  198. except NotImplementedError:
  199. result = None
  200. if result is None:
  201. if isinstance(lhs, BraBase) and isinstance(rhs, KetBase):
  202. result = InnerProduct(lhs, rhs)
  203. # TODO: I may need to expand before returning the final result.
  204. if isinstance(result, (int, complex, float)):
  205. return _sympify(result)
  206. elif result is None:
  207. if len(args) == 0:
  208. # We had two args to begin with so args=[].
  209. return e
  210. else:
  211. return qapply_Mul(e.func(*(args + [lhs])), **options)*rhs*extra
  212. elif isinstance(result, InnerProduct):
  213. return result*qapply_Mul(e.func(*args), **options)*extra
  214. else: # result is a scalar times a Mul, Add or TensorProduct
  215. return qapply(e.func(*args)*result, **options)*extra