|
"""Dirac notation for states.""" |
|
|
|
from sympy.core.cache import cacheit |
|
from sympy.core.containers import Tuple |
|
from sympy.core.expr import Expr |
|
from sympy.core.function import Function |
|
from sympy.core.numbers import oo, equal_valued |
|
from sympy.core.singleton import S |
|
from sympy.functions.elementary.complexes import conjugate |
|
from sympy.functions.elementary.miscellaneous import sqrt |
|
from sympy.integrals.integrals import integrate |
|
from sympy.printing.pretty.stringpict import stringPict |
|
from sympy.physics.quantum.qexpr import QExpr, dispatch_method |
|
from sympy.physics.quantum.kind import KetKind, BraKind |
|
|
|
|
|
__all__ = [ |
|
'KetBase', |
|
'BraBase', |
|
'StateBase', |
|
'State', |
|
'Ket', |
|
'Bra', |
|
'TimeDepState', |
|
'TimeDepBra', |
|
'TimeDepKet', |
|
'OrthogonalKet', |
|
'OrthogonalBra', |
|
'OrthogonalState', |
|
'Wavefunction' |
|
] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_lbracket = "<" |
|
_rbracket = ">" |
|
_straight_bracket = "|" |
|
|
|
|
|
|
|
|
|
_lbracket_ucode = "\N{MATHEMATICAL LEFT ANGLE BRACKET}" |
|
_rbracket_ucode = "\N{MATHEMATICAL RIGHT ANGLE BRACKET}" |
|
|
|
_straight_bracket_ucode = "\N{LIGHT VERTICAL BAR}" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class StateBase(QExpr): |
|
"""Abstract base class for general abstract states in quantum mechanics. |
|
|
|
All other state classes defined will need to inherit from this class. It |
|
carries the basic structure for all other states such as dual, _eval_adjoint |
|
and label. |
|
|
|
This is an abstract base class and you should not instantiate it directly, |
|
instead use State. |
|
""" |
|
|
|
@classmethod |
|
def _operators_to_state(self, ops, **options): |
|
""" Returns the eigenstate instance for the passed operators. |
|
|
|
This method should be overridden in subclasses. It will handle being |
|
passed either an Operator instance or set of Operator instances. It |
|
should return the corresponding state INSTANCE or simply raise a |
|
NotImplementedError. See cartesian.py for an example. |
|
""" |
|
|
|
raise NotImplementedError("Cannot map operators to states in this class. Method not implemented!") |
|
|
|
def _state_to_operators(self, op_classes, **options): |
|
""" Returns the operators which this state instance is an eigenstate |
|
of. |
|
|
|
This method should be overridden in subclasses. It will be called on |
|
state instances and be passed the operator classes that we wish to make |
|
into instances. The state instance will then transform the classes |
|
appropriately, or raise a NotImplementedError if it cannot return |
|
operator instances. See cartesian.py for examples, |
|
""" |
|
|
|
raise NotImplementedError( |
|
"Cannot map this state to operators. Method not implemented!") |
|
|
|
@property |
|
def operators(self): |
|
"""Return the operator(s) that this state is an eigenstate of""" |
|
from .operatorset import state_to_operators |
|
return state_to_operators(self) |
|
|
|
def _enumerate_state(self, num_states, **options): |
|
raise NotImplementedError("Cannot enumerate this state!") |
|
|
|
def _represent_default_basis(self, **options): |
|
return self._represent(basis=self.operators) |
|
|
|
def _apply_operator(self, op, **options): |
|
return None |
|
|
|
|
|
|
|
|
|
|
|
@property |
|
def dual(self): |
|
"""Return the dual state of this one.""" |
|
return self.dual_class()._new_rawargs(self.hilbert_space, *self.args) |
|
|
|
@classmethod |
|
def dual_class(self): |
|
"""Return the class used to construct the dual.""" |
|
raise NotImplementedError( |
|
'dual_class must be implemented in a subclass' |
|
) |
|
|
|
def _eval_adjoint(self): |
|
"""Compute the dagger of this state using the dual.""" |
|
return self.dual |
|
|
|
|
|
|
|
|
|
|
|
def _pretty_brackets(self, height, use_unicode=True): |
|
|
|
|
|
|
|
|
|
if use_unicode: |
|
lbracket, rbracket = getattr(self, 'lbracket_ucode', ""), getattr(self, 'rbracket_ucode', "") |
|
slash, bslash, vert = '\N{BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT}', \ |
|
'\N{BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT}', \ |
|
'\N{BOX DRAWINGS LIGHT VERTICAL}' |
|
else: |
|
lbracket, rbracket = getattr(self, 'lbracket', ""), getattr(self, 'rbracket', "") |
|
slash, bslash, vert = '/', '\\', '|' |
|
|
|
|
|
if height == 1: |
|
return stringPict(lbracket), stringPict(rbracket) |
|
|
|
height += (height % 2) |
|
|
|
brackets = [] |
|
for bracket in lbracket, rbracket: |
|
|
|
if bracket in {_lbracket, _lbracket_ucode}: |
|
bracket_args = [ ' ' * (height//2 - i - 1) + |
|
slash for i in range(height // 2)] |
|
bracket_args.extend( |
|
[' ' * i + bslash for i in range(height // 2)]) |
|
|
|
elif bracket in {_rbracket, _rbracket_ucode}: |
|
bracket_args = [ ' ' * i + bslash for i in range(height // 2)] |
|
bracket_args.extend([ ' ' * ( |
|
height//2 - i - 1) + slash for i in range(height // 2)]) |
|
|
|
elif bracket in {_straight_bracket, _straight_bracket_ucode}: |
|
bracket_args = [vert] * height |
|
else: |
|
raise ValueError(bracket) |
|
brackets.append( |
|
stringPict('\n'.join(bracket_args), baseline=height//2)) |
|
return brackets |
|
|
|
def _sympystr(self, printer, *args): |
|
contents = self._print_contents(printer, *args) |
|
return '%s%s%s' % (getattr(self, 'lbracket', ""), contents, getattr(self, 'rbracket', "")) |
|
|
|
def _pretty(self, printer, *args): |
|
from sympy.printing.pretty.stringpict import prettyForm |
|
|
|
pform = self._print_contents_pretty(printer, *args) |
|
lbracket, rbracket = self._pretty_brackets( |
|
pform.height(), printer._use_unicode) |
|
|
|
pform = prettyForm(*pform.left(lbracket)) |
|
pform = prettyForm(*pform.right(rbracket)) |
|
return pform |
|
|
|
def _latex(self, printer, *args): |
|
contents = self._print_contents_latex(printer, *args) |
|
|
|
|
|
return '{%s%s%s}' % (getattr(self, 'lbracket_latex', ""), contents, getattr(self, 'rbracket_latex', "")) |
|
|
|
|
|
class KetBase(StateBase): |
|
"""Base class for Kets. |
|
|
|
This class defines the dual property and the brackets for printing. This is |
|
an abstract base class and you should not instantiate it directly, instead |
|
use Ket. |
|
""" |
|
|
|
kind = KetKind |
|
|
|
lbracket = _straight_bracket |
|
rbracket = _rbracket |
|
lbracket_ucode = _straight_bracket_ucode |
|
rbracket_ucode = _rbracket_ucode |
|
lbracket_latex = r'\left|' |
|
rbracket_latex = r'\right\rangle ' |
|
|
|
@classmethod |
|
def default_args(self): |
|
return ("psi",) |
|
|
|
@classmethod |
|
def dual_class(self): |
|
return BraBase |
|
|
|
|
|
|
|
|
|
|
|
def _eval_innerproduct(self, bra, **hints): |
|
"""Evaluate the inner product between this ket and a bra. |
|
|
|
This is called to compute <bra|ket>, where the ket is ``self``. |
|
|
|
This method will dispatch to sub-methods having the format:: |
|
|
|
``def _eval_innerproduct_BraClass(self, **hints):`` |
|
|
|
Subclasses should define these methods (one for each BraClass) to |
|
teach the ket how to take inner products with bras. |
|
""" |
|
return dispatch_method(self, '_eval_innerproduct', bra, **hints) |
|
|
|
def _apply_from_right_to(self, op, **options): |
|
"""Apply an Operator to this Ket as Operator*Ket |
|
|
|
This method will dispatch to methods having the format:: |
|
|
|
``def _apply_from_right_to_OperatorName(op, **options):`` |
|
|
|
Subclasses should define these methods (one for each OperatorName) to |
|
teach the Ket how to implement OperatorName*Ket |
|
|
|
Parameters |
|
========== |
|
|
|
op : Operator |
|
The Operator that is acting on the Ket as op*Ket |
|
options : dict |
|
A dict of key/value pairs that control how the operator is applied |
|
to the Ket. |
|
""" |
|
return dispatch_method(self, '_apply_from_right_to', op, **options) |
|
|
|
|
|
class BraBase(StateBase): |
|
"""Base class for Bras. |
|
|
|
This class defines the dual property and the brackets for printing. This |
|
is an abstract base class and you should not instantiate it directly, |
|
instead use Bra. |
|
""" |
|
|
|
kind = BraKind |
|
|
|
lbracket = _lbracket |
|
rbracket = _straight_bracket |
|
lbracket_ucode = _lbracket_ucode |
|
rbracket_ucode = _straight_bracket_ucode |
|
lbracket_latex = r'\left\langle ' |
|
rbracket_latex = r'\right|' |
|
|
|
@classmethod |
|
def _operators_to_state(self, ops, **options): |
|
state = self.dual_class()._operators_to_state(ops, **options) |
|
return state.dual |
|
|
|
def _state_to_operators(self, op_classes, **options): |
|
return self.dual._state_to_operators(op_classes, **options) |
|
|
|
def _enumerate_state(self, num_states, **options): |
|
dual_states = self.dual._enumerate_state(num_states, **options) |
|
return [x.dual for x in dual_states] |
|
|
|
@classmethod |
|
def default_args(self): |
|
return self.dual_class().default_args() |
|
|
|
@classmethod |
|
def dual_class(self): |
|
return KetBase |
|
|
|
def _represent(self, **options): |
|
"""A default represent that uses the Ket's version.""" |
|
from sympy.physics.quantum.dagger import Dagger |
|
return Dagger(self.dual._represent(**options)) |
|
|
|
|
|
class State(StateBase): |
|
"""General abstract quantum state used as a base class for Ket and Bra.""" |
|
pass |
|
|
|
|
|
class Ket(State, KetBase): |
|
"""A general time-independent Ket in quantum mechanics. |
|
|
|
Inherits from State and KetBase. This class should be used as the base |
|
class for all physical, time-independent Kets in a system. This class |
|
and its subclasses will be the main classes that users will use for |
|
expressing Kets in Dirac notation [1]_. |
|
|
|
Parameters |
|
========== |
|
|
|
args : tuple |
|
The list of numbers or parameters that uniquely specify the |
|
ket. This will usually be its symbol or its quantum numbers. For |
|
time-dependent state, this will include the time. |
|
|
|
Examples |
|
======== |
|
|
|
Create a simple Ket and looking at its properties:: |
|
|
|
>>> from sympy.physics.quantum import Ket |
|
>>> from sympy import symbols, I |
|
>>> k = Ket('psi') |
|
>>> k |
|
|psi> |
|
>>> k.hilbert_space |
|
H |
|
>>> k.is_commutative |
|
False |
|
>>> k.label |
|
(psi,) |
|
|
|
Ket's know about their associated bra:: |
|
|
|
>>> k.dual |
|
<psi| |
|
>>> k.dual_class() |
|
<class 'sympy.physics.quantum.state.Bra'> |
|
|
|
Take a linear combination of two kets:: |
|
|
|
>>> k0 = Ket(0) |
|
>>> k1 = Ket(1) |
|
>>> 2*I*k0 - 4*k1 |
|
2*I*|0> - 4*|1> |
|
|
|
Compound labels are passed as tuples:: |
|
|
|
>>> n, m = symbols('n,m') |
|
>>> k = Ket(n,m) |
|
>>> k |
|
|nm> |
|
|
|
References |
|
========== |
|
|
|
.. [1] https://en.wikipedia.org/wiki/Bra-ket_notation |
|
""" |
|
|
|
@classmethod |
|
def dual_class(self): |
|
return Bra |
|
|
|
|
|
class Bra(State, BraBase): |
|
"""A general time-independent Bra in quantum mechanics. |
|
|
|
Inherits from State and BraBase. A Bra is the dual of a Ket [1]_. This |
|
class and its subclasses will be the main classes that users will use for |
|
expressing Bras in Dirac notation. |
|
|
|
Parameters |
|
========== |
|
|
|
args : tuple |
|
The list of numbers or parameters that uniquely specify the |
|
ket. This will usually be its symbol or its quantum numbers. For |
|
time-dependent state, this will include the time. |
|
|
|
Examples |
|
======== |
|
|
|
Create a simple Bra and look at its properties:: |
|
|
|
>>> from sympy.physics.quantum import Bra |
|
>>> from sympy import symbols, I |
|
>>> b = Bra('psi') |
|
>>> b |
|
<psi| |
|
>>> b.hilbert_space |
|
H |
|
>>> b.is_commutative |
|
False |
|
|
|
Bra's know about their dual Ket's:: |
|
|
|
>>> b.dual |
|
|psi> |
|
>>> b.dual_class() |
|
<class 'sympy.physics.quantum.state.Ket'> |
|
|
|
Like Kets, Bras can have compound labels and be manipulated in a similar |
|
manner:: |
|
|
|
>>> n, m = symbols('n,m') |
|
>>> b = Bra(n,m) - I*Bra(m,n) |
|
>>> b |
|
-I*<mn| + <nm| |
|
|
|
Symbols in a Bra can be substituted using ``.subs``:: |
|
|
|
>>> b.subs(n,m) |
|
<mm| - I*<mm| |
|
|
|
References |
|
========== |
|
|
|
.. [1] https://en.wikipedia.org/wiki/Bra-ket_notation |
|
""" |
|
|
|
@classmethod |
|
def dual_class(self): |
|
return Ket |
|
|
|
|
|
|
|
|
|
|
|
|
|
class TimeDepState(StateBase): |
|
"""Base class for a general time-dependent quantum state. |
|
|
|
This class is used as a base class for any time-dependent state. The main |
|
difference between this class and the time-independent state is that this |
|
class takes a second argument that is the time in addition to the usual |
|
label argument. |
|
|
|
Parameters |
|
========== |
|
|
|
args : tuple |
|
The list of numbers or parameters that uniquely specify the ket. This |
|
will usually be its symbol or its quantum numbers. For time-dependent |
|
state, this will include the time as the final argument. |
|
""" |
|
|
|
|
|
|
|
|
|
|
|
@classmethod |
|
def default_args(self): |
|
return ("psi", "t") |
|
|
|
|
|
|
|
|
|
|
|
@property |
|
def label(self): |
|
"""The label of the state.""" |
|
return self.args[:-1] |
|
|
|
@property |
|
def time(self): |
|
"""The time of the state.""" |
|
return self.args[-1] |
|
|
|
|
|
|
|
|
|
|
|
def _print_time(self, printer, *args): |
|
return printer._print(self.time, *args) |
|
|
|
_print_time_repr = _print_time |
|
_print_time_latex = _print_time |
|
|
|
def _print_time_pretty(self, printer, *args): |
|
pform = printer._print(self.time, *args) |
|
return pform |
|
|
|
def _print_contents(self, printer, *args): |
|
label = self._print_label(printer, *args) |
|
time = self._print_time(printer, *args) |
|
return '%s;%s' % (label, time) |
|
|
|
def _print_label_repr(self, printer, *args): |
|
label = self._print_sequence(self.label, ',', printer, *args) |
|
time = self._print_time_repr(printer, *args) |
|
return '%s,%s' % (label, time) |
|
|
|
def _print_contents_pretty(self, printer, *args): |
|
label = self._print_label_pretty(printer, *args) |
|
time = self._print_time_pretty(printer, *args) |
|
return printer._print_seq((label, time), delimiter=';') |
|
|
|
def _print_contents_latex(self, printer, *args): |
|
label = self._print_sequence( |
|
self.label, self._label_separator, printer, *args) |
|
time = self._print_time_latex(printer, *args) |
|
return '%s;%s' % (label, time) |
|
|
|
|
|
class TimeDepKet(TimeDepState, KetBase): |
|
"""General time-dependent Ket in quantum mechanics. |
|
|
|
This inherits from ``TimeDepState`` and ``KetBase`` and is the main class |
|
that should be used for Kets that vary with time. Its dual is a |
|
``TimeDepBra``. |
|
|
|
Parameters |
|
========== |
|
|
|
args : tuple |
|
The list of numbers or parameters that uniquely specify the ket. This |
|
will usually be its symbol or its quantum numbers. For time-dependent |
|
state, this will include the time as the final argument. |
|
|
|
Examples |
|
======== |
|
|
|
Create a TimeDepKet and look at its attributes:: |
|
|
|
>>> from sympy.physics.quantum import TimeDepKet |
|
>>> k = TimeDepKet('psi', 't') |
|
>>> k |
|
|psi;t> |
|
>>> k.time |
|
t |
|
>>> k.label |
|
(psi,) |
|
>>> k.hilbert_space |
|
H |
|
|
|
TimeDepKets know about their dual bra:: |
|
|
|
>>> k.dual |
|
<psi;t| |
|
>>> k.dual_class() |
|
<class 'sympy.physics.quantum.state.TimeDepBra'> |
|
""" |
|
|
|
@classmethod |
|
def dual_class(self): |
|
return TimeDepBra |
|
|
|
|
|
class TimeDepBra(TimeDepState, BraBase): |
|
"""General time-dependent Bra in quantum mechanics. |
|
|
|
This inherits from TimeDepState and BraBase and is the main class that |
|
should be used for Bras that vary with time. Its dual is a TimeDepBra. |
|
|
|
Parameters |
|
========== |
|
|
|
args : tuple |
|
The list of numbers or parameters that uniquely specify the ket. This |
|
will usually be its symbol or its quantum numbers. For time-dependent |
|
state, this will include the time as the final argument. |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.physics.quantum import TimeDepBra |
|
>>> b = TimeDepBra('psi', 't') |
|
>>> b |
|
<psi;t| |
|
>>> b.time |
|
t |
|
>>> b.label |
|
(psi,) |
|
>>> b.hilbert_space |
|
H |
|
>>> b.dual |
|
|psi;t> |
|
""" |
|
|
|
@classmethod |
|
def dual_class(self): |
|
return TimeDepKet |
|
|
|
|
|
class OrthogonalState(State): |
|
"""General abstract quantum state used as a base class for Ket and Bra.""" |
|
pass |
|
|
|
class OrthogonalKet(OrthogonalState, KetBase): |
|
"""Orthogonal Ket in quantum mechanics. |
|
|
|
The inner product of two states with different labels will give zero, |
|
states with the same label will give one. |
|
|
|
>>> from sympy.physics.quantum import OrthogonalBra, OrthogonalKet |
|
>>> from sympy.abc import m, n |
|
>>> (OrthogonalBra(n)*OrthogonalKet(n)).doit() |
|
1 |
|
>>> (OrthogonalBra(n)*OrthogonalKet(n+1)).doit() |
|
0 |
|
>>> (OrthogonalBra(n)*OrthogonalKet(m)).doit() |
|
<n|m> |
|
""" |
|
|
|
@classmethod |
|
def dual_class(self): |
|
return OrthogonalBra |
|
|
|
def _eval_innerproduct(self, bra, **hints): |
|
|
|
if len(self.args) != len(bra.args): |
|
raise ValueError('Cannot multiply a ket that has a different number of labels.') |
|
|
|
for arg, bra_arg in zip(self.args, bra.args): |
|
diff = arg - bra_arg |
|
diff = diff.expand() |
|
|
|
is_zero = diff.is_zero |
|
|
|
if is_zero is False: |
|
return S.Zero |
|
|
|
if is_zero is None: |
|
return None |
|
|
|
return S.One |
|
|
|
|
|
class OrthogonalBra(OrthogonalState, BraBase): |
|
"""Orthogonal Bra in quantum mechanics. |
|
""" |
|
|
|
@classmethod |
|
def dual_class(self): |
|
return OrthogonalKet |
|
|
|
|
|
class Wavefunction(Function): |
|
"""Class for representations in continuous bases |
|
|
|
This class takes an expression and coordinates in its constructor. It can |
|
be used to easily calculate normalizations and probabilities. |
|
|
|
Parameters |
|
========== |
|
|
|
expr : Expr |
|
The expression representing the functional form of the w.f. |
|
|
|
coords : Symbol or tuple |
|
The coordinates to be integrated over, and their bounds |
|
|
|
Examples |
|
======== |
|
|
|
Particle in a box, specifying bounds in the more primitive way of using |
|
Piecewise: |
|
|
|
>>> from sympy import Symbol, Piecewise, pi, N |
|
>>> from sympy.functions import sqrt, sin |
|
>>> from sympy.physics.quantum.state import Wavefunction |
|
>>> x = Symbol('x', real=True) |
|
>>> n = 1 |
|
>>> L = 1 |
|
>>> g = Piecewise((0, x < 0), (0, x > L), (sqrt(2//L)*sin(n*pi*x/L), True)) |
|
>>> f = Wavefunction(g, x) |
|
>>> f.norm |
|
1 |
|
>>> f.is_normalized |
|
True |
|
>>> p = f.prob() |
|
>>> p(0) |
|
0 |
|
>>> p(L) |
|
0 |
|
>>> p(0.5) |
|
2 |
|
>>> p(0.85*L) |
|
2*sin(0.85*pi)**2 |
|
>>> N(p(0.85*L)) |
|
0.412214747707527 |
|
|
|
Additionally, you can specify the bounds of the function and the indices in |
|
a more compact way: |
|
|
|
>>> from sympy import symbols, pi, diff |
|
>>> from sympy.functions import sqrt, sin |
|
>>> from sympy.physics.quantum.state import Wavefunction |
|
>>> x, L = symbols('x,L', positive=True) |
|
>>> n = symbols('n', integer=True, positive=True) |
|
>>> g = sqrt(2/L)*sin(n*pi*x/L) |
|
>>> f = Wavefunction(g, (x, 0, L)) |
|
>>> f.norm |
|
1 |
|
>>> f(L+1) |
|
0 |
|
>>> f(L-1) |
|
sqrt(2)*sin(pi*n*(L - 1)/L)/sqrt(L) |
|
>>> f(-1) |
|
0 |
|
>>> f(0.85) |
|
sqrt(2)*sin(0.85*pi*n/L)/sqrt(L) |
|
>>> f(0.85, n=1, L=1) |
|
sqrt(2)*sin(0.85*pi) |
|
>>> f.is_commutative |
|
False |
|
|
|
All arguments are automatically sympified, so you can define the variables |
|
as strings rather than symbols: |
|
|
|
>>> expr = x**2 |
|
>>> f = Wavefunction(expr, 'x') |
|
>>> type(f.variables[0]) |
|
<class 'sympy.core.symbol.Symbol'> |
|
|
|
Derivatives of Wavefunctions will return Wavefunctions: |
|
|
|
>>> diff(f, x) |
|
Wavefunction(2*x, x) |
|
|
|
""" |
|
|
|
|
|
|
|
|
|
def __new__(cls, *args, **options): |
|
new_args = [None for i in args] |
|
ct = 0 |
|
for arg in args: |
|
if isinstance(arg, tuple): |
|
new_args[ct] = Tuple(*arg) |
|
else: |
|
new_args[ct] = arg |
|
ct += 1 |
|
|
|
return super().__new__(cls, *new_args, **options) |
|
|
|
def __call__(self, *args, **options): |
|
var = self.variables |
|
|
|
if len(args) != len(var): |
|
raise NotImplementedError( |
|
"Incorrect number of arguments to function!") |
|
|
|
ct = 0 |
|
|
|
for v in var: |
|
lower, upper = self.limits[v] |
|
|
|
|
|
|
|
|
|
if isinstance(args[ct], Expr) and \ |
|
not (lower in args[ct].free_symbols |
|
or upper in args[ct].free_symbols): |
|
continue |
|
|
|
if (args[ct] < lower) == True or (args[ct] > upper) == True: |
|
return S.Zero |
|
|
|
ct += 1 |
|
|
|
expr = self.expr |
|
|
|
|
|
for symbol in list(expr.free_symbols): |
|
if str(symbol) in options.keys(): |
|
val = options[str(symbol)] |
|
expr = expr.subs(symbol, val) |
|
|
|
return expr.subs(zip(var, args)) |
|
|
|
def _eval_derivative(self, symbol): |
|
expr = self.expr |
|
deriv = expr._eval_derivative(symbol) |
|
|
|
return Wavefunction(deriv, *self.args[1:]) |
|
|
|
def _eval_conjugate(self): |
|
return Wavefunction(conjugate(self.expr), *self.args[1:]) |
|
|
|
def _eval_transpose(self): |
|
return self |
|
|
|
@property |
|
def is_commutative(self): |
|
""" |
|
Override Function's is_commutative so that order is preserved in |
|
represented expressions |
|
""" |
|
return False |
|
|
|
@classmethod |
|
def eval(self, *args): |
|
return None |
|
|
|
@property |
|
def variables(self): |
|
""" |
|
Return the coordinates which the wavefunction depends on |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.physics.quantum.state import Wavefunction |
|
>>> from sympy import symbols |
|
>>> x,y = symbols('x,y') |
|
>>> f = Wavefunction(x*y, x, y) |
|
>>> f.variables |
|
(x, y) |
|
>>> g = Wavefunction(x*y, x) |
|
>>> g.variables |
|
(x,) |
|
|
|
""" |
|
var = [g[0] if isinstance(g, Tuple) else g for g in self._args[1:]] |
|
return tuple(var) |
|
|
|
@property |
|
def limits(self): |
|
""" |
|
Return the limits of the coordinates which the w.f. depends on If no |
|
limits are specified, defaults to ``(-oo, oo)``. |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.physics.quantum.state import Wavefunction |
|
>>> from sympy import symbols |
|
>>> x, y = symbols('x, y') |
|
>>> f = Wavefunction(x**2, (x, 0, 1)) |
|
>>> f.limits |
|
{x: (0, 1)} |
|
>>> f = Wavefunction(x**2, x) |
|
>>> f.limits |
|
{x: (-oo, oo)} |
|
>>> f = Wavefunction(x**2 + y**2, x, (y, -1, 2)) |
|
>>> f.limits |
|
{x: (-oo, oo), y: (-1, 2)} |
|
|
|
""" |
|
limits = [(g[1], g[2]) if isinstance(g, Tuple) else (-oo, oo) |
|
for g in self._args[1:]] |
|
return dict(zip(self.variables, tuple(limits))) |
|
|
|
@property |
|
def expr(self): |
|
""" |
|
Return the expression which is the functional form of the Wavefunction |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.physics.quantum.state import Wavefunction |
|
>>> from sympy import symbols |
|
>>> x, y = symbols('x, y') |
|
>>> f = Wavefunction(x**2, x) |
|
>>> f.expr |
|
x**2 |
|
|
|
""" |
|
return self._args[0] |
|
|
|
@property |
|
def is_normalized(self): |
|
""" |
|
Returns true if the Wavefunction is properly normalized |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy import symbols, pi |
|
>>> from sympy.functions import sqrt, sin |
|
>>> from sympy.physics.quantum.state import Wavefunction |
|
>>> x, L = symbols('x,L', positive=True) |
|
>>> n = symbols('n', integer=True, positive=True) |
|
>>> g = sqrt(2/L)*sin(n*pi*x/L) |
|
>>> f = Wavefunction(g, (x, 0, L)) |
|
>>> f.is_normalized |
|
True |
|
|
|
""" |
|
|
|
return equal_valued(self.norm, 1) |
|
|
|
@property |
|
@cacheit |
|
def norm(self): |
|
""" |
|
Return the normalization of the specified functional form. |
|
|
|
This function integrates over the coordinates of the Wavefunction, with |
|
the bounds specified. |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy import symbols, pi |
|
>>> from sympy.functions import sqrt, sin |
|
>>> from sympy.physics.quantum.state import Wavefunction |
|
>>> x, L = symbols('x,L', positive=True) |
|
>>> n = symbols('n', integer=True, positive=True) |
|
>>> g = sqrt(2/L)*sin(n*pi*x/L) |
|
>>> f = Wavefunction(g, (x, 0, L)) |
|
>>> f.norm |
|
1 |
|
>>> g = sin(n*pi*x/L) |
|
>>> f = Wavefunction(g, (x, 0, L)) |
|
>>> f.norm |
|
sqrt(2)*sqrt(L)/2 |
|
|
|
""" |
|
|
|
exp = self.expr*conjugate(self.expr) |
|
var = self.variables |
|
limits = self.limits |
|
|
|
for v in var: |
|
curr_limits = limits[v] |
|
exp = integrate(exp, (v, curr_limits[0], curr_limits[1])) |
|
|
|
return sqrt(exp) |
|
|
|
def normalize(self): |
|
""" |
|
Return a normalized version of the Wavefunction |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy import symbols, pi |
|
>>> from sympy.functions import sin |
|
>>> from sympy.physics.quantum.state import Wavefunction |
|
>>> x = symbols('x', real=True) |
|
>>> L = symbols('L', positive=True) |
|
>>> n = symbols('n', integer=True, positive=True) |
|
>>> g = sin(n*pi*x/L) |
|
>>> f = Wavefunction(g, (x, 0, L)) |
|
>>> f.normalize() |
|
Wavefunction(sqrt(2)*sin(pi*n*x/L)/sqrt(L), (x, 0, L)) |
|
|
|
""" |
|
const = self.norm |
|
|
|
if const is oo: |
|
raise NotImplementedError("The function is not normalizable!") |
|
else: |
|
return Wavefunction((const)**(-1)*self.expr, *self.args[1:]) |
|
|
|
def prob(self): |
|
r""" |
|
Return the absolute magnitude of the w.f., `|\psi(x)|^2` |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy import symbols, pi |
|
>>> from sympy.functions import sin |
|
>>> from sympy.physics.quantum.state import Wavefunction |
|
>>> x, L = symbols('x,L', real=True) |
|
>>> n = symbols('n', integer=True) |
|
>>> g = sin(n*pi*x/L) |
|
>>> f = Wavefunction(g, (x, 0, L)) |
|
>>> f.prob() |
|
Wavefunction(sin(pi*n*x/L)**2, x) |
|
|
|
""" |
|
|
|
return Wavefunction(self.expr*conjugate(self.expr), *self.variables) |
|
|