represent.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  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 format == 'numpy':
  116. import numpy as np
  117. if isinstance(expr, QExpr) and not isinstance(expr, OuterProduct):
  118. options['replace_none'] = False
  119. temp_basis = get_basis(expr, **options)
  120. if temp_basis is not None:
  121. options['basis'] = temp_basis
  122. try:
  123. return expr._represent(**options)
  124. except NotImplementedError as strerr:
  125. #If no _represent_FOO method exists, map to the
  126. #appropriate basis state and try
  127. #the other methods of representation
  128. options['replace_none'] = True
  129. if isinstance(expr, (KetBase, BraBase)):
  130. try:
  131. return rep_innerproduct(expr, **options)
  132. except NotImplementedError:
  133. raise NotImplementedError(strerr)
  134. elif isinstance(expr, Operator):
  135. try:
  136. return rep_expectation(expr, **options)
  137. except NotImplementedError:
  138. raise NotImplementedError(strerr)
  139. else:
  140. raise NotImplementedError(strerr)
  141. elif isinstance(expr, Add):
  142. result = represent(expr.args[0], **options)
  143. for args in expr.args[1:]:
  144. # scipy.sparse doesn't support += so we use plain = here.
  145. result = result + represent(args, **options)
  146. return result
  147. elif isinstance(expr, Pow):
  148. base, exp = expr.as_base_exp()
  149. if format in ('numpy', 'scipy.sparse'):
  150. exp = _sympy_to_scalar(exp)
  151. base = represent(base, **options)
  152. # scipy.sparse doesn't support negative exponents
  153. # and warns when inverting a matrix in csr format.
  154. if format == 'scipy.sparse' and exp < 0:
  155. from scipy.sparse.linalg import inv
  156. exp = - exp
  157. base = inv(base.tocsc()).tocsr()
  158. if format == 'numpy':
  159. return np.linalg.matrix_power(base, exp)
  160. return base ** exp
  161. elif isinstance(expr, TensorProduct):
  162. new_args = [represent(arg, **options) for arg in expr.args]
  163. return TensorProduct(*new_args)
  164. elif isinstance(expr, Dagger):
  165. return Dagger(represent(expr.args[0], **options))
  166. elif isinstance(expr, Commutator):
  167. A = expr.args[0]
  168. B = expr.args[1]
  169. return represent(Mul(A, B) - Mul(B, A), **options)
  170. elif isinstance(expr, AntiCommutator):
  171. A = expr.args[0]
  172. B = expr.args[1]
  173. return represent(Mul(A, B) + Mul(B, A), **options)
  174. elif isinstance(expr, InnerProduct):
  175. return represent(Mul(expr.bra, expr.ket), **options)
  176. elif not isinstance(expr, (Mul, OuterProduct)):
  177. # For numpy and scipy.sparse, we can only handle numerical prefactors.
  178. if format in ('numpy', 'scipy.sparse'):
  179. return _sympy_to_scalar(expr)
  180. return expr
  181. if not isinstance(expr, (Mul, OuterProduct)):
  182. raise TypeError('Mul expected, got: %r' % expr)
  183. if "index" in options:
  184. options["index"] += 1
  185. else:
  186. options["index"] = 1
  187. if "unities" not in options:
  188. options["unities"] = []
  189. result = represent(expr.args[-1], **options)
  190. last_arg = expr.args[-1]
  191. for arg in reversed(expr.args[:-1]):
  192. if isinstance(last_arg, Operator):
  193. options["index"] += 1
  194. options["unities"].append(options["index"])
  195. elif isinstance(last_arg, BraBase) and isinstance(arg, KetBase):
  196. options["index"] += 1
  197. elif isinstance(last_arg, KetBase) and isinstance(arg, Operator):
  198. options["unities"].append(options["index"])
  199. elif isinstance(last_arg, KetBase) and isinstance(arg, BraBase):
  200. options["unities"].append(options["index"])
  201. next_arg = represent(arg, **options)
  202. if format == 'numpy' and isinstance(next_arg, np.ndarray):
  203. # Must use np.matmult to "matrix multiply" two np.ndarray
  204. result = np.matmul(next_arg, result)
  205. else:
  206. result = next_arg*result
  207. last_arg = arg
  208. # All three matrix formats create 1 by 1 matrices when inner products of
  209. # vectors are taken. In these cases, we simply return a scalar.
  210. result = flatten_scalar(result)
  211. result = integrate_result(expr, result, **options)
  212. return result
  213. def rep_innerproduct(expr, **options):
  214. """
  215. Returns an innerproduct like representation (e.g. ``<x'|x>``) for the
  216. given state.
  217. Attempts to calculate inner product with a bra from the specified
  218. basis. Should only be passed an instance of KetBase or BraBase
  219. Parameters
  220. ==========
  221. expr : KetBase or BraBase
  222. The expression to be represented
  223. Examples
  224. ========
  225. >>> from sympy.physics.quantum.represent import rep_innerproduct
  226. >>> from sympy.physics.quantum.cartesian import XOp, XKet, PxOp, PxKet
  227. >>> rep_innerproduct(XKet())
  228. DiracDelta(x - x_1)
  229. >>> rep_innerproduct(XKet(), basis=PxOp())
  230. sqrt(2)*exp(-I*px_1*x/hbar)/(2*sqrt(hbar)*sqrt(pi))
  231. >>> rep_innerproduct(PxKet(), basis=XOp())
  232. sqrt(2)*exp(I*px*x_1/hbar)/(2*sqrt(hbar)*sqrt(pi))
  233. """
  234. if not isinstance(expr, (KetBase, BraBase)):
  235. raise TypeError("expr passed is not a Bra or Ket")
  236. basis = get_basis(expr, **options)
  237. if not isinstance(basis, StateBase):
  238. raise NotImplementedError("Can't form this representation!")
  239. if "index" not in options:
  240. options["index"] = 1
  241. basis_kets = enumerate_states(basis, options["index"], 2)
  242. if isinstance(expr, BraBase):
  243. bra = expr
  244. ket = (basis_kets[1] if basis_kets[0].dual == expr else basis_kets[0])
  245. else:
  246. bra = (basis_kets[1].dual if basis_kets[0]
  247. == expr else basis_kets[0].dual)
  248. ket = expr
  249. prod = InnerProduct(bra, ket)
  250. result = prod.doit()
  251. format = options.get('format', 'sympy')
  252. return expr._format_represent(result, format)
  253. def rep_expectation(expr, **options):
  254. """
  255. Returns an ``<x'|A|x>`` type representation for the given operator.
  256. Parameters
  257. ==========
  258. expr : Operator
  259. Operator to be represented in the specified basis
  260. Examples
  261. ========
  262. >>> from sympy.physics.quantum.cartesian import XOp, PxOp, PxKet
  263. >>> from sympy.physics.quantum.represent import rep_expectation
  264. >>> rep_expectation(XOp())
  265. x_1*DiracDelta(x_1 - x_2)
  266. >>> rep_expectation(XOp(), basis=PxOp())
  267. <px_2|*X*|px_1>
  268. >>> rep_expectation(XOp(), basis=PxKet())
  269. <px_2|*X*|px_1>
  270. """
  271. if "index" not in options:
  272. options["index"] = 1
  273. if not isinstance(expr, Operator):
  274. raise TypeError("The passed expression is not an operator")
  275. basis_state = get_basis(expr, **options)
  276. if basis_state is None or not isinstance(basis_state, StateBase):
  277. raise NotImplementedError("Could not get basis kets for this operator")
  278. basis_kets = enumerate_states(basis_state, options["index"], 2)
  279. bra = basis_kets[1].dual
  280. ket = basis_kets[0]
  281. return qapply(bra*expr*ket)
  282. def integrate_result(orig_expr, result, **options):
  283. """
  284. Returns the result of integrating over any unities ``(|x><x|)`` in
  285. the given expression. Intended for integrating over the result of
  286. representations in continuous bases.
  287. This function integrates over any unities that may have been
  288. inserted into the quantum expression and returns the result.
  289. It uses the interval of the Hilbert space of the basis state
  290. passed to it in order to figure out the limits of integration.
  291. The unities option must be
  292. specified for this to work.
  293. Note: This is mostly used internally by represent(). Examples are
  294. given merely to show the use cases.
  295. Parameters
  296. ==========
  297. orig_expr : quantum expression
  298. The original expression which was to be represented
  299. result: Expr
  300. The resulting representation that we wish to integrate over
  301. Examples
  302. ========
  303. >>> from sympy import symbols, DiracDelta
  304. >>> from sympy.physics.quantum.represent import integrate_result
  305. >>> from sympy.physics.quantum.cartesian import XOp, XKet
  306. >>> x_ket = XKet()
  307. >>> X_op = XOp()
  308. >>> x, x_1, x_2 = symbols('x, x_1, x_2')
  309. >>> integrate_result(X_op*x_ket, x*DiracDelta(x-x_1)*DiracDelta(x_1-x_2))
  310. x*DiracDelta(x - x_1)*DiracDelta(x_1 - x_2)
  311. >>> integrate_result(X_op*x_ket, x*DiracDelta(x-x_1)*DiracDelta(x_1-x_2),
  312. ... unities=[1])
  313. x*DiracDelta(x - x_2)
  314. """
  315. if not isinstance(result, Expr):
  316. return result
  317. options['replace_none'] = True
  318. if "basis" not in options:
  319. arg = orig_expr.args[-1]
  320. options["basis"] = get_basis(arg, **options)
  321. elif not isinstance(options["basis"], StateBase):
  322. options["basis"] = get_basis(orig_expr, **options)
  323. basis = options.pop("basis", None)
  324. if basis is None:
  325. return result
  326. unities = options.pop("unities", [])
  327. if len(unities) == 0:
  328. return result
  329. kets = enumerate_states(basis, unities)
  330. coords = [k.label[0] for k in kets]
  331. for coord in coords:
  332. if coord in result.free_symbols:
  333. #TODO: Add support for sets of operators
  334. basis_op = state_to_operators(basis)
  335. start = basis_op.hilbert_space.interval.start
  336. end = basis_op.hilbert_space.interval.end
  337. result = integrate(result, (coord, start, end))
  338. return result
  339. def get_basis(expr, *, basis=None, replace_none=True, **options):
  340. """
  341. Returns a basis state instance corresponding to the basis specified in
  342. options=s. If no basis is specified, the function tries to form a default
  343. basis state of the given expression.
  344. There are three behaviors:
  345. 1. The basis specified in options is already an instance of StateBase. If
  346. this is the case, it is simply returned. If the class is specified but
  347. not an instance, a default instance is returned.
  348. 2. The basis specified is an operator or set of operators. If this
  349. is the case, the operator_to_state mapping method is used.
  350. 3. No basis is specified. If expr is a state, then a default instance of
  351. its class is returned. If expr is an operator, then it is mapped to the
  352. corresponding state. If it is neither, then we cannot obtain the basis
  353. state.
  354. If the basis cannot be mapped, then it is not changed.
  355. This will be called from within represent, and represent will
  356. only pass QExpr's.
  357. TODO (?): Support for Muls and other types of expressions?
  358. Parameters
  359. ==========
  360. expr : Operator or StateBase
  361. Expression whose basis is sought
  362. Examples
  363. ========
  364. >>> from sympy.physics.quantum.represent import get_basis
  365. >>> from sympy.physics.quantum.cartesian import XOp, XKet, PxOp, PxKet
  366. >>> x = XKet()
  367. >>> X = XOp()
  368. >>> get_basis(x)
  369. |x>
  370. >>> get_basis(X)
  371. |x>
  372. >>> get_basis(x, basis=PxOp())
  373. |px>
  374. >>> get_basis(x, basis=PxKet)
  375. |px>
  376. """
  377. if basis is None and not replace_none:
  378. return None
  379. if basis is None:
  380. if isinstance(expr, KetBase):
  381. return _make_default(expr.__class__)
  382. elif isinstance(expr, BraBase):
  383. return _make_default(expr.dual_class())
  384. elif isinstance(expr, Operator):
  385. state_inst = operators_to_state(expr)
  386. return (state_inst if state_inst is not None else None)
  387. else:
  388. return None
  389. elif (isinstance(basis, Operator) or
  390. (not isinstance(basis, StateBase) and issubclass(basis, Operator))):
  391. state = operators_to_state(basis)
  392. if state is None:
  393. return None
  394. elif isinstance(state, StateBase):
  395. return state
  396. else:
  397. return _make_default(state)
  398. elif isinstance(basis, StateBase):
  399. return basis
  400. elif issubclass(basis, StateBase):
  401. return _make_default(basis)
  402. else:
  403. return None
  404. def _make_default(expr):
  405. # XXX: Catching TypeError like this is a bad way of distinguishing
  406. # instances from classes. The logic using this function should be
  407. # rewritten somehow.
  408. try:
  409. expr = expr()
  410. except TypeError:
  411. return expr
  412. return expr
  413. def enumerate_states(*args, **options):
  414. """
  415. Returns instances of the given state with dummy indices appended
  416. Operates in two different modes:
  417. 1. Two arguments are passed to it. The first is the base state which is to
  418. be indexed, and the second argument is a list of indices to append.
  419. 2. Three arguments are passed. The first is again the base state to be
  420. indexed. The second is the start index for counting. The final argument
  421. is the number of kets you wish to receive.
  422. Tries to call state._enumerate_state. If this fails, returns an empty list
  423. Parameters
  424. ==========
  425. args : list
  426. See list of operation modes above for explanation
  427. Examples
  428. ========
  429. >>> from sympy.physics.quantum.cartesian import XBra, XKet
  430. >>> from sympy.physics.quantum.represent import enumerate_states
  431. >>> test = XKet('foo')
  432. >>> enumerate_states(test, 1, 3)
  433. [|foo_1>, |foo_2>, |foo_3>]
  434. >>> test2 = XBra('bar')
  435. >>> enumerate_states(test2, [4, 5, 10])
  436. [<bar_4|, <bar_5|, <bar_10|]
  437. """
  438. state = args[0]
  439. if len(args) not in (2, 3):
  440. raise NotImplementedError("Wrong number of arguments!")
  441. if not isinstance(state, StateBase):
  442. raise TypeError("First argument is not a state!")
  443. if len(args) == 3:
  444. num_states = args[2]
  445. options['start_index'] = args[1]
  446. else:
  447. num_states = len(args[1])
  448. options['index_list'] = args[1]
  449. try:
  450. ret = state._enumerate_state(num_states, **options)
  451. except NotImplementedError:
  452. ret = []
  453. return ret