operator.py 19 KB

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