def test_eval_trace(): # This test includes tests with dependencies between TensorProducts #and density operators. Since, the test is more to test the behavior of #TensorProducts it remains here A, B, C, D, E, F = symbols('A B C D E F', commutative=False) # Density with simple tensor products as args t = TensorProduct(A, B) d = Density([t, 1.0]) tr = Tr(d) assert tr.doit() == 1.0*Tr(A*Dagger(A))*Tr(B*Dagger(B)) ## partial trace with simple tensor products as args t = TensorProduct(A, B, C) d = Density([t, 1.0]) tr = Tr(d, [1]) assert tr.doit() == 1.0*A*Dagger(A)*Tr(B*Dagger(B))*C*Dagger(C) tr = Tr(d, [0, 2]) assert tr.doit() == 1.0*Tr(A*Dagger(A))*B*Dagger(B)*Tr(C*Dagger(C)) # Density with multiple Tensorproducts as states t2 = TensorProduct(A, B) t3 = TensorProduct(C, D) d = Density([t2, 0.5], [t3, 0.5]) t = Tr(d) assert t.doit() == (0.5*Tr(A*Dagger(A))*Tr(B*Dagger(B)) + 0.5*Tr(C*Dagger(C))*Tr(D*Dagger(D))) t = Tr(d, [0]) assert t.doit() == (0.5*Tr(A*Dagger(A))*B*Dagger(B) + 0.5*Tr(C*Dagger(C))*D*Dagger(D)) #Density with mixed states d = Density([t2 + t3, 1.0]) t = Tr(d) assert t.doit() == ( 1.0*Tr(A*Dagger(A))*Tr(B*Dagger(B)) + 1.0*Tr(A*Dagger(C))*Tr(B*Dagger(D)) + 1.0*Tr(C*Dagger(A))*Tr(D*Dagger(B)) + 1.0*Tr(C*Dagger(C))*Tr(D*Dagger(D))) t = Tr(d, [1] ) assert t.doit() == ( 1.0*A*Dagger(A)*Tr(B*Dagger(B)) + 1.0*A*Dagger(C)*Tr(B*Dagger(D)) + 1.0*C*Dagger(A)*Tr(D*Dagger(B)) + 1.0*C*Dagger(C)*Tr(D*Dagger(D)))
B = BOp('B') _tests = [ # Bra (b, Dagger(Avec)), (Dagger(b), Avec), # Ket (k, Avec), (Dagger(k), Dagger(Avec)), # Operator (A, Amat), (Dagger(A), Dagger(Amat)), # OuterProduct (OuterProduct(k, b), Avec * Avec.H), # TensorProduct (TensorProduct(A, B), matrix_tensor_product(Amat, Bmat)), # Pow (A**2, Amat**2), # Add/Mul (A * B + 2 * A, Amat * Bmat + 2 * Amat), # Commutator (Commutator(A, B), Amat * Bmat - Bmat * Amat), # AntiCommutator (AntiCommutator(A, B), Amat * Bmat + Bmat * Amat), # InnerProduct (InnerProduct(b, k), (Avec.H * Avec)[0]) ] def test_format_sympy(): for test in _tests:
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) >>> represent(X*x*y) x*DiracDelta(x - x_3)*DiracDelta(x_1 - y) """ format = options.get('format', 'sympy') 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 == 'numpy' or format == 'scipy.sparse': exp = _sympy_to_scalar(exp) return represent(base, **options)**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 = represent(expr.args[0], **options) B = represent(expr.args[1], **options) return A * B - B * A elif isinstance(expr, AntiCommutator): A = represent(expr.args[0], **options) B = represent(expr.args[1], **options) return A * B + B * A elif isinstance(expr, InnerProduct): return represent(Mul(expr.bra, expr.ket), **options) elif not (isinstance(expr, Mul) or isinstance(expr, OuterProduct)): # For numpy and scipy.sparse, we can only handle numerical prefactors. if format == 'numpy' or format == 'scipy.sparse': return _sympy_to_scalar(expr) return expr if not (isinstance(expr, Mul) or isinstance(expr, OuterProduct)): raise TypeError('Mul expected, got: %r' % expr) if "index" in options: options["index"] += 1 else: options["index"] = 1 if not "unities" 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"]) result = represent(arg, **options) * 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
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 == 'numpy' or format == 'scipy.sparse': exp = _sympy_to_scalar(exp) return represent(base, **options)**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 = represent(expr.args[0], **options) B = represent(expr.args[1], **options) return A*B - B*A elif isinstance(expr, AntiCommutator): A = represent(expr.args[0], **options) B = represent(expr.args[1], **options) return A*B + B*A elif isinstance(expr, InnerProduct): return represent(Mul(expr.bra,expr.ket), **options) elif not (isinstance(expr, Mul) or isinstance(expr, OuterProduct)): # For numpy and scipy.sparse, we can only handle numerical prefactors. if format == 'numpy' or format == 'scipy.sparse':
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 does 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 definining 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) [1] [0] """ format = options.get('format', 'sympy') if isinstance(expr, QExpr): return expr._represent(**options) 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): exp = expr.exp if format == 'numpy' or format == 'scipy.sparse': exp = _sympy_to_scalar(exp) return represent(expr.base, **options)**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 = represent(expr.args[0], **options) B = represent(expr.args[1], **options) return A * B - B * A elif isinstance(expr, AntiCommutator): A = represent(expr.args[0], **options) B = represent(expr.args[1], **options) return A * B + B * A elif isinstance(expr, InnerProduct): return represent(Mul(expr.bra, expr.ket), **options) elif not isinstance(expr, Mul): # For numpy and scipy.sparse, we can only handle numerical prefactors. if format == 'numpy' or format == 'scipy.sparse': return _sympy_to_scalar(expr) return expr if not isinstance(expr, Mul): raise TypeError('Mul expected, got: %r' % expr) result = represent(expr.args[-1], **options) for arg in reversed(expr.args[:-1]): result = represent(arg, **options) * result # 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) return result
def qapply(e, **options): """Apply operators to states in a quantum expression. Parameters ========== e : Expr The expression containing operators and states. This expression tree will be walked to find operators acting on states symbolically. options : dict A dict of key/value pairs that determine how the operator actions are carried out. The following options are valid: * ``dagger``: try to apply Dagger operators to the left (default: False). * ``ip_doit``: call ``.doit()`` in inner products when they are encountered (default: True). Returns ======= e : Expr The original expression, but with the operators applied to states. """ from sympy.physics.quantum.density import Density dagger = options.get('dagger', False) if e == 0: return S.Zero # This may be a bit aggressive but ensures that everything gets expanded # to its simplest form before trying to apply operators. This includes # things like (A+B+C)*|a> and A*(|a>+|b>) and all Commutators and # TensorProducts. The only problem with this is that if we can't apply # all the Operators, we have just expanded everything. # TODO: don't expand the scalars in front of each Mul. e = e.expand(commutator=True, tensorproduct=True) # If we just have a raw ket, return it. if isinstance(e, KetBase): return e # We have an Add(a, b, c, ...) and compute # Add(qapply(a), qapply(b), ...) elif isinstance(e, Add): result = 0 for arg in e.args: result += qapply(arg, **options) return result # For a Density operator call qapply on its state elif isinstance(e, Density): new_args = [(qapply(state, **options), prob) for (state, prob) in e.args] return Density(*new_args) # For a raw TensorProduct, call qapply on its args. elif isinstance(e, TensorProduct): return TensorProduct(*[qapply(t, **options) for t in e.args]) # For a Pow, call qapply on its base. elif isinstance(e, Pow): return qapply(e.base, **options)**e.exp # We have a Mul where there might be actual operators to apply to kets. elif isinstance(e, Mul): result = qapply_Mul(e, **options) if result == e and dagger: return Dagger(qapply_Mul(Dagger(e), **options)) else: return result # In all other cases (State, Operator, Pow, Commutator, InnerProduct, # OuterProduct) we won't ever have operators to apply to kets. else: return e
def couple(tp): """ Couple an uncoupled spin states This function can be used to couple an uncoupled tensor product of spin states. All of the eigenstates to be coupled must be of the same class. It will return a linear combination of eigenstates that are subclasses of CoupledSpinState. Parameters ========== tp: TensorProduct TensorProduct of spin states to be coupled Examples ======== Couple a tensor product of numerical states: >>> from sympy.physics.quantum.spin import JzKet, couple >>> from sympy.physics.quantum.tensorproduct import TensorProduct >>> couple(TensorProduct(JzKet(1,0), JzKet(1,1))) -sqrt(2)*|1,1,1,1>/2 + sqrt(2)*|2,1,1,1>/2 Couple a tensor product of symbolic states: >>> from sympy import symbols >>> j1,m1,j2,m2 = symbols('j1 m1 j2 m2') >>> couple(TensorProduct(JzKet(j1,m1), JzKet(j2,m2))) Sum(CG(j1, m1, j2, m2, j, m1 + m2)*|j,m1 + m2>, (j, 0, j1 + j2)) """ states = tp.args evect = states[0].__class__ if not all([arg.__class__ is evect for arg in states]): raise TypeError('All operands must be of the same class') evect = evect.coupled_class() if all(state.j.is_number for state in states): # Numerical coupling vect = TensorProduct(*[state._represent() for state in states]) maxj = states[0].j + states[1].j j1, j2 = states[0].j, states[1].j if maxj == int(maxj): minj = 0 else: minj = S(1)/2 result = [] for i in range(maxj-minj+1): j = maxj-i for k in range(2*j+1): m = j-k max_m1 = min(j1, m+j2) min_m1 = max(-j1, m-j2) min_m2 = m-max_m1 result.append(Add(*[vect[(j1-(max_m1-l))*(2*j2+1)+(j2-(min_m2+l)),0] * CG(j1,max_m1-l,j2,min_m2+l,j,m) * evect(j,m,j1,j2) for l in range(max_m1-min_m1+1)])) if all(state.m.is_number for state in states): return Add(*result).doit() else: return Add(*result) else: # Symbolic coupling maxj = Add(*[state.j for state in states]) m = Add(*[state.m for state in states]) j = symbols('j') if not maxj.is_number or maxj == int(maxj): minj = 0 else: minj = S(1)/2 j1 = states[0].j j2 = states[1].j m1 = states[0].m m2 = states[1].m return Sum(CG(j1,m1,j2,m2,j,m) * evect(j,m), (j,minj,maxj))
def test_doit(): x, y = symbols('x y') A, B, C, D, E, F = symbols('A B C D E F', commutative=False) d = Density([XKet(), 0.5], [PxKet(), 0.5]) assert (0.5*(PxKet()*Dagger(PxKet())) + 0.5*(XKet()*Dagger(XKet()))) == d.doit() # check for kets with expr in them d_with_sym = Density([XKet(x*y), 0.5], [PxKet(x*y), 0.5]) assert (0.5*(PxKet(x*y)*Dagger(PxKet(x*y))) + 0.5*(XKet(x*y)*Dagger(XKet(x*y)))) == d_with_sym.doit() d = Density([(A + B)*C, 1.0]) assert d.doit() == (1.0*A*C*Dagger(C)*Dagger(A) + 1.0*A*C*Dagger(C)*Dagger(B) + 1.0*B*C*Dagger(C)*Dagger(A) + 1.0*B*C*Dagger(C)*Dagger(B)) # With TensorProducts as args # Density with simple tensor products as args t = TensorProduct(A, B, C) d = Density([t, 1.0]) assert d.doit() == \ 1.0 * TensorProduct(A*Dagger(A), B*Dagger(B), C*Dagger(C)) # Density with multiple Tensorproducts as states t2 = TensorProduct(A, B) t3 = TensorProduct(C, D) d = Density([t2, 0.5], [t3, 0.5]) assert d.doit() == (0.5 * TensorProduct(A*Dagger(A), B*Dagger(B)) + 0.5 * TensorProduct(C*Dagger(C), D*Dagger(D))) #Density with mixed states d = Density([t2 + t3, 1.0]) assert d.doit() == (1.0 * TensorProduct(A*Dagger(A), B*Dagger(B)) + 1.0 * TensorProduct(A*Dagger(C), B*Dagger(D)) + 1.0 * TensorProduct(C*Dagger(A), D*Dagger(B)) + 1.0 * TensorProduct(C*Dagger(C), D*Dagger(D))) #Density operators with spin states tp1 = TensorProduct(JzKet(1, 1), JzKet(1, -1)) d = Density([tp1, 1]) # full trace t = Tr(d) assert t.doit() == 1 #Partial trace on density operators with spin states t = Tr(d, [0]) assert t.doit() == JzKet(1, -1) * Dagger(JzKet(1, -1)) t = Tr(d, [1]) assert t.doit() == JzKet(1, 1) * Dagger(JzKet(1, 1)) # with another spin state tp2 = TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2)) d = Density([tp2, 1]) #full trace t = Tr(d) assert t.doit() == 1 #Partial trace on density operators with spin states t = Tr(d, [0]) assert t.doit() == JzKet(S(1)/2, -S(1)/2) * Dagger(JzKet(S(1)/2, -S(1)/2)) t = Tr(d, [1]) assert t.doit() == JzKet(S(1)/2, S(1)/2) * Dagger(JzKet(S(1)/2, S(1)/2))
def test_issue_5923(): # most of the issue regarding sympification of args has been handled # and is tested internally by the use of args_cnc through the quantum # module, but the following is a test from the issue that used to raise. assert TensorProduct(1, Qubit('1')*Qubit('1').dual) == \ TensorProduct(1, OuterProduct(Qubit(1), QubitBra(1)))
def test_tensor_product_dagger(): assert Dagger(TensorProduct(I*A, B)) == \ -I*TensorProduct(Dagger(A), Dagger(B)) assert Dagger(TensorProduct(mat1, mat2)) == \ TensorProduct(Dagger(mat1), Dagger(mat2))
def test_big_expr(): f = Function('f') x = symbols('x') e1 = Dagger(AntiCommutator(Operator('A') + Operator('B'), Pow(DifferentialOperator(Derivative(f(x), x), f(x)), 3))*TensorProduct(Jz**2, Operator('A') + Operator('B')))*(JzBra(1, 0) + JzBra(1, 1))*(JzKet(0, 0) + JzKet(1, -1)) e2 = Commutator(Jz**2, Operator('A') + Operator('B'))*AntiCommutator(Dagger(Operator('C')*Operator('D')), Operator('E').inv()**2)*Dagger(Commutator(Jz, J2)) e3 = Wigner3j(1, 2, 3, 4, 5, 6)*TensorProduct(Commutator(Operator('A') + Dagger(Operator('B')), Operator('C') + Operator('D')), Jz - J2)*Dagger(OuterProduct(Dagger(JzBra(1, 1)), JzBra(1, 0)))*TensorProduct(JzKetCoupled(1, 1, (1, 1)) + JzKetCoupled(1, 0, (1, 1)), JzKetCoupled(1, -1, (1, 1))) e4 = (ComplexSpace(1)*ComplexSpace(2) + FockSpace()**2)*(L2(Interval( 0, oo)) + HilbertSpace()) assert str(e1) == '(Jz**2)x(Dagger(A) + Dagger(B))*{Dagger(DifferentialOperator(Derivative(f(x), x),f(x)))**3,Dagger(A) + Dagger(B)}*(<1,0| + <1,1|)*(|0,0> + |1,-1>)' ascii_str = \ """\ / 3 \\ \n\ |/ +\\ | \n\ 2 / + +\\ <| /d \\ | + +> \n\ /J \\ x \\A + B /*||DifferentialOperator|--(f(x)),f(x)| | ,A + B |*(<1,0| + <1,1|)*(|0,0> + |1,-1>)\n\ \\ z/ \\\\ \\dx / / / \ """ ucode_str = \ u("""\ ⎧ 3 ⎫ \n\ ⎪⎛ †⎞ ⎪ \n\ 2 ⎛ † †⎞ ⎨⎜ ⎛d ⎞ ⎟ † †⎬ \n\ ⎛J ⎞ ⨂ ⎝A + B ⎠⋅⎪⎜DifferentialOperator⎜──(f(x)),f(x)⎟ ⎟ ,A + B ⎪⋅(⟨1,0❘ + ⟨1,1❘)⋅(❘0,0⟩ + ❘1,-1⟩)\n\ ⎝ z⎠ ⎩⎝ ⎝dx ⎠ ⎠ ⎭ \ """) assert pretty(e1) == ascii_str assert upretty(e1) == ucode_str assert latex(e1) == \ r'{J_z^{2}}\otimes \left({A^{\dagger} + B^{\dagger}}\right) \left\{\left(DifferentialOperator\left(\frac{d}{d x} f{\left (x \right )},f{\left (x \right )}\right)^{\dagger}\right)^{3},A^{\dagger} + B^{\dagger}\right\} \left({\left\langle 1,0\right|} + {\left\langle 1,1\right|}\right) \left({\left|0,0\right\rangle } + {\left|1,-1\right\rangle }\right)' sT(e1, "Mul(TensorProduct(Pow(JzOp(Symbol('J')), Integer(2)), Add(Dagger(Operator(Symbol('A'))), Dagger(Operator(Symbol('B'))))), AntiCommutator(Pow(Dagger(DifferentialOperator(Derivative(Function('f')(Symbol('x')), Symbol('x')),Function('f')(Symbol('x')))), Integer(3)),Add(Dagger(Operator(Symbol('A'))), Dagger(Operator(Symbol('B'))))), Add(JzBra(Integer(1),Integer(0)), JzBra(Integer(1),Integer(1))), Add(JzKet(Integer(0),Integer(0)), JzKet(Integer(1),Integer(-1))))") assert str(e2) == '[Jz**2,A + B]*{E**(-2),Dagger(D)*Dagger(C)}*[J2,Jz]' ascii_str = \ """\ [ 2 ] / -2 + +\\ [ 2 ]\n\ [/J \\ ,A + B]*<E ,D *C >*[J ,J ]\n\ [\\ z/ ] \\ / [ z]\ """ ucode_str = \ u("""\ ⎡ 2 ⎤ ⎧ -2 † †⎫ ⎡ 2 ⎤\n\ ⎢⎛J ⎞ ,A + B⎥⋅⎨E ,D ⋅C ⎬⋅⎢J ,J ⎥\n\ ⎣⎝ z⎠ ⎦ ⎩ ⎭ ⎣ z⎦\ """) assert pretty(e2) == ascii_str assert upretty(e2) == ucode_str assert latex(e2) == \ r'\left[J_z^{2},A + B\right] \left\{E^{-2},D^{\dagger} C^{\dagger}\right\} \left[J^2,J_z\right]' sT(e2, "Mul(Commutator(Pow(JzOp(Symbol('J')), Integer(2)),Add(Operator(Symbol('A')), Operator(Symbol('B')))), AntiCommutator(Pow(Operator(Symbol('E')), Integer(-2)),Mul(Dagger(Operator(Symbol('D'))), Dagger(Operator(Symbol('C'))))), Commutator(J2Op(Symbol('J')),JzOp(Symbol('J'))))") assert str(e3) == \ "Wigner3j(1, 2, 3, 4, 5, 6)*[Dagger(B) + A,C + D]x(-J2 + Jz)*|1,0><1,1|*(|1,0,j1=1,j2=1> + |1,1,j1=1,j2=1>)x|1,-1,j1=1,j2=1>" ascii_str = \ """\ [ + ] / 2 \\ \n\ /1 3 5\\*[B + A,C + D]x |- J + J |*|1,0><1,1|*(|1,0,j1=1,j2=1> + |1,1,j1=1,j2=1>)x |1,-1,j1=1,j2=1>\n\ | | \\ z/ \n\ \\2 4 6/ \ """ ucode_str = \ u("""\ ⎡ † ⎤ ⎛ 2 ⎞ \n\ ⎛1 3 5⎞⋅⎣B + A,C + D⎦⨂ ⎜- J + J ⎟⋅❘1,0⟩⟨1,1❘⋅(❘1,0,j₁=1,j₂=1⟩ + ❘1,1,j₁=1,j₂=1⟩)⨂ ❘1,-1,j₁=1,j₂=1⟩\n\ ⎜ ⎟ ⎝ z⎠ \n\ ⎝2 4 6⎠ \ """) assert pretty(e3) == ascii_str assert upretty(e3) == ucode_str assert latex(e3) == \ r'\left(\begin{array}{ccc} 1 & 3 & 5 \\ 2 & 4 & 6 \end{array}\right) {\left[B^{\dagger} + A,C + D\right]}\otimes \left({- J^2 + J_z}\right) {\left|1,0\right\rangle }{\left\langle 1,1\right|} \left({{\left|1,0,j_{1}=1,j_{2}=1\right\rangle } + {\left|1,1,j_{1}=1,j_{2}=1\right\rangle }}\right)\otimes {{\left|1,-1,j_{1}=1,j_{2}=1\right\rangle }}' sT(e3, "Mul(Wigner3j(Integer(1), Integer(2), Integer(3), Integer(4), Integer(5), Integer(6)), TensorProduct(Commutator(Add(Dagger(Operator(Symbol('B'))), Operator(Symbol('A'))),Add(Operator(Symbol('C')), Operator(Symbol('D')))), Add(Mul(Integer(-1), J2Op(Symbol('J'))), JzOp(Symbol('J')))), OuterProduct(JzKet(Integer(1),Integer(0)),JzBra(Integer(1),Integer(1))), TensorProduct(Add(JzKetCoupled(Integer(1),Integer(0),Tuple(Integer(1), Integer(1)),Tuple(Tuple(Integer(1), Integer(2), Integer(1)))), JzKetCoupled(Integer(1),Integer(1),Tuple(Integer(1), Integer(1)),Tuple(Tuple(Integer(1), Integer(2), Integer(1))))), JzKetCoupled(Integer(1),Integer(-1),Tuple(Integer(1), Integer(1)),Tuple(Tuple(Integer(1), Integer(2), Integer(1))))))") assert str(e4) == '(C(1)*C(2)+F**2)*(L2(Interval(0, oo))+H)' ascii_str = \ """\ // 1 2\\ x2\\ / 2 \\\n\ \\\\C x C / + F / x \\L + H/\ """ ucode_str = \ u("""\ ⎛⎛ 1 2⎞ ⨂2⎞ ⎛ 2 ⎞\n\ ⎝⎝C ⨂ C ⎠ ⊕ F ⎠ ⨂ ⎝L ⊕ H⎠\ """) assert pretty(e4) == ascii_str assert upretty(e4) == ucode_str assert latex(e4) == \ r'\left(\left(\mathcal{C}^{1}\otimes \mathcal{C}^{2}\right)\oplus {\mathcal{F}}^{\otimes 2}\right)\otimes \left({\mathcal{L}^2}\left( \left[0, \infty\right) \right)\oplus \mathcal{H}\right)' sT(e4, "TensorProductHilbertSpace((DirectSumHilbertSpace(TensorProductHilbertSpace(ComplexSpace(Integer(1)),ComplexSpace(Integer(2))),TensorPowerHilbertSpace(FockSpace(),Integer(2)))),(DirectSumHilbertSpace(L2(Interval(Integer(0), oo, S.false, S.true)),HilbertSpace())))")
def test_sympy__physics__quantum__tensorproduct__TensorProduct(): from sympy.physics.quantum.tensorproduct import TensorProduct assert _test_args(TensorProduct(x, y))
def test_tensorproduct(): a = BosonOp("a") b = BosonOp("b") ket1 = TensorProduct(BosonFockKet(1), BosonFockKet(2)) ket2 = TensorProduct(BosonFockKet(0), BosonFockKet(0)) ket3 = TensorProduct(BosonFockKet(0), BosonFockKet(2)) bra1 = TensorProduct(BosonFockBra(0), BosonFockBra(0)) bra2 = TensorProduct(BosonFockBra(1), BosonFockBra(2)) assert qapply(TensorProduct(a, b ** 2) * ket1) == sqrt(2) * ket2 assert qapply(TensorProduct(a, Dagger(b) * b) * ket1) == 2 * ket3 assert qapply(bra1 * TensorProduct(a, b * b), dagger=True) == sqrt(2) * bra2 assert qapply(bra2 * ket1).doit() == TensorProduct(1, 1) assert qapply(TensorProduct(a, b * b) * ket1) == sqrt(2) * ket2 assert qapply(Dagger(TensorProduct(a, b * b) * ket1), dagger=True) == sqrt(2) * Dagger(ket2)
def test_issue3044(): expr1 = TensorProduct(Jz * JzKet(S(2), S.NegativeOne) / sqrt(2), Jz * JzKet(S.Half, S.Half)) result = Mul(S.NegativeOne, Rational(1, 4), 2**S.Half, hbar**2) result *= TensorProduct(JzKet(2, -1), JzKet(S.Half, S.Half)) assert qapply(expr1) == result
def test_doit(): x, y = symbols('x y') A, B, C, D, E, F = symbols('A B C D E F', commutative=False) d = Density([XKet(), 0.5], [PxKet(), 0.5]) assert (0.5 * (PxKet() * Dagger(PxKet())) + 0.5 * (XKet() * Dagger(XKet()))) == d.doit() # check for kets with expr in them d_with_sym = Density([XKet(x * y), 0.5], [PxKet(x * y), 0.5]) assert (0.5 * (PxKet(x * y) * Dagger(PxKet(x * y))) + 0.5 * (XKet(x * y) * Dagger(XKet(x * y)))) == d_with_sym.doit() d = Density([(A + B) * C, 1.0]) assert d.doit() == (1.0 * A * C * Dagger(C) * Dagger(A) + 1.0 * A * C * Dagger(C) * Dagger(B) + 1.0 * B * C * Dagger(C) * Dagger(A) + 1.0 * B * C * Dagger(C) * Dagger(B)) # With TensorProducts as args # Density with simple tensor products as args t = TensorProduct(A, B, C) d = Density([t, 1.0]) assert d.doit() == 1.0 * TensorProduct(A * Dagger(A), B * Dagger(B), C * Dagger(C)) # Density with multiple Tensorproducts as states t2 = TensorProduct(A, B) t3 = TensorProduct(C, D) d = Density([t2, 0.5], [t3, 0.5]) assert d.doit() == (0.5 * TensorProduct(A * Dagger(A), B * Dagger(B)) + 0.5 * TensorProduct(C * Dagger(C), D * Dagger(D))) #Density with mixed states d = Density([t2 + t3, 1.0]) assert d.doit() == (1.0 * TensorProduct(A * Dagger(A), B * Dagger(B)) + 1.0 * TensorProduct(A * Dagger(C), B * Dagger(D)) + 1.0 * TensorProduct(C * Dagger(A), D * Dagger(B)) + 1.0 * TensorProduct(C * Dagger(C), D * Dagger(D))) #Density operators with spin states tp1 = TensorProduct(JzKet(1, 1), JzKet(1, -1)) d = Density([tp1, 1]) # full trace t = Tr(d) assert t.doit() == 1 #Partial trace on density operators with spin states t = Tr(d, [0]) assert t.doit() == JzKet(1, -1) * Dagger(JzKet(1, -1)) t = Tr(d, [1]) assert t.doit() == JzKet(1, 1) * Dagger(JzKet(1, 1)) # with another spin state tp2 = TensorProduct(JzKet(S(1) / 2, S(1) / 2), JzKet(S(1) / 2, -S(1) / 2)) d = Density([tp2, 1]) #full trace t = Tr(d) assert t.doit() == 1 #Partial trace on density operators with spin states t = Tr(d, [0]) assert t.doit() == JzKet(S(1) / 2, -S(1) / 2) * Dagger( JzKet(S(1) / 2, -S(1) / 2)) t = Tr(d, [1]) assert t.doit() == JzKet(S(1) / 2, S(1) / 2) * Dagger(JzKet(S(1) / 2, S(1) / 2))
def test_eval_trace(): # This test includes tests with dependencies between TensorProducts #and density operators. Since, the test is more to test the behavior of #TensorProducts it remains here A, B, C, D, E, F = symbols('A B C D E F', commutative=False) # Density with simple tensor products as args t = TensorProduct(A, B) d = Density([t, 1.0]) tr = Tr(d) assert tr.doit() == 1.0 * Tr(A * Dagger(A)) * Tr(B * Dagger(B)) ## partial trace with simple tensor products as args t = TensorProduct(A, B, C) d = Density([t, 1.0]) tr = Tr(d, [1]) assert tr.doit() == 1.0 * A * Dagger(A) * Tr(B * Dagger(B)) * C * Dagger(C) tr = Tr(d, [0, 2]) assert tr.doit() == 1.0 * Tr(A * Dagger(A)) * B * Dagger(B) * Tr( C * Dagger(C)) # Density with multiple Tensorproducts as states t2 = TensorProduct(A, B) t3 = TensorProduct(C, D) d = Density([t2, 0.5], [t3, 0.5]) t = Tr(d) assert t.doit() == (0.5 * Tr(A * Dagger(A)) * Tr(B * Dagger(B)) + 0.5 * Tr(C * Dagger(C)) * Tr(D * Dagger(D))) t = Tr(d, [0]) assert t.doit() == (0.5 * Tr(A * Dagger(A)) * B * Dagger(B) + 0.5 * Tr(C * Dagger(C)) * D * Dagger(D)) #Density with mixed states d = Density([t2 + t3, 1.0]) t = Tr(d) assert t.doit() == (1.0 * Tr(A * Dagger(A)) * Tr(B * Dagger(B)) + 1.0 * Tr(A * Dagger(C)) * Tr(B * Dagger(D)) + 1.0 * Tr(C * Dagger(A)) * Tr(D * Dagger(B)) + 1.0 * Tr(C * Dagger(C)) * Tr(D * Dagger(D))) t = Tr(d, [1]) assert t.doit() == (1.0 * A * Dagger(A) * Tr(B * Dagger(B)) + 1.0 * A * Dagger(C) * Tr(B * Dagger(D)) + 1.0 * C * Dagger(A) * Tr(D * Dagger(B)) + 1.0 * C * Dagger(C) * Tr(D * Dagger(D)))
def qapply_Mul(e, **options): ip_doit = options.get('ip_doit', True) args = list(e.args) # If we only have 0 or 1 args, we have nothing to do and return. if len(args) <= 1 or not isinstance(e, Mul): return e rhs = args.pop() lhs = args.pop() # Make sure we have two non-commutative objects before proceeding. if (sympify(rhs).is_commutative and not isinstance(rhs, Wavefunction)) or \ (sympify(lhs).is_commutative and not isinstance(lhs, Wavefunction)): return e # For a Pow with an integer exponent, apply one of them and reduce the # exponent by one. if isinstance(lhs, Pow) and lhs.exp.is_Integer: args.append(lhs.base**(lhs.exp - 1)) lhs = lhs.base # Pull OuterProduct apart if isinstance(lhs, OuterProduct): args.append(lhs.ket) lhs = lhs.bra # Call .doit() on Commutator/AntiCommutator. if isinstance(lhs, (Commutator, AntiCommutator)): comm = lhs.doit() if isinstance(comm, Add): return qapply( e.func(*(args + [comm.args[0], rhs])) + e.func(*(args + [comm.args[1], rhs])), **options ) else: return qapply(e.func(*args)*comm*rhs, **options) # Apply tensor products of operators to states if isinstance(lhs, TensorProduct) and all([isinstance(arg, Operator) or arg == 1 for arg in lhs.args]) and \ isinstance(rhs, TensorProduct) and all([isinstance(arg, State) or arg == 1 for arg in rhs.args]) and \ len(lhs.args) == len(rhs.args): result = TensorProduct(*[qapply(lhs.args[n]*rhs.args[n], **options) for n in range(len(lhs.args))]).expand(tensorproduct=True) return qapply_Mul(e.func(*args), **options)*result # Now try to actually apply the operator and build an inner product. try: result = lhs._apply_operator(rhs, **options) except (NotImplementedError, AttributeError): try: result = rhs._apply_operator(lhs, **options) except (NotImplementedError, AttributeError): if isinstance(lhs, BraBase) and isinstance(rhs, KetBase): result = InnerProduct(lhs, rhs) if ip_doit: result = result.doit() else: result = None # TODO: I may need to expand before returning the final result. if result == 0: return S.Zero elif result is None: if len(args) == 0: # We had two args to begin with so args=[]. return e else: return qapply_Mul(e.func(*(args + [lhs])), **options)*rhs elif isinstance(result, InnerProduct): return result*qapply_Mul(e.func(*args), **options) else: # result is a scalar times a Mul, Add or TensorProduct return qapply(e.func(*args)*result, **options)
def qapply_Mul(e, **options): ip_doit = options.get('ip_doit', True) args = list(e.args) # If we only have 0 or 1 args, we have nothing to do and return. if len(args) <= 1 or not isinstance(e, Mul): return e rhs = args.pop() lhs = args.pop() # Make sure we have two non-commutative objects before proceeding. if (sympify(rhs).is_commutative and not isinstance(rhs, Wavefunction)) or \ (sympify(lhs).is_commutative and not isinstance(lhs, Wavefunction)): return e # For a Pow with an integer exponent, apply one of them and reduce the # exponent by one. if isinstance(lhs, Pow) and lhs.exp.is_Integer: args.append(lhs.base**(lhs.exp-1)) lhs = lhs.base # Pull OuterProduct apart if isinstance(lhs, OuterProduct): args.append(lhs.ket) lhs = lhs.bra # Call .doit() on Commutator/AntiCommutator. if isinstance(lhs, (Commutator, AntiCommutator)): comm = lhs.doit() if isinstance(comm, Add): return qapply( e._new_rawargs(*(args + [comm.args[0], rhs])) +\ e._new_rawargs(*(args + [comm.args[1], rhs])), **options ) else: return qapply(e._new_rawargs(*args)*comm*rhs, **options) # Apply tensor products of operators to states if isinstance(lhs, TensorProduct) and all([isinstance(arg,Operator) or arg == 1 for arg in lhs.args]) and \ isinstance(rhs, TensorProduct) and all([isinstance(arg,State) or arg == 1 for arg in rhs.args]) and \ len(lhs.args) == len(rhs.args): result = TensorProduct(*[qapply(lhs.args[n]*rhs.args[n], **options) for n in range(len(lhs.args))]).expand(tensorproduct=True) return qapply_Mul(e._new_rawargs(*args), **options)*result # Now try to actually apply the operator and build an inner product. try: result = lhs._apply_operator(rhs, **options) except (NotImplementedError, AttributeError): try: result = rhs._apply_operator(lhs, **options) except (NotImplementedError, AttributeError): if isinstance(lhs, BraBase) and isinstance(rhs, KetBase): result = InnerProduct(lhs, rhs) if ip_doit: result = result.doit() else: result = None # TODO: I may need to expand before returning the final result. if result == 0: return S.Zero elif result is None: if len(args) == 0: # We had two args to begin with so args=[]. return e else: return qapply_Mul(e._new_rawargs(*(args+[lhs])), **options)*rhs elif isinstance(result, InnerProduct): return result*qapply_Mul(e._new_rawargs(*args), **options) else: # result is a scalar times a Mul, Add or TensorProduct return qapply(e._new_rawargs(*args)*result, **options)
def uncouple(*args): """ Uncouple a coupled spin state Gives the uncoupled representation of a coupled spin state. Arguments must be either a spin state that is a subclass of CoupledSpinState or a spin state that is a subclass of SpinState and an array giving the j values of the spaces that are to be coupled Parameters ========== args: CoupledSpinState or SpinState The state that is to be coupled. If a subclass of SpinState is used, the state must be followed by the j values of the spaces that are to be coupled. Examples ======== Uncouple a numerical state using a CoupledSpinState state: >>> from sympy.physics.quantum.spin import JzKetCoupled, uncouple >>> from sympy import S >>> uncouple(JzKetCoupled(1, 0, S(1)/2, S(1)/2)) sqrt(2)*|1/2,-1/2>x|1/2,1/2>/2 + sqrt(2)*|1/2,1/2>x|1/2,-1/2>/2 Perform the same calculation using a SpinState state: >>> from sympy.physics.quantum.spin import JzKet >>> uncouple(JzKet(1, 0), S(1)/2, S(1)/2) sqrt(2)*|1/2,-1/2>x|1/2,1/2>/2 + sqrt(2)*|1/2,1/2>x|1/2,-1/2>/2 Uncouple a symbolic state using a CoupledSpinState state: >>> from sympy import symbols >>> j,m,j1,j2 = symbols('j m j1 j2') >>> uncouple(JzKetCoupled(j, m, j1, j2)) Sum(CG(j1, m1, j2, m2, j, m)*|j1,m1>x|j2,m2>, (m1, -j1, j1), (m2, -j2, j2)) Perform the same calculation using a SpinState state >>> uncouple(JzKet(j, m), j1, j2) Sum(CG(j1, m1, j2, m2, j, m)*|j1,m1>x|j2,m2>, (m1, -j1, j1), (m2, -j2, j2)) """ if len(args) == 3: state, j1, j2 = args evect = state.__class__ elif len(args) == 1: state = args[0] evect = state.uncoupled_class() j1, j2 = state.jvals state = evect(state.j, state.m) else: raise TypeError j = state.j m = state.m if state.j.is_number and state.m.is_number: result = [] for i_m1 in range(2*j1+1): m1 = j1-i_m1 for i_m2 in range(2*j2+1): m2 = j2-i_m2 result.append(CG(j1,m1,j2,m2,j,m).doit() * TensorProduct(evect(j1,m1), evect(j2,m2))) return Add(*result) else: m1,m2,mi = symbols('m1 m2 mi') # Hack to get rotation angles angles = (evect(0,mi)._represent())[0].args[3:6] out_state = TensorProduct(evect(j1,m1),evect(j2,m2)) if angles == (0,0,0): lt = CG(j1,m1,j2,m2,state.j,state.m) return Sum(lt * out_state, (m1,-j1,j1), (m2,-j2,j2)) else: lt = CG(j1,m1,j2,m2,state.j,mi) * Rotation.D(state.j,mi,state.m,*angles) return Sum(lt * out_state, (mi,-state.j,state.j), (m1,-j1,j1), (m2,-j2,j2))