123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650 |
- """Quantum mechanical operators.
- TODO:
- * Fix early 0 in apply_operators.
- * Debug and test apply_operators.
- * Get cse working with classes in this file.
- * Doctests and documentation of special methods for InnerProduct, Commutator,
- AntiCommutator, represent, apply_operators.
- """
- from sympy.core.add import Add
- from sympy.core.expr import Expr
- from sympy.core.function import (Derivative, expand)
- from sympy.core.mul import Mul
- from sympy.core.numbers import oo
- from sympy.core.singleton import S
- from sympy.printing.pretty.stringpict import prettyForm
- from sympy.physics.quantum.dagger import Dagger
- from sympy.physics.quantum.qexpr import QExpr, dispatch_method
- from sympy.matrices import eye
- __all__ = [
- 'Operator',
- 'HermitianOperator',
- 'UnitaryOperator',
- 'IdentityOperator',
- 'OuterProduct',
- 'DifferentialOperator'
- ]
- #-----------------------------------------------------------------------------
- # Operators and outer products
- #-----------------------------------------------------------------------------
- class Operator(QExpr):
- """Base class for non-commuting quantum operators.
- An operator maps between quantum states [1]_. In quantum mechanics,
- observables (including, but not limited to, measured physical values) are
- represented as Hermitian operators [2]_.
- Parameters
- ==========
- args : tuple
- The list of numbers or parameters that uniquely specify the
- operator. For time-dependent operators, this will include the time.
- Examples
- ========
- Create an operator and examine its attributes::
- >>> from sympy.physics.quantum import Operator
- >>> from sympy import I
- >>> A = Operator('A')
- >>> A
- A
- >>> A.hilbert_space
- H
- >>> A.label
- (A,)
- >>> A.is_commutative
- False
- Create another operator and do some arithmetic operations::
- >>> B = Operator('B')
- >>> C = 2*A*A + I*B
- >>> C
- 2*A**2 + I*B
- Operators do not commute::
- >>> A.is_commutative
- False
- >>> B.is_commutative
- False
- >>> A*B == B*A
- False
- Polymonials of operators respect the commutation properties::
- >>> e = (A+B)**3
- >>> e.expand()
- A*B*A + A*B**2 + A**2*B + A**3 + B*A*B + B*A**2 + B**2*A + B**3
- Operator inverses are handle symbolically::
- >>> A.inv()
- A**(-1)
- >>> A*A.inv()
- 1
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Operator_%28physics%29
- .. [2] https://en.wikipedia.org/wiki/Observable
- """
- @classmethod
- def default_args(self):
- return ("O",)
- #-------------------------------------------------------------------------
- # Printing
- #-------------------------------------------------------------------------
- _label_separator = ','
- def _print_operator_name(self, printer, *args):
- return self.__class__.__name__
- _print_operator_name_latex = _print_operator_name
- def _print_operator_name_pretty(self, printer, *args):
- return prettyForm(self.__class__.__name__)
- def _print_contents(self, printer, *args):
- if len(self.label) == 1:
- return self._print_label(printer, *args)
- else:
- return '%s(%s)' % (
- self._print_operator_name(printer, *args),
- self._print_label(printer, *args)
- )
- def _print_contents_pretty(self, printer, *args):
- if len(self.label) == 1:
- return self._print_label_pretty(printer, *args)
- else:
- pform = self._print_operator_name_pretty(printer, *args)
- label_pform = self._print_label_pretty(printer, *args)
- label_pform = prettyForm(
- *label_pform.parens(left='(', right=')')
- )
- pform = prettyForm(*pform.right(label_pform))
- return pform
- def _print_contents_latex(self, printer, *args):
- if len(self.label) == 1:
- return self._print_label_latex(printer, *args)
- else:
- return r'%s\left(%s\right)' % (
- self._print_operator_name_latex(printer, *args),
- self._print_label_latex(printer, *args)
- )
- #-------------------------------------------------------------------------
- # _eval_* methods
- #-------------------------------------------------------------------------
- def _eval_commutator(self, other, **options):
- """Evaluate [self, other] if known, return None if not known."""
- return dispatch_method(self, '_eval_commutator', other, **options)
- def _eval_anticommutator(self, other, **options):
- """Evaluate [self, other] if known."""
- return dispatch_method(self, '_eval_anticommutator', other, **options)
- #-------------------------------------------------------------------------
- # Operator application
- #-------------------------------------------------------------------------
- def _apply_operator(self, ket, **options):
- return dispatch_method(self, '_apply_operator', ket, **options)
- def matrix_element(self, *args):
- raise NotImplementedError('matrix_elements is not defined')
- def inverse(self):
- return self._eval_inverse()
- inv = inverse
- def _eval_inverse(self):
- return self**(-1)
- def __mul__(self, other):
- if isinstance(other, IdentityOperator):
- return self
- return Mul(self, other)
- class HermitianOperator(Operator):
- """A Hermitian operator that satisfies H == Dagger(H).
- Parameters
- ==========
- args : tuple
- The list of numbers or parameters that uniquely specify the
- operator. For time-dependent operators, this will include the time.
- Examples
- ========
- >>> from sympy.physics.quantum import Dagger, HermitianOperator
- >>> H = HermitianOperator('H')
- >>> Dagger(H)
- H
- """
- is_hermitian = True
- def _eval_inverse(self):
- if isinstance(self, UnitaryOperator):
- return self
- else:
- return Operator._eval_inverse(self)
- def _eval_power(self, exp):
- if isinstance(self, UnitaryOperator):
- # so all eigenvalues of self are 1 or -1
- if exp.is_even:
- from sympy.core.singleton import S
- return S.One # is identity, see Issue 24153.
- elif exp.is_odd:
- return self
- # No simplification in all other cases
- return Operator._eval_power(self, exp)
- class UnitaryOperator(Operator):
- """A unitary operator that satisfies U*Dagger(U) == 1.
- Parameters
- ==========
- args : tuple
- The list of numbers or parameters that uniquely specify the
- operator. For time-dependent operators, this will include the time.
- Examples
- ========
- >>> from sympy.physics.quantum import Dagger, UnitaryOperator
- >>> U = UnitaryOperator('U')
- >>> U*Dagger(U)
- 1
- """
- def _eval_adjoint(self):
- return self._eval_inverse()
- class IdentityOperator(Operator):
- """An identity operator I that satisfies op * I == I * op == op for any
- operator op.
- Parameters
- ==========
- N : Integer
- Optional parameter that specifies the dimension of the Hilbert space
- of operator. This is used when generating a matrix representation.
- Examples
- ========
- >>> from sympy.physics.quantum import IdentityOperator
- >>> IdentityOperator()
- I
- """
- @property
- def dimension(self):
- return self.N
- @classmethod
- def default_args(self):
- return (oo,)
- def __init__(self, *args, **hints):
- if not len(args) in (0, 1):
- raise ValueError('0 or 1 parameters expected, got %s' % args)
- self.N = args[0] if (len(args) == 1 and args[0]) else oo
- def _eval_commutator(self, other, **hints):
- return S.Zero
- def _eval_anticommutator(self, other, **hints):
- return 2 * other
- def _eval_inverse(self):
- return self
- def _eval_adjoint(self):
- return self
- def _apply_operator(self, ket, **options):
- return ket
- def _apply_from_right_to(self, bra, **options):
- return bra
- def _eval_power(self, exp):
- return self
- def _print_contents(self, printer, *args):
- return 'I'
- def _print_contents_pretty(self, printer, *args):
- return prettyForm('I')
- def _print_contents_latex(self, printer, *args):
- return r'{\mathcal{I}}'
- def __mul__(self, other):
- if isinstance(other, (Operator, Dagger)):
- return other
- return Mul(self, other)
- def _represent_default_basis(self, **options):
- if not self.N or self.N == oo:
- raise NotImplementedError('Cannot represent infinite dimensional' +
- ' identity operator as a matrix')
- format = options.get('format', 'sympy')
- if format != 'sympy':
- raise NotImplementedError('Representation in format ' +
- '%s not implemented.' % format)
- return eye(self.N)
- class OuterProduct(Operator):
- """An unevaluated outer product between a ket and bra.
- This constructs an outer product between any subclass of ``KetBase`` and
- ``BraBase`` as ``|a><b|``. An ``OuterProduct`` inherits from Operator as they act as
- operators in quantum expressions. For reference see [1]_.
- Parameters
- ==========
- ket : KetBase
- The ket on the left side of the outer product.
- bar : BraBase
- The bra on the right side of the outer product.
- Examples
- ========
- Create a simple outer product by hand and take its dagger::
- >>> from sympy.physics.quantum import Ket, Bra, OuterProduct, Dagger
- >>> from sympy.physics.quantum import Operator
- >>> k = Ket('k')
- >>> b = Bra('b')
- >>> op = OuterProduct(k, b)
- >>> op
- |k><b|
- >>> op.hilbert_space
- H
- >>> op.ket
- |k>
- >>> op.bra
- <b|
- >>> Dagger(op)
- |b><k|
- In simple products of kets and bras outer products will be automatically
- identified and created::
- >>> k*b
- |k><b|
- But in more complex expressions, outer products are not automatically
- created::
- >>> A = Operator('A')
- >>> A*k*b
- A*|k>*<b|
- A user can force the creation of an outer product in a complex expression
- by using parentheses to group the ket and bra::
- >>> A*(k*b)
- A*|k><b|
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Outer_product
- """
- is_commutative = False
- def __new__(cls, *args, **old_assumptions):
- from sympy.physics.quantum.state import KetBase, BraBase
- if len(args) != 2:
- raise ValueError('2 parameters expected, got %d' % len(args))
- ket_expr = expand(args[0])
- bra_expr = expand(args[1])
- if (isinstance(ket_expr, (KetBase, Mul)) and
- isinstance(bra_expr, (BraBase, Mul))):
- ket_c, kets = ket_expr.args_cnc()
- bra_c, bras = bra_expr.args_cnc()
- if len(kets) != 1 or not isinstance(kets[0], KetBase):
- raise TypeError('KetBase subclass expected'
- ', got: %r' % Mul(*kets))
- if len(bras) != 1 or not isinstance(bras[0], BraBase):
- raise TypeError('BraBase subclass expected'
- ', got: %r' % Mul(*bras))
- if not kets[0].dual_class() == bras[0].__class__:
- raise TypeError(
- 'ket and bra are not dual classes: %r, %r' %
- (kets[0].__class__, bras[0].__class__)
- )
- # TODO: make sure the hilbert spaces of the bra and ket are
- # compatible
- obj = Expr.__new__(cls, *(kets[0], bras[0]), **old_assumptions)
- obj.hilbert_space = kets[0].hilbert_space
- return Mul(*(ket_c + bra_c)) * obj
- op_terms = []
- if isinstance(ket_expr, Add) and isinstance(bra_expr, Add):
- for ket_term in ket_expr.args:
- for bra_term in bra_expr.args:
- op_terms.append(OuterProduct(ket_term, bra_term,
- **old_assumptions))
- elif isinstance(ket_expr, Add):
- for ket_term in ket_expr.args:
- op_terms.append(OuterProduct(ket_term, bra_expr,
- **old_assumptions))
- elif isinstance(bra_expr, Add):
- for bra_term in bra_expr.args:
- op_terms.append(OuterProduct(ket_expr, bra_term,
- **old_assumptions))
- else:
- raise TypeError(
- 'Expected ket and bra expression, got: %r, %r' %
- (ket_expr, bra_expr)
- )
- return Add(*op_terms)
- @property
- def ket(self):
- """Return the ket on the left side of the outer product."""
- return self.args[0]
- @property
- def bra(self):
- """Return the bra on the right side of the outer product."""
- return self.args[1]
- def _eval_adjoint(self):
- return OuterProduct(Dagger(self.bra), Dagger(self.ket))
- def _sympystr(self, printer, *args):
- return printer._print(self.ket) + printer._print(self.bra)
- def _sympyrepr(self, printer, *args):
- return '%s(%s,%s)' % (self.__class__.__name__,
- printer._print(self.ket, *args), printer._print(self.bra, *args))
- def _pretty(self, printer, *args):
- pform = self.ket._pretty(printer, *args)
- return prettyForm(*pform.right(self.bra._pretty(printer, *args)))
- def _latex(self, printer, *args):
- k = printer._print(self.ket, *args)
- b = printer._print(self.bra, *args)
- return k + b
- def _represent(self, **options):
- k = self.ket._represent(**options)
- b = self.bra._represent(**options)
- return k*b
- def _eval_trace(self, **kwargs):
- # TODO if operands are tensorproducts this may be will be handled
- # differently.
- return self.ket._eval_trace(self.bra, **kwargs)
- class DifferentialOperator(Operator):
- """An operator for representing the differential operator, i.e. d/dx
- It is initialized by passing two arguments. The first is an arbitrary
- expression that involves a function, such as ``Derivative(f(x), x)``. The
- second is the function (e.g. ``f(x)``) which we are to replace with the
- ``Wavefunction`` that this ``DifferentialOperator`` is applied to.
- Parameters
- ==========
- expr : Expr
- The arbitrary expression which the appropriate Wavefunction is to be
- substituted into
- func : Expr
- A function (e.g. f(x)) which is to be replaced with the appropriate
- Wavefunction when this DifferentialOperator is applied
- Examples
- ========
- You can define a completely arbitrary expression and specify where the
- Wavefunction is to be substituted
- >>> from sympy import Derivative, Function, Symbol
- >>> from sympy.physics.quantum.operator import DifferentialOperator
- >>> from sympy.physics.quantum.state import Wavefunction
- >>> from sympy.physics.quantum.qapply import qapply
- >>> f = Function('f')
- >>> x = Symbol('x')
- >>> d = DifferentialOperator(1/x*Derivative(f(x), x), f(x))
- >>> w = Wavefunction(x**2, x)
- >>> d.function
- f(x)
- >>> d.variables
- (x,)
- >>> qapply(d*w)
- Wavefunction(2, x)
- """
- @property
- def variables(self):
- """
- Returns the variables with which the function in the specified
- arbitrary expression is evaluated
- Examples
- ========
- >>> from sympy.physics.quantum.operator import DifferentialOperator
- >>> from sympy import Symbol, Function, Derivative
- >>> x = Symbol('x')
- >>> f = Function('f')
- >>> d = DifferentialOperator(1/x*Derivative(f(x), x), f(x))
- >>> d.variables
- (x,)
- >>> y = Symbol('y')
- >>> d = DifferentialOperator(Derivative(f(x, y), x) +
- ... Derivative(f(x, y), y), f(x, y))
- >>> d.variables
- (x, y)
- """
- return self.args[-1].args
- @property
- def function(self):
- """
- Returns the function which is to be replaced with the Wavefunction
- Examples
- ========
- >>> from sympy.physics.quantum.operator import DifferentialOperator
- >>> from sympy import Function, Symbol, Derivative
- >>> x = Symbol('x')
- >>> f = Function('f')
- >>> d = DifferentialOperator(Derivative(f(x), x), f(x))
- >>> d.function
- f(x)
- >>> y = Symbol('y')
- >>> d = DifferentialOperator(Derivative(f(x, y), x) +
- ... Derivative(f(x, y), y), f(x, y))
- >>> d.function
- f(x, y)
- """
- return self.args[-1]
- @property
- def expr(self):
- """
- Returns the arbitrary expression which is to have the Wavefunction
- substituted into it
- Examples
- ========
- >>> from sympy.physics.quantum.operator import DifferentialOperator
- >>> from sympy import Function, Symbol, Derivative
- >>> x = Symbol('x')
- >>> f = Function('f')
- >>> d = DifferentialOperator(Derivative(f(x), x), f(x))
- >>> d.expr
- Derivative(f(x), x)
- >>> y = Symbol('y')
- >>> d = DifferentialOperator(Derivative(f(x, y), x) +
- ... Derivative(f(x, y), y), f(x, y))
- >>> d.expr
- Derivative(f(x, y), x) + Derivative(f(x, y), y)
- """
- return self.args[0]
- @property
- def free_symbols(self):
- """
- Return the free symbols of the expression.
- """
- return self.expr.free_symbols
- def _apply_operator_Wavefunction(self, func, **options):
- from sympy.physics.quantum.state import Wavefunction
- var = self.variables
- wf_vars = func.args[1:]
- f = self.function
- new_expr = self.expr.subs(f, func(*var))
- new_expr = new_expr.doit()
- return Wavefunction(new_expr, *wf_vars)
- def _eval_derivative(self, symbol):
- new_expr = Derivative(self.expr, symbol)
- return DifferentialOperator(new_expr, self.args[-1])
- #-------------------------------------------------------------------------
- # Printing
- #-------------------------------------------------------------------------
- def _print(self, printer, *args):
- return '%s(%s)' % (
- self._print_operator_name(printer, *args),
- self._print_label(printer, *args)
- )
- def _print_pretty(self, printer, *args):
- pform = self._print_operator_name_pretty(printer, *args)
- label_pform = self._print_label_pretty(printer, *args)
- label_pform = prettyForm(
- *label_pform.parens(left='(', right=')')
- )
- pform = prettyForm(*pform.right(label_pform))
- return pform
|