operator.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653
  1. """Quantum mechanical operators.
  2. TODO:
  3. * Fix early 0 in apply_operators.
  4. * Debug and test apply_operators.
  5. * Get cse working with classes in this file.
  6. * Doctests and documentation of special methods for InnerProduct, Commutator,
  7. AntiCommutator, represent, apply_operators.
  8. """
  9. from typing import Optional
  10. from sympy.core.add import Add
  11. from sympy.core.expr import Expr
  12. from sympy.core.function import (Derivative, expand)
  13. from sympy.core.mul import Mul
  14. from sympy.core.numbers import oo
  15. from sympy.core.singleton import S
  16. from sympy.printing.pretty.stringpict import prettyForm
  17. from sympy.physics.quantum.dagger import Dagger
  18. from sympy.physics.quantum.kind import OperatorKind
  19. from sympy.physics.quantum.qexpr import QExpr, dispatch_method
  20. from sympy.matrices import eye
  21. from sympy.utilities.exceptions import sympy_deprecation_warning
  22. __all__ = [
  23. 'Operator',
  24. 'HermitianOperator',
  25. 'UnitaryOperator',
  26. 'IdentityOperator',
  27. 'OuterProduct',
  28. 'DifferentialOperator'
  29. ]
  30. #-----------------------------------------------------------------------------
  31. # Operators and outer products
  32. #-----------------------------------------------------------------------------
  33. class Operator(QExpr):
  34. """Base class for non-commuting quantum operators.
  35. An operator maps between quantum states [1]_. In quantum mechanics,
  36. observables (including, but not limited to, measured physical values) are
  37. represented as Hermitian operators [2]_.
  38. Parameters
  39. ==========
  40. args : tuple
  41. The list of numbers or parameters that uniquely specify the
  42. operator. For time-dependent operators, this will include the time.
  43. Examples
  44. ========
  45. Create an operator and examine its attributes::
  46. >>> from sympy.physics.quantum import Operator
  47. >>> from sympy import I
  48. >>> A = Operator('A')
  49. >>> A
  50. A
  51. >>> A.hilbert_space
  52. H
  53. >>> A.label
  54. (A,)
  55. >>> A.is_commutative
  56. False
  57. Create another operator and do some arithmetic operations::
  58. >>> B = Operator('B')
  59. >>> C = 2*A*A + I*B
  60. >>> C
  61. 2*A**2 + I*B
  62. Operators do not commute::
  63. >>> A.is_commutative
  64. False
  65. >>> B.is_commutative
  66. False
  67. >>> A*B == B*A
  68. False
  69. Polymonials of operators respect the commutation properties::
  70. >>> e = (A+B)**3
  71. >>> e.expand()
  72. A*B*A + A*B**2 + A**2*B + A**3 + B*A*B + B*A**2 + B**2*A + B**3
  73. Operator inverses are handle symbolically::
  74. >>> A.inv()
  75. A**(-1)
  76. >>> A*A.inv()
  77. 1
  78. References
  79. ==========
  80. .. [1] https://en.wikipedia.org/wiki/Operator_%28physics%29
  81. .. [2] https://en.wikipedia.org/wiki/Observable
  82. """
  83. is_hermitian: Optional[bool] = None
  84. is_unitary: Optional[bool] = None
  85. @classmethod
  86. def default_args(self):
  87. return ("O",)
  88. kind = OperatorKind
  89. #-------------------------------------------------------------------------
  90. # Printing
  91. #-------------------------------------------------------------------------
  92. _label_separator = ','
  93. def _print_operator_name(self, printer, *args):
  94. return self.__class__.__name__
  95. _print_operator_name_latex = _print_operator_name
  96. def _print_operator_name_pretty(self, printer, *args):
  97. return prettyForm(self.__class__.__name__)
  98. def _print_contents(self, printer, *args):
  99. if len(self.label) == 1:
  100. return self._print_label(printer, *args)
  101. else:
  102. return '%s(%s)' % (
  103. self._print_operator_name(printer, *args),
  104. self._print_label(printer, *args)
  105. )
  106. def _print_contents_pretty(self, printer, *args):
  107. if len(self.label) == 1:
  108. return self._print_label_pretty(printer, *args)
  109. else:
  110. pform = self._print_operator_name_pretty(printer, *args)
  111. label_pform = self._print_label_pretty(printer, *args)
  112. label_pform = prettyForm(
  113. *label_pform.parens(left='(', right=')')
  114. )
  115. pform = prettyForm(*pform.right(label_pform))
  116. return pform
  117. def _print_contents_latex(self, printer, *args):
  118. if len(self.label) == 1:
  119. return self._print_label_latex(printer, *args)
  120. else:
  121. return r'%s\left(%s\right)' % (
  122. self._print_operator_name_latex(printer, *args),
  123. self._print_label_latex(printer, *args)
  124. )
  125. #-------------------------------------------------------------------------
  126. # _eval_* methods
  127. #-------------------------------------------------------------------------
  128. def _eval_commutator(self, other, **options):
  129. """Evaluate [self, other] if known, return None if not known."""
  130. return dispatch_method(self, '_eval_commutator', other, **options)
  131. def _eval_anticommutator(self, other, **options):
  132. """Evaluate [self, other] if known."""
  133. return dispatch_method(self, '_eval_anticommutator', other, **options)
  134. #-------------------------------------------------------------------------
  135. # Operator application
  136. #-------------------------------------------------------------------------
  137. def _apply_operator(self, ket, **options):
  138. return dispatch_method(self, '_apply_operator', ket, **options)
  139. def _apply_from_right_to(self, bra, **options):
  140. return None
  141. def matrix_element(self, *args):
  142. raise NotImplementedError('matrix_elements is not defined')
  143. def inverse(self):
  144. return self._eval_inverse()
  145. inv = inverse
  146. def _eval_inverse(self):
  147. return self**(-1)
  148. class HermitianOperator(Operator):
  149. """A Hermitian operator that satisfies H == Dagger(H).
  150. Parameters
  151. ==========
  152. args : tuple
  153. The list of numbers or parameters that uniquely specify the
  154. operator. For time-dependent operators, this will include the time.
  155. Examples
  156. ========
  157. >>> from sympy.physics.quantum import Dagger, HermitianOperator
  158. >>> H = HermitianOperator('H')
  159. >>> Dagger(H)
  160. H
  161. """
  162. is_hermitian = True
  163. def _eval_inverse(self):
  164. if isinstance(self, UnitaryOperator):
  165. return self
  166. else:
  167. return Operator._eval_inverse(self)
  168. def _eval_power(self, exp):
  169. if isinstance(self, UnitaryOperator):
  170. # so all eigenvalues of self are 1 or -1
  171. if exp.is_even:
  172. from sympy.core.singleton import S
  173. return S.One # is identity, see Issue 24153.
  174. elif exp.is_odd:
  175. return self
  176. # No simplification in all other cases
  177. return Operator._eval_power(self, exp)
  178. class UnitaryOperator(Operator):
  179. """A unitary operator that satisfies U*Dagger(U) == 1.
  180. Parameters
  181. ==========
  182. args : tuple
  183. The list of numbers or parameters that uniquely specify the
  184. operator. For time-dependent operators, this will include the time.
  185. Examples
  186. ========
  187. >>> from sympy.physics.quantum import Dagger, UnitaryOperator
  188. >>> U = UnitaryOperator('U')
  189. >>> U*Dagger(U)
  190. 1
  191. """
  192. is_unitary = True
  193. def _eval_adjoint(self):
  194. return self._eval_inverse()
  195. class IdentityOperator(Operator):
  196. """An identity operator I that satisfies op * I == I * op == op for any
  197. operator op.
  198. .. deprecated:: 1.14.
  199. Use the scalar S.One instead as the multiplicative identity for
  200. operators and states.
  201. Parameters
  202. ==========
  203. N : Integer
  204. Optional parameter that specifies the dimension of the Hilbert space
  205. of operator. This is used when generating a matrix representation.
  206. Examples
  207. ========
  208. >>> from sympy.physics.quantum import IdentityOperator
  209. >>> IdentityOperator() # doctest: +SKIP
  210. I
  211. """
  212. is_hermitian = True
  213. is_unitary = True
  214. @property
  215. def dimension(self):
  216. return self.N
  217. @classmethod
  218. def default_args(self):
  219. return (oo,)
  220. def __init__(self, *args, **hints):
  221. sympy_deprecation_warning(
  222. """
  223. IdentityOperator has been deprecated. In the future, please use
  224. S.One as the identity for quantum operators and states.
  225. """,
  226. deprecated_since_version="1.14",
  227. active_deprecations_target='deprecated-operator-identity',
  228. )
  229. if not len(args) in (0, 1):
  230. raise ValueError('0 or 1 parameters expected, got %s' % args)
  231. self.N = args[0] if (len(args) == 1 and args[0]) else oo
  232. def _eval_commutator(self, other, **hints):
  233. return S.Zero
  234. def _eval_anticommutator(self, other, **hints):
  235. return 2 * other
  236. def _eval_inverse(self):
  237. return self
  238. def _eval_adjoint(self):
  239. return self
  240. def _apply_operator(self, ket, **options):
  241. return ket
  242. def _apply_from_right_to(self, bra, **options):
  243. return bra
  244. def _eval_power(self, exp):
  245. return self
  246. def _print_contents(self, printer, *args):
  247. return 'I'
  248. def _print_contents_pretty(self, printer, *args):
  249. return prettyForm('I')
  250. def _print_contents_latex(self, printer, *args):
  251. return r'{\mathcal{I}}'
  252. def _represent_default_basis(self, **options):
  253. if not self.N or self.N == oo:
  254. raise NotImplementedError('Cannot represent infinite dimensional' +
  255. ' identity operator as a matrix')
  256. format = options.get('format', 'sympy')
  257. if format != 'sympy':
  258. raise NotImplementedError('Representation in format ' +
  259. '%s not implemented.' % format)
  260. return eye(self.N)
  261. class OuterProduct(Operator):
  262. """An unevaluated outer product between a ket and bra.
  263. This constructs an outer product between any subclass of ``KetBase`` and
  264. ``BraBase`` as ``|a><b|``. An ``OuterProduct`` inherits from Operator as they act as
  265. operators in quantum expressions. For reference see [1]_.
  266. Parameters
  267. ==========
  268. ket : KetBase
  269. The ket on the left side of the outer product.
  270. bar : BraBase
  271. The bra on the right side of the outer product.
  272. Examples
  273. ========
  274. Create a simple outer product by hand and take its dagger::
  275. >>> from sympy.physics.quantum import Ket, Bra, OuterProduct, Dagger
  276. >>> k = Ket('k')
  277. >>> b = Bra('b')
  278. >>> op = OuterProduct(k, b)
  279. >>> op
  280. |k><b|
  281. >>> op.hilbert_space
  282. H
  283. >>> op.ket
  284. |k>
  285. >>> op.bra
  286. <b|
  287. >>> Dagger(op)
  288. |b><k|
  289. In quantum expressions, outer products will be automatically
  290. identified and created::
  291. >>> k*b
  292. |k><b|
  293. However, the creation of inner products always has higher priority than that of
  294. outer products:
  295. >>> b*k*b
  296. <b|k>*<b|
  297. References
  298. ==========
  299. .. [1] https://en.wikipedia.org/wiki/Outer_product
  300. """
  301. is_commutative = False
  302. def __new__(cls, *args, **old_assumptions):
  303. from sympy.physics.quantum.state import KetBase, BraBase
  304. if len(args) != 2:
  305. raise ValueError('2 parameters expected, got %d' % len(args))
  306. ket_expr = expand(args[0])
  307. bra_expr = expand(args[1])
  308. if (isinstance(ket_expr, (KetBase, Mul)) and
  309. isinstance(bra_expr, (BraBase, Mul))):
  310. ket_c, kets = ket_expr.args_cnc()
  311. bra_c, bras = bra_expr.args_cnc()
  312. if len(kets) != 1 or not isinstance(kets[0], KetBase):
  313. raise TypeError('KetBase subclass expected'
  314. ', got: %r' % Mul(*kets))
  315. if len(bras) != 1 or not isinstance(bras[0], BraBase):
  316. raise TypeError('BraBase subclass expected'
  317. ', got: %r' % Mul(*bras))
  318. if not kets[0].dual_class() == bras[0].__class__:
  319. raise TypeError(
  320. 'ket and bra are not dual classes: %r, %r' %
  321. (kets[0].__class__, bras[0].__class__)
  322. )
  323. # TODO: make sure the hilbert spaces of the bra and ket are
  324. # compatible
  325. obj = Expr.__new__(cls, *(kets[0], bras[0]), **old_assumptions)
  326. obj.hilbert_space = kets[0].hilbert_space
  327. return Mul(*(ket_c + bra_c)) * obj
  328. op_terms = []
  329. if isinstance(ket_expr, Add) and isinstance(bra_expr, Add):
  330. for ket_term in ket_expr.args:
  331. for bra_term in bra_expr.args:
  332. op_terms.append(OuterProduct(ket_term, bra_term,
  333. **old_assumptions))
  334. elif isinstance(ket_expr, Add):
  335. for ket_term in ket_expr.args:
  336. op_terms.append(OuterProduct(ket_term, bra_expr,
  337. **old_assumptions))
  338. elif isinstance(bra_expr, Add):
  339. for bra_term in bra_expr.args:
  340. op_terms.append(OuterProduct(ket_expr, bra_term,
  341. **old_assumptions))
  342. else:
  343. raise TypeError(
  344. 'Expected ket and bra expression, got: %r, %r' %
  345. (ket_expr, bra_expr)
  346. )
  347. return Add(*op_terms)
  348. @property
  349. def ket(self):
  350. """Return the ket on the left side of the outer product."""
  351. return self.args[0]
  352. @property
  353. def bra(self):
  354. """Return the bra on the right side of the outer product."""
  355. return self.args[1]
  356. def _eval_adjoint(self):
  357. return OuterProduct(Dagger(self.bra), Dagger(self.ket))
  358. def _sympystr(self, printer, *args):
  359. return printer._print(self.ket) + printer._print(self.bra)
  360. def _sympyrepr(self, printer, *args):
  361. return '%s(%s,%s)' % (self.__class__.__name__,
  362. printer._print(self.ket, *args), printer._print(self.bra, *args))
  363. def _pretty(self, printer, *args):
  364. pform = self.ket._pretty(printer, *args)
  365. return prettyForm(*pform.right(self.bra._pretty(printer, *args)))
  366. def _latex(self, printer, *args):
  367. k = printer._print(self.ket, *args)
  368. b = printer._print(self.bra, *args)
  369. return k + b
  370. def _represent(self, **options):
  371. k = self.ket._represent(**options)
  372. b = self.bra._represent(**options)
  373. return k*b
  374. def _eval_trace(self, **kwargs):
  375. # TODO if operands are tensorproducts this may be will be handled
  376. # differently.
  377. return self.ket._eval_trace(self.bra, **kwargs)
  378. class DifferentialOperator(Operator):
  379. """An operator for representing the differential operator, i.e. d/dx
  380. It is initialized by passing two arguments. The first is an arbitrary
  381. expression that involves a function, such as ``Derivative(f(x), x)``. The
  382. second is the function (e.g. ``f(x)``) which we are to replace with the
  383. ``Wavefunction`` that this ``DifferentialOperator`` is applied to.
  384. Parameters
  385. ==========
  386. expr : Expr
  387. The arbitrary expression which the appropriate Wavefunction is to be
  388. substituted into
  389. func : Expr
  390. A function (e.g. f(x)) which is to be replaced with the appropriate
  391. Wavefunction when this DifferentialOperator is applied
  392. Examples
  393. ========
  394. You can define a completely arbitrary expression and specify where the
  395. Wavefunction is to be substituted
  396. >>> from sympy import Derivative, Function, Symbol
  397. >>> from sympy.physics.quantum.operator import DifferentialOperator
  398. >>> from sympy.physics.quantum.state import Wavefunction
  399. >>> from sympy.physics.quantum.qapply import qapply
  400. >>> f = Function('f')
  401. >>> x = Symbol('x')
  402. >>> d = DifferentialOperator(1/x*Derivative(f(x), x), f(x))
  403. >>> w = Wavefunction(x**2, x)
  404. >>> d.function
  405. f(x)
  406. >>> d.variables
  407. (x,)
  408. >>> qapply(d*w)
  409. Wavefunction(2, x)
  410. """
  411. @property
  412. def variables(self):
  413. """
  414. Returns the variables with which the function in the specified
  415. arbitrary expression is evaluated
  416. Examples
  417. ========
  418. >>> from sympy.physics.quantum.operator import DifferentialOperator
  419. >>> from sympy import Symbol, Function, Derivative
  420. >>> x = Symbol('x')
  421. >>> f = Function('f')
  422. >>> d = DifferentialOperator(1/x*Derivative(f(x), x), f(x))
  423. >>> d.variables
  424. (x,)
  425. >>> y = Symbol('y')
  426. >>> d = DifferentialOperator(Derivative(f(x, y), x) +
  427. ... Derivative(f(x, y), y), f(x, y))
  428. >>> d.variables
  429. (x, y)
  430. """
  431. return self.args[-1].args
  432. @property
  433. def function(self):
  434. """
  435. Returns the function which is to be replaced with the Wavefunction
  436. Examples
  437. ========
  438. >>> from sympy.physics.quantum.operator import DifferentialOperator
  439. >>> from sympy import Function, Symbol, Derivative
  440. >>> x = Symbol('x')
  441. >>> f = Function('f')
  442. >>> d = DifferentialOperator(Derivative(f(x), x), f(x))
  443. >>> d.function
  444. f(x)
  445. >>> y = Symbol('y')
  446. >>> d = DifferentialOperator(Derivative(f(x, y), x) +
  447. ... Derivative(f(x, y), y), f(x, y))
  448. >>> d.function
  449. f(x, y)
  450. """
  451. return self.args[-1]
  452. @property
  453. def expr(self):
  454. """
  455. Returns the arbitrary expression which is to have the Wavefunction
  456. substituted into it
  457. Examples
  458. ========
  459. >>> from sympy.physics.quantum.operator import DifferentialOperator
  460. >>> from sympy import Function, Symbol, Derivative
  461. >>> x = Symbol('x')
  462. >>> f = Function('f')
  463. >>> d = DifferentialOperator(Derivative(f(x), x), f(x))
  464. >>> d.expr
  465. Derivative(f(x), x)
  466. >>> y = Symbol('y')
  467. >>> d = DifferentialOperator(Derivative(f(x, y), x) +
  468. ... Derivative(f(x, y), y), f(x, y))
  469. >>> d.expr
  470. Derivative(f(x, y), x) + Derivative(f(x, y), y)
  471. """
  472. return self.args[0]
  473. @property
  474. def free_symbols(self):
  475. """
  476. Return the free symbols of the expression.
  477. """
  478. return self.expr.free_symbols
  479. def _apply_operator_Wavefunction(self, func, **options):
  480. from sympy.physics.quantum.state import Wavefunction
  481. var = self.variables
  482. wf_vars = func.args[1:]
  483. f = self.function
  484. new_expr = self.expr.subs(f, func(*var))
  485. new_expr = new_expr.doit()
  486. return Wavefunction(new_expr, *wf_vars)
  487. def _eval_derivative(self, symbol):
  488. new_expr = Derivative(self.expr, symbol)
  489. return DifferentialOperator(new_expr, self.args[-1])
  490. #-------------------------------------------------------------------------
  491. # Printing
  492. #-------------------------------------------------------------------------
  493. def _print(self, printer, *args):
  494. return '%s(%s)' % (
  495. self._print_operator_name(printer, *args),
  496. self._print_label(printer, *args)
  497. )
  498. def _print_pretty(self, printer, *args):
  499. pform = self._print_operator_name_pretty(printer, *args)
  500. label_pform = self._print_label_pretty(printer, *args)
  501. label_pform = prettyForm(
  502. *label_pform.parens(left='(', right=')')
  503. )
  504. pform = prettyForm(*pform.right(label_pform))
  505. return pform