switching to high quality piper tts and added label translations
This commit is contained in:
@@ -0,0 +1,574 @@
|
||||
"""Logic for representing operators in state in various bases.
|
||||
|
||||
TODO:
|
||||
|
||||
* Get represent working with continuous hilbert spaces.
|
||||
* Document default basis functionality.
|
||||
"""
|
||||
|
||||
from sympy.core.add import Add
|
||||
from sympy.core.expr import Expr
|
||||
from sympy.core.mul import Mul
|
||||
from sympy.core.numbers import I
|
||||
from sympy.core.power import Pow
|
||||
from sympy.integrals.integrals import integrate
|
||||
from sympy.physics.quantum.dagger import Dagger
|
||||
from sympy.physics.quantum.commutator import Commutator
|
||||
from sympy.physics.quantum.anticommutator import AntiCommutator
|
||||
from sympy.physics.quantum.innerproduct import InnerProduct
|
||||
from sympy.physics.quantum.qexpr import QExpr
|
||||
from sympy.physics.quantum.tensorproduct import TensorProduct
|
||||
from sympy.physics.quantum.matrixutils import flatten_scalar
|
||||
from sympy.physics.quantum.state import KetBase, BraBase, StateBase
|
||||
from sympy.physics.quantum.operator import Operator, OuterProduct
|
||||
from sympy.physics.quantum.qapply import qapply
|
||||
from sympy.physics.quantum.operatorset import operators_to_state, state_to_operators
|
||||
|
||||
|
||||
__all__ = [
|
||||
'represent',
|
||||
'rep_innerproduct',
|
||||
'rep_expectation',
|
||||
'integrate_result',
|
||||
'get_basis',
|
||||
'enumerate_states'
|
||||
]
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Represent
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _sympy_to_scalar(e):
|
||||
"""Convert from a SymPy scalar to a Python scalar."""
|
||||
if isinstance(e, Expr):
|
||||
if e.is_Integer:
|
||||
return int(e)
|
||||
elif e.is_Float:
|
||||
return float(e)
|
||||
elif e.is_Rational:
|
||||
return float(e)
|
||||
elif e.is_Number or e.is_NumberSymbol or e == I:
|
||||
return complex(e)
|
||||
raise TypeError('Expected number, got: %r' % e)
|
||||
|
||||
|
||||
def represent(expr, **options):
|
||||
"""Represent the quantum expression in the given basis.
|
||||
|
||||
In quantum mechanics abstract states and operators can be represented in
|
||||
various basis sets. Under this operation the follow transforms happen:
|
||||
|
||||
* Ket -> column vector or function
|
||||
* Bra -> row vector of function
|
||||
* Operator -> matrix or differential operator
|
||||
|
||||
This function is the top-level interface for this action.
|
||||
|
||||
This function walks the SymPy expression tree looking for ``QExpr``
|
||||
instances that have a ``_represent`` method. This method is then called
|
||||
and the object is replaced by the representation returned by this method.
|
||||
By default, the ``_represent`` method will dispatch to other methods
|
||||
that handle the representation logic for a particular basis set. The
|
||||
naming convention for these methods is the following::
|
||||
|
||||
def _represent_FooBasis(self, e, basis, **options)
|
||||
|
||||
This function will have the logic for representing instances of its class
|
||||
in the basis set having a class named ``FooBasis``.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
expr : Expr
|
||||
The expression to represent.
|
||||
basis : Operator, basis set
|
||||
An object that contains the information about the basis set. If an
|
||||
operator is used, the basis is assumed to be the orthonormal
|
||||
eigenvectors of that operator. In general though, the basis argument
|
||||
can be any object that contains the basis set information.
|
||||
options : dict
|
||||
Key/value pairs of options that are passed to the underlying method
|
||||
that finds the representation. These options can be used to
|
||||
control how the representation is done. For example, this is where
|
||||
the size of the basis set would be set.
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
e : Expr
|
||||
The SymPy expression of the represented quantum expression.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
Here we subclass ``Operator`` and ``Ket`` to create the z-spin operator
|
||||
and its spin 1/2 up eigenstate. By defining the ``_represent_SzOp``
|
||||
method, the ket can be represented in the z-spin basis.
|
||||
|
||||
>>> from sympy.physics.quantum import Operator, represent, Ket
|
||||
>>> from sympy import Matrix
|
||||
|
||||
>>> class SzUpKet(Ket):
|
||||
... def _represent_SzOp(self, basis, **options):
|
||||
... return Matrix([1,0])
|
||||
...
|
||||
>>> class SzOp(Operator):
|
||||
... pass
|
||||
...
|
||||
>>> sz = SzOp('Sz')
|
||||
>>> up = SzUpKet('up')
|
||||
>>> represent(up, basis=sz)
|
||||
Matrix([
|
||||
[1],
|
||||
[0]])
|
||||
|
||||
Here we see an example of representations in a continuous
|
||||
basis. We see that the result of representing various combinations
|
||||
of cartesian position operators and kets give us continuous
|
||||
expressions involving DiracDelta functions.
|
||||
|
||||
>>> from sympy.physics.quantum.cartesian import XOp, XKet, XBra
|
||||
>>> X = XOp()
|
||||
>>> x = XKet()
|
||||
>>> y = XBra('y')
|
||||
>>> represent(X*x)
|
||||
x*DiracDelta(x - x_2)
|
||||
"""
|
||||
|
||||
format = options.get('format', 'sympy')
|
||||
if format == 'numpy':
|
||||
import numpy as np
|
||||
if isinstance(expr, QExpr) and not isinstance(expr, OuterProduct):
|
||||
options['replace_none'] = False
|
||||
temp_basis = get_basis(expr, **options)
|
||||
if temp_basis is not None:
|
||||
options['basis'] = temp_basis
|
||||
try:
|
||||
return expr._represent(**options)
|
||||
except NotImplementedError as strerr:
|
||||
#If no _represent_FOO method exists, map to the
|
||||
#appropriate basis state and try
|
||||
#the other methods of representation
|
||||
options['replace_none'] = True
|
||||
|
||||
if isinstance(expr, (KetBase, BraBase)):
|
||||
try:
|
||||
return rep_innerproduct(expr, **options)
|
||||
except NotImplementedError:
|
||||
raise NotImplementedError(strerr)
|
||||
elif isinstance(expr, Operator):
|
||||
try:
|
||||
return rep_expectation(expr, **options)
|
||||
except NotImplementedError:
|
||||
raise NotImplementedError(strerr)
|
||||
else:
|
||||
raise NotImplementedError(strerr)
|
||||
elif isinstance(expr, Add):
|
||||
result = represent(expr.args[0], **options)
|
||||
for args in expr.args[1:]:
|
||||
# scipy.sparse doesn't support += so we use plain = here.
|
||||
result = result + represent(args, **options)
|
||||
return result
|
||||
elif isinstance(expr, Pow):
|
||||
base, exp = expr.as_base_exp()
|
||||
if format in ('numpy', 'scipy.sparse'):
|
||||
exp = _sympy_to_scalar(exp)
|
||||
base = represent(base, **options)
|
||||
# scipy.sparse doesn't support negative exponents
|
||||
# and warns when inverting a matrix in csr format.
|
||||
if format == 'scipy.sparse' and exp < 0:
|
||||
from scipy.sparse.linalg import inv
|
||||
exp = - exp
|
||||
base = inv(base.tocsc()).tocsr()
|
||||
if format == 'numpy':
|
||||
return np.linalg.matrix_power(base, exp)
|
||||
return base ** exp
|
||||
elif isinstance(expr, TensorProduct):
|
||||
new_args = [represent(arg, **options) for arg in expr.args]
|
||||
return TensorProduct(*new_args)
|
||||
elif isinstance(expr, Dagger):
|
||||
return Dagger(represent(expr.args[0], **options))
|
||||
elif isinstance(expr, Commutator):
|
||||
A = expr.args[0]
|
||||
B = expr.args[1]
|
||||
return represent(Mul(A, B) - Mul(B, A), **options)
|
||||
elif isinstance(expr, AntiCommutator):
|
||||
A = expr.args[0]
|
||||
B = expr.args[1]
|
||||
return represent(Mul(A, B) + Mul(B, A), **options)
|
||||
elif not isinstance(expr, (Mul, OuterProduct, InnerProduct)):
|
||||
# We have removed special handling of inner products that used to be
|
||||
# required (before automatic transforms).
|
||||
# For numpy and scipy.sparse, we can only handle numerical prefactors.
|
||||
if format in ('numpy', 'scipy.sparse'):
|
||||
return _sympy_to_scalar(expr)
|
||||
return expr
|
||||
|
||||
if not isinstance(expr, (Mul, OuterProduct, InnerProduct)):
|
||||
raise TypeError('Mul expected, got: %r' % expr)
|
||||
|
||||
if "index" in options:
|
||||
options["index"] += 1
|
||||
else:
|
||||
options["index"] = 1
|
||||
|
||||
if "unities" not in options:
|
||||
options["unities"] = []
|
||||
|
||||
result = represent(expr.args[-1], **options)
|
||||
last_arg = expr.args[-1]
|
||||
|
||||
for arg in reversed(expr.args[:-1]):
|
||||
if isinstance(last_arg, Operator):
|
||||
options["index"] += 1
|
||||
options["unities"].append(options["index"])
|
||||
elif isinstance(last_arg, BraBase) and isinstance(arg, KetBase):
|
||||
options["index"] += 1
|
||||
elif isinstance(last_arg, KetBase) and isinstance(arg, Operator):
|
||||
options["unities"].append(options["index"])
|
||||
elif isinstance(last_arg, KetBase) and isinstance(arg, BraBase):
|
||||
options["unities"].append(options["index"])
|
||||
|
||||
next_arg = represent(arg, **options)
|
||||
if format == 'numpy' and isinstance(next_arg, np.ndarray):
|
||||
# Must use np.matmult to "matrix multiply" two np.ndarray
|
||||
result = np.matmul(next_arg, result)
|
||||
else:
|
||||
result = next_arg*result
|
||||
last_arg = arg
|
||||
|
||||
# All three matrix formats create 1 by 1 matrices when inner products of
|
||||
# vectors are taken. In these cases, we simply return a scalar.
|
||||
result = flatten_scalar(result)
|
||||
|
||||
result = integrate_result(expr, result, **options)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def rep_innerproduct(expr, **options):
|
||||
"""
|
||||
Returns an innerproduct like representation (e.g. ``<x'|x>``) for the
|
||||
given state.
|
||||
|
||||
Attempts to calculate inner product with a bra from the specified
|
||||
basis. Should only be passed an instance of KetBase or BraBase
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
expr : KetBase or BraBase
|
||||
The expression to be represented
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.quantum.represent import rep_innerproduct
|
||||
>>> from sympy.physics.quantum.cartesian import XOp, XKet, PxOp, PxKet
|
||||
>>> rep_innerproduct(XKet())
|
||||
DiracDelta(x - x_1)
|
||||
>>> rep_innerproduct(XKet(), basis=PxOp())
|
||||
sqrt(2)*exp(-I*px_1*x/hbar)/(2*sqrt(hbar)*sqrt(pi))
|
||||
>>> rep_innerproduct(PxKet(), basis=XOp())
|
||||
sqrt(2)*exp(I*px*x_1/hbar)/(2*sqrt(hbar)*sqrt(pi))
|
||||
|
||||
"""
|
||||
|
||||
if not isinstance(expr, (KetBase, BraBase)):
|
||||
raise TypeError("expr passed is not a Bra or Ket")
|
||||
|
||||
basis = get_basis(expr, **options)
|
||||
|
||||
if not isinstance(basis, StateBase):
|
||||
raise NotImplementedError("Can't form this representation!")
|
||||
|
||||
if "index" not in options:
|
||||
options["index"] = 1
|
||||
|
||||
basis_kets = enumerate_states(basis, options["index"], 2)
|
||||
|
||||
if isinstance(expr, BraBase):
|
||||
bra = expr
|
||||
ket = (basis_kets[1] if basis_kets[0].dual == expr else basis_kets[0])
|
||||
else:
|
||||
bra = (basis_kets[1].dual if basis_kets[0]
|
||||
== expr else basis_kets[0].dual)
|
||||
ket = expr
|
||||
|
||||
prod = InnerProduct(bra, ket)
|
||||
result = prod.doit()
|
||||
|
||||
format = options.get('format', 'sympy')
|
||||
result = expr._format_represent(result, format)
|
||||
return result
|
||||
|
||||
|
||||
def rep_expectation(expr, **options):
|
||||
"""
|
||||
Returns an ``<x'|A|x>`` type representation for the given operator.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
expr : Operator
|
||||
Operator to be represented in the specified basis
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.quantum.cartesian import XOp, PxOp, PxKet
|
||||
>>> from sympy.physics.quantum.represent import rep_expectation
|
||||
>>> rep_expectation(XOp())
|
||||
x_1*DiracDelta(x_1 - x_2)
|
||||
>>> rep_expectation(XOp(), basis=PxOp())
|
||||
<px_2|*X*|px_1>
|
||||
>>> rep_expectation(XOp(), basis=PxKet())
|
||||
<px_2|*X*|px_1>
|
||||
|
||||
"""
|
||||
|
||||
if "index" not in options:
|
||||
options["index"] = 1
|
||||
|
||||
if not isinstance(expr, Operator):
|
||||
raise TypeError("The passed expression is not an operator")
|
||||
|
||||
basis_state = get_basis(expr, **options)
|
||||
|
||||
if basis_state is None or not isinstance(basis_state, StateBase):
|
||||
raise NotImplementedError("Could not get basis kets for this operator")
|
||||
|
||||
basis_kets = enumerate_states(basis_state, options["index"], 2)
|
||||
|
||||
bra = basis_kets[1].dual
|
||||
ket = basis_kets[0]
|
||||
|
||||
result = qapply(bra*expr*ket)
|
||||
return result
|
||||
|
||||
|
||||
def integrate_result(orig_expr, result, **options):
|
||||
"""
|
||||
Returns the result of integrating over any unities ``(|x><x|)`` in
|
||||
the given expression. Intended for integrating over the result of
|
||||
representations in continuous bases.
|
||||
|
||||
This function integrates over any unities that may have been
|
||||
inserted into the quantum expression and returns the result.
|
||||
It uses the interval of the Hilbert space of the basis state
|
||||
passed to it in order to figure out the limits of integration.
|
||||
The unities option must be
|
||||
specified for this to work.
|
||||
|
||||
Note: This is mostly used internally by represent(). Examples are
|
||||
given merely to show the use cases.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
orig_expr : quantum expression
|
||||
The original expression which was to be represented
|
||||
|
||||
result: Expr
|
||||
The resulting representation that we wish to integrate over
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import symbols, DiracDelta
|
||||
>>> from sympy.physics.quantum.represent import integrate_result
|
||||
>>> from sympy.physics.quantum.cartesian import XOp, XKet
|
||||
>>> x_ket = XKet()
|
||||
>>> X_op = XOp()
|
||||
>>> x, x_1, x_2 = symbols('x, x_1, x_2')
|
||||
>>> integrate_result(X_op*x_ket, x*DiracDelta(x-x_1)*DiracDelta(x_1-x_2))
|
||||
x*DiracDelta(x - x_1)*DiracDelta(x_1 - x_2)
|
||||
>>> integrate_result(X_op*x_ket, x*DiracDelta(x-x_1)*DiracDelta(x_1-x_2),
|
||||
... unities=[1])
|
||||
x*DiracDelta(x - x_2)
|
||||
|
||||
"""
|
||||
if not isinstance(result, Expr):
|
||||
return result
|
||||
|
||||
options['replace_none'] = True
|
||||
if "basis" not in options:
|
||||
arg = orig_expr.args[-1]
|
||||
options["basis"] = get_basis(arg, **options)
|
||||
elif not isinstance(options["basis"], StateBase):
|
||||
options["basis"] = get_basis(orig_expr, **options)
|
||||
|
||||
basis = options.pop("basis", None)
|
||||
|
||||
if basis is None:
|
||||
return result
|
||||
|
||||
unities = options.pop("unities", [])
|
||||
|
||||
if len(unities) == 0:
|
||||
return result
|
||||
|
||||
kets = enumerate_states(basis, unities)
|
||||
coords = [k.label[0] for k in kets]
|
||||
|
||||
for coord in coords:
|
||||
if coord in result.free_symbols:
|
||||
#TODO: Add support for sets of operators
|
||||
basis_op = state_to_operators(basis)
|
||||
start = basis_op.hilbert_space.interval.start
|
||||
end = basis_op.hilbert_space.interval.end
|
||||
result = integrate(result, (coord, start, end))
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def get_basis(expr, *, basis=None, replace_none=True, **options):
|
||||
"""
|
||||
Returns a basis state instance corresponding to the basis specified in
|
||||
options=s. If no basis is specified, the function tries to form a default
|
||||
basis state of the given expression.
|
||||
|
||||
There are three behaviors:
|
||||
|
||||
1. The basis specified in options is already an instance of StateBase. If
|
||||
this is the case, it is simply returned. If the class is specified but
|
||||
not an instance, a default instance is returned.
|
||||
|
||||
2. The basis specified is an operator or set of operators. If this
|
||||
is the case, the operator_to_state mapping method is used.
|
||||
|
||||
3. No basis is specified. If expr is a state, then a default instance of
|
||||
its class is returned. If expr is an operator, then it is mapped to the
|
||||
corresponding state. If it is neither, then we cannot obtain the basis
|
||||
state.
|
||||
|
||||
If the basis cannot be mapped, then it is not changed.
|
||||
|
||||
This will be called from within represent, and represent will
|
||||
only pass QExpr's.
|
||||
|
||||
TODO (?): Support for Muls and other types of expressions?
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
expr : Operator or StateBase
|
||||
Expression whose basis is sought
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.quantum.represent import get_basis
|
||||
>>> from sympy.physics.quantum.cartesian import XOp, XKet, PxOp, PxKet
|
||||
>>> x = XKet()
|
||||
>>> X = XOp()
|
||||
>>> get_basis(x)
|
||||
|x>
|
||||
>>> get_basis(X)
|
||||
|x>
|
||||
>>> get_basis(x, basis=PxOp())
|
||||
|px>
|
||||
>>> get_basis(x, basis=PxKet)
|
||||
|px>
|
||||
|
||||
"""
|
||||
|
||||
if basis is None and not replace_none:
|
||||
return None
|
||||
|
||||
if basis is None:
|
||||
if isinstance(expr, KetBase):
|
||||
return _make_default(expr.__class__)
|
||||
elif isinstance(expr, BraBase):
|
||||
return _make_default(expr.dual_class())
|
||||
elif isinstance(expr, Operator):
|
||||
state_inst = operators_to_state(expr)
|
||||
return (state_inst if state_inst is not None else None)
|
||||
else:
|
||||
return None
|
||||
elif (isinstance(basis, Operator) or
|
||||
(not isinstance(basis, StateBase) and issubclass(basis, Operator))):
|
||||
state = operators_to_state(basis)
|
||||
if state is None:
|
||||
return None
|
||||
elif isinstance(state, StateBase):
|
||||
return state
|
||||
else:
|
||||
return _make_default(state)
|
||||
elif isinstance(basis, StateBase):
|
||||
return basis
|
||||
elif issubclass(basis, StateBase):
|
||||
return _make_default(basis)
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def _make_default(expr):
|
||||
# XXX: Catching TypeError like this is a bad way of distinguishing
|
||||
# instances from classes. The logic using this function should be
|
||||
# rewritten somehow.
|
||||
try:
|
||||
expr = expr()
|
||||
except TypeError:
|
||||
return expr
|
||||
|
||||
return expr
|
||||
|
||||
|
||||
def enumerate_states(*args, **options):
|
||||
"""
|
||||
Returns instances of the given state with dummy indices appended
|
||||
|
||||
Operates in two different modes:
|
||||
|
||||
1. Two arguments are passed to it. The first is the base state which is to
|
||||
be indexed, and the second argument is a list of indices to append.
|
||||
|
||||
2. Three arguments are passed. The first is again the base state to be
|
||||
indexed. The second is the start index for counting. The final argument
|
||||
is the number of kets you wish to receive.
|
||||
|
||||
Tries to call state._enumerate_state. If this fails, returns an empty list
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
args : list
|
||||
See list of operation modes above for explanation
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.quantum.cartesian import XBra, XKet
|
||||
>>> from sympy.physics.quantum.represent import enumerate_states
|
||||
>>> test = XKet('foo')
|
||||
>>> enumerate_states(test, 1, 3)
|
||||
[|foo_1>, |foo_2>, |foo_3>]
|
||||
>>> test2 = XBra('bar')
|
||||
>>> enumerate_states(test2, [4, 5, 10])
|
||||
[<bar_4|, <bar_5|, <bar_10|]
|
||||
|
||||
"""
|
||||
|
||||
state = args[0]
|
||||
|
||||
if len(args) not in (2, 3):
|
||||
raise NotImplementedError("Wrong number of arguments!")
|
||||
|
||||
if not isinstance(state, StateBase):
|
||||
raise TypeError("First argument is not a state!")
|
||||
|
||||
if len(args) == 3:
|
||||
num_states = args[2]
|
||||
options['start_index'] = args[1]
|
||||
else:
|
||||
num_states = len(args[1])
|
||||
options['index_list'] = args[1]
|
||||
|
||||
try:
|
||||
ret = state._enumerate_state(num_states, **options)
|
||||
except NotImplementedError:
|
||||
ret = []
|
||||
|
||||
return ret
|
||||
Reference in New Issue
Block a user