| """Simple Harmonic Oscillator 1-Dimension""" |
|
|
| from sympy.core.numbers import (I, Integer) |
| from sympy.core.singleton import S |
| from sympy.core.symbol import Symbol |
| from sympy.functions.elementary.miscellaneous import sqrt |
| from sympy.physics.quantum.constants import hbar |
| from sympy.physics.quantum.operator import Operator |
| from sympy.physics.quantum.state import Bra, Ket, State |
| from sympy.physics.quantum.qexpr import QExpr |
| from sympy.physics.quantum.cartesian import X, Px |
| from sympy.functions.special.tensor_functions import KroneckerDelta |
| from sympy.physics.quantum.hilbert import ComplexSpace |
| from sympy.physics.quantum.matrixutils import matrix_zeros |
|
|
| |
|
|
| class SHOOp(Operator): |
| """A base class for the SHO Operators. |
| |
| We are limiting the number of arguments to be 1. |
| |
| """ |
|
|
| @classmethod |
| def _eval_args(cls, args): |
| args = QExpr._eval_args(args) |
| if len(args) == 1: |
| return args |
| else: |
| raise ValueError("Too many arguments") |
|
|
| @classmethod |
| def _eval_hilbert_space(cls, label): |
| return ComplexSpace(S.Infinity) |
|
|
| class RaisingOp(SHOOp): |
| """The Raising Operator or a^dagger. |
| |
| When a^dagger acts on a state it raises the state up by one. Taking |
| the adjoint of a^dagger returns 'a', the Lowering Operator. a^dagger |
| can be rewritten in terms of position and momentum. We can represent |
| a^dagger as a matrix, which will be its default basis. |
| |
| Parameters |
| ========== |
| |
| args : tuple |
| The list of numbers or parameters that uniquely specify the |
| operator. |
| |
| Examples |
| ======== |
| |
| Create a Raising Operator and rewrite it in terms of position and |
| momentum, and show that taking its adjoint returns 'a': |
| |
| >>> from sympy.physics.quantum.sho1d import RaisingOp |
| >>> from sympy.physics.quantum import Dagger |
| |
| >>> ad = RaisingOp('a') |
| >>> ad.rewrite('xp').doit() |
| sqrt(2)*(m*omega*X - I*Px)/(2*sqrt(hbar)*sqrt(m*omega)) |
| |
| >>> Dagger(ad) |
| a |
| |
| Taking the commutator of a^dagger with other Operators: |
| |
| >>> from sympy.physics.quantum import Commutator |
| >>> from sympy.physics.quantum.sho1d import RaisingOp, LoweringOp |
| >>> from sympy.physics.quantum.sho1d import NumberOp |
| |
| >>> ad = RaisingOp('a') |
| >>> a = LoweringOp('a') |
| >>> N = NumberOp('N') |
| >>> Commutator(ad, a).doit() |
| -1 |
| >>> Commutator(ad, N).doit() |
| -RaisingOp(a) |
| |
| Apply a^dagger to a state: |
| |
| >>> from sympy.physics.quantum import qapply |
| >>> from sympy.physics.quantum.sho1d import RaisingOp, SHOKet |
| |
| >>> ad = RaisingOp('a') |
| >>> k = SHOKet('k') |
| >>> qapply(ad*k) |
| sqrt(k + 1)*|k + 1> |
| |
| Matrix Representation |
| |
| >>> from sympy.physics.quantum.sho1d import RaisingOp |
| >>> from sympy.physics.quantum.represent import represent |
| >>> ad = RaisingOp('a') |
| >>> represent(ad, basis=N, ndim=4, format='sympy') |
| Matrix([ |
| [0, 0, 0, 0], |
| [1, 0, 0, 0], |
| [0, sqrt(2), 0, 0], |
| [0, 0, sqrt(3), 0]]) |
| |
| """ |
|
|
| def _eval_rewrite_as_xp(self, *args, **kwargs): |
| return (S.One/sqrt(Integer(2)*hbar*m*omega))*( |
| S.NegativeOne*I*Px + m*omega*X) |
|
|
| def _eval_adjoint(self): |
| return LoweringOp(*self.args) |
|
|
| def _eval_commutator_LoweringOp(self, other): |
| return S.NegativeOne |
|
|
| def _eval_commutator_NumberOp(self, other): |
| return S.NegativeOne*self |
|
|
| def _apply_operator_SHOKet(self, ket, **options): |
| temp = ket.n + S.One |
| return sqrt(temp)*SHOKet(temp) |
|
|
| def _represent_default_basis(self, **options): |
| return self._represent_NumberOp(None, **options) |
|
|
| def _represent_XOp(self, basis, **options): |
| |
| |
| |
| |
| |
| raise NotImplementedError('Position representation is not implemented') |
|
|
| def _represent_NumberOp(self, basis, **options): |
| ndim_info = options.get('ndim', 4) |
| format = options.get('format','sympy') |
| matrix = matrix_zeros(ndim_info, ndim_info, **options) |
| for i in range(ndim_info - 1): |
| value = sqrt(i + 1) |
| if format == 'scipy.sparse': |
| value = float(value) |
| matrix[i + 1, i] = value |
| if format == 'scipy.sparse': |
| matrix = matrix.tocsr() |
| return matrix |
|
|
| |
| |
| |
|
|
| def _print_contents(self, printer, *args): |
| arg0 = printer._print(self.args[0], *args) |
| return '%s(%s)' % (self.__class__.__name__, arg0) |
|
|
| def _print_contents_pretty(self, printer, *args): |
| from sympy.printing.pretty.stringpict import prettyForm |
| pform = printer._print(self.args[0], *args) |
| pform = pform**prettyForm('\N{DAGGER}') |
| return pform |
|
|
| def _print_contents_latex(self, printer, *args): |
| arg = printer._print(self.args[0]) |
| return '%s^{\\dagger}' % arg |
|
|
| class LoweringOp(SHOOp): |
| """The Lowering Operator or 'a'. |
| |
| When 'a' acts on a state it lowers the state up by one. Taking |
| the adjoint of 'a' returns a^dagger, the Raising Operator. 'a' |
| can be rewritten in terms of position and momentum. We can |
| represent 'a' as a matrix, which will be its default basis. |
| |
| Parameters |
| ========== |
| |
| args : tuple |
| The list of numbers or parameters that uniquely specify the |
| operator. |
| |
| Examples |
| ======== |
| |
| Create a Lowering Operator and rewrite it in terms of position and |
| momentum, and show that taking its adjoint returns a^dagger: |
| |
| >>> from sympy.physics.quantum.sho1d import LoweringOp |
| >>> from sympy.physics.quantum import Dagger |
| |
| >>> a = LoweringOp('a') |
| >>> a.rewrite('xp').doit() |
| sqrt(2)*(m*omega*X + I*Px)/(2*sqrt(hbar)*sqrt(m*omega)) |
| |
| >>> Dagger(a) |
| RaisingOp(a) |
| |
| Taking the commutator of 'a' with other Operators: |
| |
| >>> from sympy.physics.quantum import Commutator |
| >>> from sympy.physics.quantum.sho1d import LoweringOp, RaisingOp |
| >>> from sympy.physics.quantum.sho1d import NumberOp |
| |
| >>> a = LoweringOp('a') |
| >>> ad = RaisingOp('a') |
| >>> N = NumberOp('N') |
| >>> Commutator(a, ad).doit() |
| 1 |
| >>> Commutator(a, N).doit() |
| a |
| |
| Apply 'a' to a state: |
| |
| >>> from sympy.physics.quantum import qapply |
| >>> from sympy.physics.quantum.sho1d import LoweringOp, SHOKet |
| |
| >>> a = LoweringOp('a') |
| >>> k = SHOKet('k') |
| >>> qapply(a*k) |
| sqrt(k)*|k - 1> |
| |
| Taking 'a' of the lowest state will return 0: |
| |
| >>> from sympy.physics.quantum import qapply |
| >>> from sympy.physics.quantum.sho1d import LoweringOp, SHOKet |
| |
| >>> a = LoweringOp('a') |
| >>> k = SHOKet(0) |
| >>> qapply(a*k) |
| 0 |
| |
| Matrix Representation |
| |
| >>> from sympy.physics.quantum.sho1d import LoweringOp |
| >>> from sympy.physics.quantum.represent import represent |
| >>> a = LoweringOp('a') |
| >>> represent(a, basis=N, ndim=4, format='sympy') |
| Matrix([ |
| [0, 1, 0, 0], |
| [0, 0, sqrt(2), 0], |
| [0, 0, 0, sqrt(3)], |
| [0, 0, 0, 0]]) |
| |
| """ |
|
|
| def _eval_rewrite_as_xp(self, *args, **kwargs): |
| return (S.One/sqrt(Integer(2)*hbar*m*omega))*( |
| I*Px + m*omega*X) |
|
|
| def _eval_adjoint(self): |
| return RaisingOp(*self.args) |
|
|
| def _eval_commutator_RaisingOp(self, other): |
| return S.One |
|
|
| def _eval_commutator_NumberOp(self, other): |
| return self |
|
|
| def _apply_operator_SHOKet(self, ket, **options): |
| temp = ket.n - Integer(1) |
| if ket.n is S.Zero: |
| return S.Zero |
| else: |
| return sqrt(ket.n)*SHOKet(temp) |
|
|
| def _represent_default_basis(self, **options): |
| return self._represent_NumberOp(None, **options) |
|
|
| def _represent_XOp(self, basis, **options): |
| |
| |
| |
| |
| |
| raise NotImplementedError('Position representation is not implemented') |
|
|
| def _represent_NumberOp(self, basis, **options): |
| ndim_info = options.get('ndim', 4) |
| format = options.get('format', 'sympy') |
| matrix = matrix_zeros(ndim_info, ndim_info, **options) |
| for i in range(ndim_info - 1): |
| value = sqrt(i + 1) |
| if format == 'scipy.sparse': |
| value = float(value) |
| matrix[i,i + 1] = value |
| if format == 'scipy.sparse': |
| matrix = matrix.tocsr() |
| return matrix |
|
|
|
|
| class NumberOp(SHOOp): |
| """The Number Operator is simply a^dagger*a |
| |
| It is often useful to write a^dagger*a as simply the Number Operator |
| because the Number Operator commutes with the Hamiltonian. And can be |
| expressed using the Number Operator. Also the Number Operator can be |
| applied to states. We can represent the Number Operator as a matrix, |
| which will be its default basis. |
| |
| Parameters |
| ========== |
| |
| args : tuple |
| The list of numbers or parameters that uniquely specify the |
| operator. |
| |
| Examples |
| ======== |
| |
| Create a Number Operator and rewrite it in terms of the ladder |
| operators, position and momentum operators, and Hamiltonian: |
| |
| >>> from sympy.physics.quantum.sho1d import NumberOp |
| |
| >>> N = NumberOp('N') |
| >>> N.rewrite('a').doit() |
| RaisingOp(a)*a |
| >>> N.rewrite('xp').doit() |
| -1/2 + (m**2*omega**2*X**2 + Px**2)/(2*hbar*m*omega) |
| >>> N.rewrite('H').doit() |
| -1/2 + H/(hbar*omega) |
| |
| Take the Commutator of the Number Operator with other Operators: |
| |
| >>> from sympy.physics.quantum import Commutator |
| >>> from sympy.physics.quantum.sho1d import NumberOp, Hamiltonian |
| >>> from sympy.physics.quantum.sho1d import RaisingOp, LoweringOp |
| |
| >>> N = NumberOp('N') |
| >>> H = Hamiltonian('H') |
| >>> ad = RaisingOp('a') |
| >>> a = LoweringOp('a') |
| >>> Commutator(N,H).doit() |
| 0 |
| >>> Commutator(N,ad).doit() |
| RaisingOp(a) |
| >>> Commutator(N,a).doit() |
| -a |
| |
| Apply the Number Operator to a state: |
| |
| >>> from sympy.physics.quantum import qapply |
| >>> from sympy.physics.quantum.sho1d import NumberOp, SHOKet |
| |
| >>> N = NumberOp('N') |
| >>> k = SHOKet('k') |
| >>> qapply(N*k) |
| k*|k> |
| |
| Matrix Representation |
| |
| >>> from sympy.physics.quantum.sho1d import NumberOp |
| >>> from sympy.physics.quantum.represent import represent |
| >>> N = NumberOp('N') |
| >>> represent(N, basis=N, ndim=4, format='sympy') |
| Matrix([ |
| [0, 0, 0, 0], |
| [0, 1, 0, 0], |
| [0, 0, 2, 0], |
| [0, 0, 0, 3]]) |
| |
| """ |
|
|
| def _eval_rewrite_as_a(self, *args, **kwargs): |
| return ad*a |
|
|
| def _eval_rewrite_as_xp(self, *args, **kwargs): |
| return (S.One/(Integer(2)*m*hbar*omega))*(Px**2 + ( |
| m*omega*X)**2) - S.Half |
|
|
| def _eval_rewrite_as_H(self, *args, **kwargs): |
| return H/(hbar*omega) - S.Half |
|
|
| def _apply_operator_SHOKet(self, ket, **options): |
| return ket.n*ket |
|
|
| def _eval_commutator_Hamiltonian(self, other): |
| return S.Zero |
|
|
| def _eval_commutator_RaisingOp(self, other): |
| return other |
|
|
| def _eval_commutator_LoweringOp(self, other): |
| return S.NegativeOne*other |
|
|
| def _represent_default_basis(self, **options): |
| return self._represent_NumberOp(None, **options) |
|
|
| def _represent_XOp(self, basis, **options): |
| |
| |
| |
| |
| |
| raise NotImplementedError('Position representation is not implemented') |
|
|
| def _represent_NumberOp(self, basis, **options): |
| ndim_info = options.get('ndim', 4) |
| format = options.get('format', 'sympy') |
| matrix = matrix_zeros(ndim_info, ndim_info, **options) |
| for i in range(ndim_info): |
| value = i |
| if format == 'scipy.sparse': |
| value = float(value) |
| matrix[i,i] = value |
| if format == 'scipy.sparse': |
| matrix = matrix.tocsr() |
| return matrix |
|
|
|
|
| class Hamiltonian(SHOOp): |
| """The Hamiltonian Operator. |
| |
| The Hamiltonian is used to solve the time-independent Schrodinger |
| equation. The Hamiltonian can be expressed using the ladder operators, |
| as well as by position and momentum. We can represent the Hamiltonian |
| Operator as a matrix, which will be its default basis. |
| |
| Parameters |
| ========== |
| |
| args : tuple |
| The list of numbers or parameters that uniquely specify the |
| operator. |
| |
| Examples |
| ======== |
| |
| Create a Hamiltonian Operator and rewrite it in terms of the ladder |
| operators, position and momentum, and the Number Operator: |
| |
| >>> from sympy.physics.quantum.sho1d import Hamiltonian |
| |
| >>> H = Hamiltonian('H') |
| >>> H.rewrite('a').doit() |
| hbar*omega*(1/2 + RaisingOp(a)*a) |
| >>> H.rewrite('xp').doit() |
| (m**2*omega**2*X**2 + Px**2)/(2*m) |
| >>> H.rewrite('N').doit() |
| hbar*omega*(1/2 + N) |
| |
| Take the Commutator of the Hamiltonian and the Number Operator: |
| |
| >>> from sympy.physics.quantum import Commutator |
| >>> from sympy.physics.quantum.sho1d import Hamiltonian, NumberOp |
| |
| >>> H = Hamiltonian('H') |
| >>> N = NumberOp('N') |
| >>> Commutator(H,N).doit() |
| 0 |
| |
| Apply the Hamiltonian Operator to a state: |
| |
| >>> from sympy.physics.quantum import qapply |
| >>> from sympy.physics.quantum.sho1d import Hamiltonian, SHOKet |
| |
| >>> H = Hamiltonian('H') |
| >>> k = SHOKet('k') |
| >>> qapply(H*k) |
| hbar*k*omega*|k> + hbar*omega*|k>/2 |
| |
| Matrix Representation |
| |
| >>> from sympy.physics.quantum.sho1d import Hamiltonian |
| >>> from sympy.physics.quantum.represent import represent |
| |
| >>> H = Hamiltonian('H') |
| >>> represent(H, basis=N, ndim=4, format='sympy') |
| Matrix([ |
| [hbar*omega/2, 0, 0, 0], |
| [ 0, 3*hbar*omega/2, 0, 0], |
| [ 0, 0, 5*hbar*omega/2, 0], |
| [ 0, 0, 0, 7*hbar*omega/2]]) |
| |
| """ |
|
|
| def _eval_rewrite_as_a(self, *args, **kwargs): |
| return hbar*omega*(ad*a + S.Half) |
|
|
| def _eval_rewrite_as_xp(self, *args, **kwargs): |
| return (S.One/(Integer(2)*m))*(Px**2 + (m*omega*X)**2) |
|
|
| def _eval_rewrite_as_N(self, *args, **kwargs): |
| return hbar*omega*(N + S.Half) |
|
|
| def _apply_operator_SHOKet(self, ket, **options): |
| return (hbar*omega*(ket.n + S.Half))*ket |
|
|
| def _eval_commutator_NumberOp(self, other): |
| return S.Zero |
|
|
| def _represent_default_basis(self, **options): |
| return self._represent_NumberOp(None, **options) |
|
|
| def _represent_XOp(self, basis, **options): |
| |
| |
| |
| |
| |
| raise NotImplementedError('Position representation is not implemented') |
|
|
| def _represent_NumberOp(self, basis, **options): |
| ndim_info = options.get('ndim', 4) |
| format = options.get('format', 'sympy') |
| matrix = matrix_zeros(ndim_info, ndim_info, **options) |
| for i in range(ndim_info): |
| value = i + S.Half |
| if format == 'scipy.sparse': |
| value = float(value) |
| matrix[i,i] = value |
| if format == 'scipy.sparse': |
| matrix = matrix.tocsr() |
| return hbar*omega*matrix |
|
|
| |
|
|
| class SHOState(State): |
| """State class for SHO states""" |
|
|
| @classmethod |
| def _eval_hilbert_space(cls, label): |
| return ComplexSpace(S.Infinity) |
|
|
| @property |
| def n(self): |
| return self.args[0] |
|
|
|
|
| class SHOKet(SHOState, Ket): |
| """1D eigenket. |
| |
| Inherits from SHOState and Ket. |
| |
| Parameters |
| ========== |
| |
| args : tuple |
| The list of numbers or parameters that uniquely specify the ket |
| This is usually its quantum numbers or its symbol. |
| |
| Examples |
| ======== |
| |
| Ket's know about their associated bra: |
| |
| >>> from sympy.physics.quantum.sho1d import SHOKet |
| |
| >>> k = SHOKet('k') |
| >>> k.dual |
| <k| |
| >>> k.dual_class() |
| <class 'sympy.physics.quantum.sho1d.SHOBra'> |
| |
| Take the Inner Product with a bra: |
| |
| >>> from sympy.physics.quantum import InnerProduct |
| >>> from sympy.physics.quantum.sho1d import SHOKet, SHOBra |
| |
| >>> k = SHOKet('k') |
| >>> b = SHOBra('b') |
| >>> InnerProduct(b,k).doit() |
| KroneckerDelta(b, k) |
| |
| Vector representation of a numerical state ket: |
| |
| >>> from sympy.physics.quantum.sho1d import SHOKet, NumberOp |
| >>> from sympy.physics.quantum.represent import represent |
| |
| >>> k = SHOKet(3) |
| >>> N = NumberOp('N') |
| >>> represent(k, basis=N, ndim=4) |
| Matrix([ |
| [0], |
| [0], |
| [0], |
| [1]]) |
| |
| """ |
|
|
| @classmethod |
| def dual_class(self): |
| return SHOBra |
|
|
| def _eval_innerproduct_SHOBra(self, bra, **hints): |
| result = KroneckerDelta(self.n, bra.n) |
| return result |
|
|
| def _represent_default_basis(self, **options): |
| return self._represent_NumberOp(None, **options) |
|
|
| def _represent_NumberOp(self, basis, **options): |
| ndim_info = options.get('ndim', 4) |
| format = options.get('format', 'sympy') |
| options['spmatrix'] = 'lil' |
| vector = matrix_zeros(ndim_info, 1, **options) |
| if isinstance(self.n, Integer): |
| if self.n >= ndim_info: |
| return ValueError("N-Dimension too small") |
| if format == 'scipy.sparse': |
| vector[int(self.n), 0] = 1.0 |
| vector = vector.tocsr() |
| elif format == 'numpy': |
| vector[int(self.n), 0] = 1.0 |
| else: |
| vector[self.n, 0] = S.One |
| return vector |
| else: |
| return ValueError("Not Numerical State") |
|
|
|
|
| class SHOBra(SHOState, Bra): |
| """A time-independent Bra in SHO. |
| |
| Inherits from SHOState and Bra. |
| |
| Parameters |
| ========== |
| |
| args : tuple |
| The list of numbers or parameters that uniquely specify the ket |
| This is usually its quantum numbers or its symbol. |
| |
| Examples |
| ======== |
| |
| Bra's know about their associated ket: |
| |
| >>> from sympy.physics.quantum.sho1d import SHOBra |
| |
| >>> b = SHOBra('b') |
| >>> b.dual |
| |b> |
| >>> b.dual_class() |
| <class 'sympy.physics.quantum.sho1d.SHOKet'> |
| |
| Vector representation of a numerical state bra: |
| |
| >>> from sympy.physics.quantum.sho1d import SHOBra, NumberOp |
| >>> from sympy.physics.quantum.represent import represent |
| |
| >>> b = SHOBra(3) |
| >>> N = NumberOp('N') |
| >>> represent(b, basis=N, ndim=4) |
| Matrix([[0, 0, 0, 1]]) |
| |
| """ |
|
|
| @classmethod |
| def dual_class(self): |
| return SHOKet |
|
|
| def _represent_default_basis(self, **options): |
| return self._represent_NumberOp(None, **options) |
|
|
| def _represent_NumberOp(self, basis, **options): |
| ndim_info = options.get('ndim', 4) |
| format = options.get('format', 'sympy') |
| options['spmatrix'] = 'lil' |
| vector = matrix_zeros(1, ndim_info, **options) |
| if isinstance(self.n, Integer): |
| if self.n >= ndim_info: |
| return ValueError("N-Dimension too small") |
| if format == 'scipy.sparse': |
| vector[0, int(self.n)] = 1.0 |
| vector = vector.tocsr() |
| elif format == 'numpy': |
| vector[0, int(self.n)] = 1.0 |
| else: |
| vector[0, self.n] = S.One |
| return vector |
| else: |
| return ValueError("Not Numerical State") |
|
|
|
|
| ad = RaisingOp('a') |
| a = LoweringOp('a') |
| H = Hamiltonian('H') |
| N = NumberOp('N') |
| omega = Symbol('omega') |
| m = Symbol('m') |
|
|