represent.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. """Logic for representing operators in state in various bases.
  2. TODO:
  3. * Get represent working with continuous hilbert spaces.
  4. * Document default basis functionality.
  5. """
  6. from sympy.core.add import Add
  7. from sympy.core.expr import Expr
  8. from sympy.core.mul import Mul
  9. from sympy.core.numbers import I
  10. from sympy.core.power import Pow
  11. from sympy.integrals.integrals import integrate
  12. from sympy.physics.quantum.dagger import Dagger
  13. from sympy.physics.quantum.commutator import Commutator
  14. from sympy.physics.quantum.anticommutator import AntiCommutator
  15. from sympy.physics.quantum.innerproduct import InnerProduct
  16. from sympy.physics.quantum.qexpr import QExpr
  17. from sympy.physics.quantum.tensorproduct import TensorProduct
  18. from sympy.physics.quantum.matrixutils import flatten_scalar
  19. from sympy.physics.quantum.state import KetBase, BraBase, StateBase
  20. from sympy.physics.quantum.operator import Operator, OuterProduct
  21. from sympy.physics.quantum.qapply import qapply
  22. from sympy.physics.quantum.operatorset import operators_to_state, state_to_operators
  23. __all__ = [
  24. 'represent',
  25. 'rep_innerproduct',
  26. 'rep_expectation',
  27. 'integrate_result',
  28. 'get_basis',
  29. 'enumerate_states'
  30. ]
  31. #-----------------------------------------------------------------------------
  32. # Represent
  33. #-----------------------------------------------------------------------------
  34. def _sympy_to_scalar(e):
  35. """Convert from a SymPy scalar to a Python scalar."""
  36. if isinstance(e, Expr):
  37. if e.is_Integer:
  38. return int(e)
  39. elif e.is_Float:
  40. return float(e)
  41. elif e.is_Rational:
  42. return float(e)
  43. elif e.is_Number or e.is_NumberSymbol or e == I:
  44. return complex(e)
  45. raise TypeError('Expected number, got: %r' % e)
  46. def represent(expr, **options):
  47. """Represent the quantum expression in the given basis.
  48. In quantum mechanics abstract states and operators can be represented in
  49. various basis sets. Under this operation the follow transforms happen:
  50. * Ket -> column vector or function
  51. * Bra -> row vector of function
  52. * Operator -> matrix or differential operator
  53. This function is the top-level interface for this action.
  54. This function walks the SymPy expression tree looking for ``QExpr``
  55. instances that have a ``_represent`` method. This method is then called
  56. and the object is replaced by the representation returned by this method.
  57. By default, the ``_represent`` method will dispatch to other methods
  58. that handle the representation logic for a particular basis set. The
  59. naming convention for these methods is the following::
  60. def _represent_FooBasis(self, e, basis, **options)
  61. This function will have the logic for representing instances of its class
  62. in the basis set having a class named ``FooBasis``.
  63. Parameters
  64. ==========
  65. expr : Expr
  66. The expression to represent.
  67. basis : Operator, basis set
  68. An object that contains the information about the basis set. If an
  69. operator is used, the basis is assumed to be the orthonormal
  70. eigenvectors of that operator. In general though, the basis argument
  71. can be any object that contains the basis set information.
  72. options : dict
  73. Key/value pairs of options that are passed to the underlying method
  74. that finds the representation. These options can be used to
  75. control how the representation is done. For example, this is where
  76. the size of the basis set would be set.
  77. Returns
  78. =======
  79. e : Expr
  80. The SymPy expression of the represented quantum expression.
  81. Examples
  82. ========
  83. Here we subclass ``Operator`` and ``Ket`` to create the z-spin operator
  84. and its spin 1/2 up eigenstate. By defining the ``_represent_SzOp``
  85. method, the ket can be represented in the z-spin basis.
  86. >>> from sympy.physics.quantum import Operator, represent, Ket
  87. >>> from sympy import Matrix
  88. >>> class SzUpKet(Ket):
  89. ... def _represent_SzOp(self, basis, **options):
  90. ... return Matrix([1,0])
  91. ...
  92. >>> class SzOp(Operator):
  93. ... pass
  94. ...
  95. >>> sz = SzOp('Sz')
  96. >>> up = SzUpKet('up')
  97. >>> represent(up, basis=sz)
  98. Matrix([
  99. [1],
  100. [0]])
  101. Here we see an example of representations in a continuous
  102. basis. We see that the result of representing various combinations
  103. of cartesian position operators and kets give us continuous
  104. expressions involving DiracDelta functions.
  105. >>> from sympy.physics.quantum.cartesian import XOp, XKet, XBra
  106. >>> X = XOp()
  107. >>> x = XKet()
  108. >>> y = XBra('y')
  109. >>> represent(X*x)
  110. x*DiracDelta(x - x_2)
  111. >>> represent(X*x*y)
  112. x*DiracDelta(x - x_3)*DiracDelta(x_1 - y)
  113. """
  114. format = options.get('format', 'sympy')
  115. if isinstance(expr, QExpr) and not isinstance(expr, OuterProduct):
  116. options['replace_none'] = False
  117. temp_basis = get_basis(expr, **options)
  118. if temp_basis is not None:
  119. options['basis'] = temp_basis
  120. try:
  121. return expr._represent(**options)
  122. except NotImplementedError as strerr:
  123. #If no _represent_FOO method exists, map to the
  124. #appropriate basis state and try
  125. #the other methods of representation
  126. options['replace_none'] = True
  127. if isinstance(expr, (KetBase, BraBase)):
  128. try:
  129. return rep_innerproduct(expr, **options)
  130. except NotImplementedError:
  131. raise NotImplementedError(strerr)
  132. elif isinstance(expr, Operator):
  133. try:
  134. return rep_expectation(expr, **options)
  135. except NotImplementedError:
  136. raise NotImplementedError(strerr)
  137. else:
  138. raise NotImplementedError(strerr)
  139. elif isinstance(expr, Add):
  140. result = represent(expr.args[0], **options)
  141. for args in expr.args[1:]:
  142. # scipy.sparse doesn't support += so we use plain = here.
  143. result = result + represent(args, **options)
  144. return result
  145. elif isinstance(expr, Pow):
  146. base, exp = expr.as_base_exp()
  147. if format in ('numpy', 'scipy.sparse'):
  148. exp = _sympy_to_scalar(exp)
  149. base = represent(base, **options)
  150. # scipy.sparse doesn't support negative exponents
  151. # and warns when inverting a matrix in csr format.
  152. if format == 'scipy.sparse' and exp < 0:
  153. from scipy.sparse.linalg import inv
  154. exp = - exp
  155. base = inv(base.tocsc()).tocsr()
  156. return base ** exp
  157. elif isinstance(expr, TensorProduct):
  158. new_args = [represent(arg, **options) for arg in expr.args]
  159. return TensorProduct(*new_args)
  160. elif isinstance(expr, Dagger):
  161. return Dagger(represent(expr.args[0], **options))
  162. elif isinstance(expr, Commutator):
  163. A = represent(expr.args[0], **options)
  164. B = represent(expr.args[1], **options)
  165. return A*B - B*A
  166. elif isinstance(expr, AntiCommutator):
  167. A = represent(expr.args[0], **options)
  168. B = represent(expr.args[1], **options)
  169. return A*B + B*A
  170. elif isinstance(expr, InnerProduct):
  171. return represent(Mul(expr.bra, expr.ket), **options)
  172. elif not isinstance(expr, (Mul, OuterProduct)):
  173. # For numpy and scipy.sparse, we can only handle numerical prefactors.
  174. if format in ('numpy', 'scipy.sparse'):
  175. return _sympy_to_scalar(expr)
  176. return expr
  177. if not isinstance(expr, (Mul, OuterProduct)):
  178. raise TypeError('Mul expected, got: %r' % expr)
  179. if "index" in options:
  180. options["index"] += 1
  181. else:
  182. options["index"] = 1
  183. if "unities" not in options:
  184. options["unities"] = []
  185. result = represent(expr.args[-1], **options)
  186. last_arg = expr.args[-1]
  187. for arg in reversed(expr.args[:-1]):
  188. if isinstance(last_arg, Operator):
  189. options["index"] += 1
  190. options["unities"].append(options["index"])
  191. elif isinstance(last_arg, BraBase) and isinstance(arg, KetBase):
  192. options["index"] += 1
  193. elif isinstance(last_arg, KetBase) and isinstance(arg, Operator):
  194. options["unities"].append(options["index"])
  195. elif isinstance(last_arg, KetBase) and isinstance(arg, BraBase):
  196. options["unities"].append(options["index"])
  197. result = represent(arg, **options)*result
  198. last_arg = arg
  199. # All three matrix formats create 1 by 1 matrices when inner products of
  200. # vectors are taken. In these cases, we simply return a scalar.
  201. result = flatten_scalar(result)
  202. result = integrate_result(expr, result, **options)
  203. return result
  204. def rep_innerproduct(expr, **options):
  205. """
  206. Returns an innerproduct like representation (e.g. ``<x'|x>``) for the
  207. given state.
  208. Attempts to calculate inner product with a bra from the specified
  209. basis. Should only be passed an instance of KetBase or BraBase
  210. Parameters
  211. ==========
  212. expr : KetBase or BraBase
  213. The expression to be represented
  214. Examples
  215. ========
  216. >>> from sympy.physics.quantum.represent import rep_innerproduct
  217. >>> from sympy.physics.quantum.cartesian import XOp, XKet, PxOp, PxKet
  218. >>> rep_innerproduct(XKet())
  219. DiracDelta(x - x_1)
  220. >>> rep_innerproduct(XKet(), basis=PxOp())
  221. sqrt(2)*exp(-I*px_1*x/hbar)/(2*sqrt(hbar)*sqrt(pi))
  222. >>> rep_innerproduct(PxKet(), basis=XOp())
  223. sqrt(2)*exp(I*px*x_1/hbar)/(2*sqrt(hbar)*sqrt(pi))
  224. """
  225. if not isinstance(expr, (KetBase, BraBase)):
  226. raise TypeError("expr passed is not a Bra or Ket")
  227. basis = get_basis(expr, **options)
  228. if not isinstance(basis, StateBase):
  229. raise NotImplementedError("Can't form this representation!")
  230. if "index" not in options:
  231. options["index"] = 1
  232. basis_kets = enumerate_states(basis, options["index"], 2)
  233. if isinstance(expr, BraBase):
  234. bra = expr
  235. ket = (basis_kets[1] if basis_kets[0].dual == expr else basis_kets[0])
  236. else:
  237. bra = (basis_kets[1].dual if basis_kets[0]
  238. == expr else basis_kets[0].dual)
  239. ket = expr
  240. prod = InnerProduct(bra, ket)
  241. result = prod.doit()
  242. format = options.get('format', 'sympy')
  243. return expr._format_represent(result, format)
  244. def rep_expectation(expr, **options):
  245. """
  246. Returns an ``<x'|A|x>`` type representation for the given operator.
  247. Parameters
  248. ==========
  249. expr : Operator
  250. Operator to be represented in the specified basis
  251. Examples
  252. ========
  253. >>> from sympy.physics.quantum.cartesian import XOp, PxOp, PxKet
  254. >>> from sympy.physics.quantum.represent import rep_expectation
  255. >>> rep_expectation(XOp())
  256. x_1*DiracDelta(x_1 - x_2)
  257. >>> rep_expectation(XOp(), basis=PxOp())
  258. <px_2|*X*|px_1>
  259. >>> rep_expectation(XOp(), basis=PxKet())
  260. <px_2|*X*|px_1>
  261. """
  262. if "index" not in options:
  263. options["index"] = 1
  264. if not isinstance(expr, Operator):
  265. raise TypeError("The passed expression is not an operator")
  266. basis_state = get_basis(expr, **options)
  267. if basis_state is None or not isinstance(basis_state, StateBase):
  268. raise NotImplementedError("Could not get basis kets for this operator")
  269. basis_kets = enumerate_states(basis_state, options["index"], 2)
  270. bra = basis_kets[1].dual
  271. ket = basis_kets[0]
  272. return qapply(bra*expr*ket)
  273. def integrate_result(orig_expr, result, **options):
  274. """
  275. Returns the result of integrating over any unities ``(|x><x|)`` in
  276. the given expression. Intended for integrating over the result of
  277. representations in continuous bases.
  278. This function integrates over any unities that may have been
  279. inserted into the quantum expression and returns the result.
  280. It uses the interval of the Hilbert space of the basis state
  281. passed to it in order to figure out the limits of integration.
  282. The unities option must be
  283. specified for this to work.
  284. Note: This is mostly used internally by represent(). Examples are
  285. given merely to show the use cases.
  286. Parameters
  287. ==========
  288. orig_expr : quantum expression
  289. The original expression which was to be represented
  290. result: Expr
  291. The resulting representation that we wish to integrate over
  292. Examples
  293. ========
  294. >>> from sympy import symbols, DiracDelta
  295. >>> from sympy.physics.quantum.represent import integrate_result
  296. >>> from sympy.physics.quantum.cartesian import XOp, XKet
  297. >>> x_ket = XKet()
  298. >>> X_op = XOp()
  299. >>> x, x_1, x_2 = symbols('x, x_1, x_2')
  300. >>> integrate_result(X_op*x_ket, x*DiracDelta(x-x_1)*DiracDelta(x_1-x_2))
  301. x*DiracDelta(x - x_1)*DiracDelta(x_1 - x_2)
  302. >>> integrate_result(X_op*x_ket, x*DiracDelta(x-x_1)*DiracDelta(x_1-x_2),
  303. ... unities=[1])
  304. x*DiracDelta(x - x_2)
  305. """
  306. if not isinstance(result, Expr):
  307. return result
  308. options['replace_none'] = True
  309. if "basis" not in options:
  310. arg = orig_expr.args[-1]
  311. options["basis"] = get_basis(arg, **options)
  312. elif not isinstance(options["basis"], StateBase):
  313. options["basis"] = get_basis(orig_expr, **options)
  314. basis = options.pop("basis", None)
  315. if basis is None:
  316. return result
  317. unities = options.pop("unities", [])
  318. if len(unities) == 0:
  319. return result
  320. kets = enumerate_states(basis, unities)
  321. coords = [k.label[0] for k in kets]
  322. for coord in coords:
  323. if coord in result.free_symbols:
  324. #TODO: Add support for sets of operators
  325. basis_op = state_to_operators(basis)
  326. start = basis_op.hilbert_space.interval.start
  327. end = basis_op.hilbert_space.interval.end
  328. result = integrate(result, (coord, start, end))
  329. return result
  330. def get_basis(expr, *, basis=None, replace_none=True, **options):
  331. """
  332. Returns a basis state instance corresponding to the basis specified in
  333. options=s. If no basis is specified, the function tries to form a default
  334. basis state of the given expression.
  335. There are three behaviors:
  336. 1. The basis specified in options is already an instance of StateBase. If
  337. this is the case, it is simply returned. If the class is specified but
  338. not an instance, a default instance is returned.
  339. 2. The basis specified is an operator or set of operators. If this
  340. is the case, the operator_to_state mapping method is used.
  341. 3. No basis is specified. If expr is a state, then a default instance of
  342. its class is returned. If expr is an operator, then it is mapped to the
  343. corresponding state. If it is neither, then we cannot obtain the basis
  344. state.
  345. If the basis cannot be mapped, then it is not changed.
  346. This will be called from within represent, and represent will
  347. only pass QExpr's.
  348. TODO (?): Support for Muls and other types of expressions?
  349. Parameters
  350. ==========
  351. expr : Operator or StateBase
  352. Expression whose basis is sought
  353. Examples
  354. ========
  355. >>> from sympy.physics.quantum.represent import get_basis
  356. >>> from sympy.physics.quantum.cartesian import XOp, XKet, PxOp, PxKet
  357. >>> x = XKet()
  358. >>> X = XOp()
  359. >>> get_basis(x)
  360. |x>
  361. >>> get_basis(X)
  362. |x>
  363. >>> get_basis(x, basis=PxOp())
  364. |px>
  365. >>> get_basis(x, basis=PxKet)
  366. |px>
  367. """
  368. if basis is None and not replace_none:
  369. return None
  370. if basis is None:
  371. if isinstance(expr, KetBase):
  372. return _make_default(expr.__class__)
  373. elif isinstance(expr, BraBase):
  374. return _make_default(expr.dual_class())
  375. elif isinstance(expr, Operator):
  376. state_inst = operators_to_state(expr)
  377. return (state_inst if state_inst is not None else None)
  378. else:
  379. return None
  380. elif (isinstance(basis, Operator) or
  381. (not isinstance(basis, StateBase) and issubclass(basis, Operator))):
  382. state = operators_to_state(basis)
  383. if state is None:
  384. return None
  385. elif isinstance(state, StateBase):
  386. return state
  387. else:
  388. return _make_default(state)
  389. elif isinstance(basis, StateBase):
  390. return basis
  391. elif issubclass(basis, StateBase):
  392. return _make_default(basis)
  393. else:
  394. return None
  395. def _make_default(expr):
  396. # XXX: Catching TypeError like this is a bad way of distinguishing
  397. # instances from classes. The logic using this function should be
  398. # rewritten somehow.
  399. try:
  400. expr = expr()
  401. except TypeError:
  402. return expr
  403. return expr
  404. def enumerate_states(*args, **options):
  405. """
  406. Returns instances of the given state with dummy indices appended
  407. Operates in two different modes:
  408. 1. Two arguments are passed to it. The first is the base state which is to
  409. be indexed, and the second argument is a list of indices to append.
  410. 2. Three arguments are passed. The first is again the base state to be
  411. indexed. The second is the start index for counting. The final argument
  412. is the number of kets you wish to receive.
  413. Tries to call state._enumerate_state. If this fails, returns an empty list
  414. Parameters
  415. ==========
  416. args : list
  417. See list of operation modes above for explanation
  418. Examples
  419. ========
  420. >>> from sympy.physics.quantum.cartesian import XBra, XKet
  421. >>> from sympy.physics.quantum.represent import enumerate_states
  422. >>> test = XKet('foo')
  423. >>> enumerate_states(test, 1, 3)
  424. [|foo_1>, |foo_2>, |foo_3>]
  425. >>> test2 = XBra('bar')
  426. >>> enumerate_states(test2, [4, 5, 10])
  427. [<bar_4|, <bar_5|, <bar_10|]
  428. """
  429. state = args[0]
  430. if len(args) not in (2, 3):
  431. raise NotImplementedError("Wrong number of arguments!")
  432. if not isinstance(state, StateBase):
  433. raise TypeError("First argument is not a state!")
  434. if len(args) == 3:
  435. num_states = args[2]
  436. options['start_index'] = args[1]
  437. else:
  438. num_states = len(args[1])
  439. options['index_list'] = args[1]
  440. try:
  441. ret = state._enumerate_state(num_states, **options)
  442. except NotImplementedError:
  443. ret = []
  444. return ret