Spaces:
Sleeping
Sleeping
"""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 | |
__all__ = [ | |
'KetBase', | |
'BraBase', | |
'StateBase', | |
'State', | |
'Ket', | |
'Bra', | |
'TimeDepState', | |
'TimeDepBra', | |
'TimeDepKet', | |
'OrthogonalKet', | |
'OrthogonalBra', | |
'OrthogonalState', | |
'Wavefunction' | |
] | |
#----------------------------------------------------------------------------- | |
# States, bras and kets. | |
#----------------------------------------------------------------------------- | |
# ASCII brackets | |
_lbracket = "<" | |
_rbracket = ">" | |
_straight_bracket = "|" | |
# Unicode brackets | |
# MATHEMATICAL ANGLE BRACKETS | |
_lbracket_ucode = "\N{MATHEMATICAL LEFT ANGLE BRACKET}" | |
_rbracket_ucode = "\N{MATHEMATICAL RIGHT ANGLE BRACKET}" | |
# LIGHT VERTICAL BAR | |
_straight_bracket_ucode = "\N{LIGHT VERTICAL BAR}" | |
# Other options for unicode printing of <, > and | for Dirac notation. | |
# LEFT-POINTING ANGLE BRACKET | |
# _lbracket = "\u2329" | |
# _rbracket = "\u232A" | |
# LEFT ANGLE BRACKET | |
# _lbracket = "\u3008" | |
# _rbracket = "\u3009" | |
# VERTICAL LINE | |
# _straight_bracket = "\u007C" | |
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. | |
""" | |
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!") | |
def operators(self): | |
"""Return the operator(s) that this state is an eigenstate of""" | |
from .operatorset import state_to_operators # import internally to avoid circular import errors | |
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 | |
#------------------------------------------------------------------------- | |
# Dagger/dual | |
#------------------------------------------------------------------------- | |
def dual(self): | |
"""Return the dual state of this one.""" | |
return self.dual_class()._new_rawargs(self.hilbert_space, *self.args) | |
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 | |
#------------------------------------------------------------------------- | |
# Printing | |
#------------------------------------------------------------------------- | |
def _pretty_brackets(self, height, use_unicode=True): | |
# Return pretty printed brackets for the state | |
# Ideally, this could be done by pform.parens but it does not support the angled < and > | |
# Setup for unicode vs ascii | |
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 is 1, just return brackets | |
if height == 1: | |
return stringPict(lbracket), stringPict(rbracket) | |
# Make height even | |
height += (height % 2) | |
brackets = [] | |
for bracket in lbracket, rbracket: | |
# Create left bracket | |
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)]) | |
# Create right bracket | |
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)]) | |
# Create straight bracket | |
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 | |
# Get brackets | |
pform = self._print_contents_pretty(printer, *args) | |
lbracket, rbracket = self._pretty_brackets( | |
pform.height(), printer._use_unicode) | |
# Put together state | |
pform = prettyForm(*pform.left(lbracket)) | |
pform = prettyForm(*pform.right(rbracket)) | |
return pform | |
def _latex(self, printer, *args): | |
contents = self._print_contents_latex(printer, *args) | |
# The extra {} brackets are needed to get matplotlib's latex | |
# rendered to render this properly. | |
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. | |
""" | |
lbracket = _straight_bracket | |
rbracket = _rbracket | |
lbracket_ucode = _straight_bracket_ucode | |
rbracket_ucode = _rbracket_ucode | |
lbracket_latex = r'\left|' | |
rbracket_latex = r'\right\rangle ' | |
def default_args(self): | |
return ("psi",) | |
def dual_class(self): | |
return BraBase | |
def __mul__(self, other): | |
"""KetBase*other""" | |
from sympy.physics.quantum.operator import OuterProduct | |
if isinstance(other, BraBase): | |
return OuterProduct(self, other) | |
else: | |
return Expr.__mul__(self, other) | |
def __rmul__(self, other): | |
"""other*KetBase""" | |
from sympy.physics.quantum.innerproduct import InnerProduct | |
if isinstance(other, BraBase): | |
return InnerProduct(other, self) | |
else: | |
return Expr.__rmul__(self, other) | |
#------------------------------------------------------------------------- | |
# _eval_* methods | |
#------------------------------------------------------------------------- | |
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. | |
""" | |
lbracket = _lbracket | |
rbracket = _straight_bracket | |
lbracket_ucode = _lbracket_ucode | |
rbracket_ucode = _straight_bracket_ucode | |
lbracket_latex = r'\left\langle ' | |
rbracket_latex = r'\right|' | |
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] | |
def default_args(self): | |
return self.dual_class().default_args() | |
def dual_class(self): | |
return KetBase | |
def __mul__(self, other): | |
"""BraBase*other""" | |
from sympy.physics.quantum.innerproduct import InnerProduct | |
if isinstance(other, KetBase): | |
return InnerProduct(self, other) | |
else: | |
return Expr.__mul__(self, other) | |
def __rmul__(self, other): | |
"""other*BraBase""" | |
from sympy.physics.quantum.operator import OuterProduct | |
if isinstance(other, KetBase): | |
return OuterProduct(other, self) | |
else: | |
return Expr.__rmul__(self, other) | |
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 | |
""" | |
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 | |
""" | |
def dual_class(self): | |
return Ket | |
#----------------------------------------------------------------------------- | |
# Time dependent states, bras and kets. | |
#----------------------------------------------------------------------------- | |
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. | |
""" | |
#------------------------------------------------------------------------- | |
# Initialization | |
#------------------------------------------------------------------------- | |
def default_args(self): | |
return ("psi", "t") | |
#------------------------------------------------------------------------- | |
# Properties | |
#------------------------------------------------------------------------- | |
def label(self): | |
"""The label of the state.""" | |
return self.args[:-1] | |
def time(self): | |
"""The time of the state.""" | |
return self.args[-1] | |
#------------------------------------------------------------------------- | |
# Printing | |
#------------------------------------------------------------------------- | |
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'> | |
""" | |
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> | |
""" | |
def dual_class(self): | |
return TimeDepKet | |
class OrthogonalState(State, StateBase): | |
"""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> | |
""" | |
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 # i.e. Integer(0) | |
if is_zero is None: | |
return None | |
return S.One # i.e. Integer(1) | |
class OrthogonalBra(OrthogonalState, BraBase): | |
"""Orthogonal Bra in quantum mechanics. | |
""" | |
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) | |
""" | |
#Any passed tuples for coordinates and their bounds need to be | |
#converted to Tuples before Function's constructor is called, to | |
#avoid errors from calling is_Float in the constructor | |
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 | |
#If the passed value is outside the specified bounds, return 0 | |
for v in var: | |
lower, upper = self.limits[v] | |
#Do the comparison to limits only if the passed symbol is actually | |
#a symbol present in the limits; | |
#Had problems with a comparison of x > L | |
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 | |
#Allows user to make a call like f(2, 4, m=1, n=1) | |
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 | |
def free_symbols(self): | |
return self.expr.free_symbols | |
def is_commutative(self): | |
""" | |
Override Function's is_commutative so that order is preserved in | |
represented expressions | |
""" | |
return False | |
def eval(self, *args): | |
return None | |
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) | |
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))) | |
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] | |
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) | |
# type: ignore | |
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) | |