def test_density(): d = Density([Jz * mo, 0.5], [Jz * po, 0.5]) assert qapply(d) == Density([-hbar * mo, 0.5], [hbar * po, 0.5])
def test_apply_op(): d = Density([Ket(0), 0.5], [Ket(1), 0.5]) assert d.apply_op(XOp()) == Density([XOp() * Ket(0), 0.5], [XOp() * Ket(1), 0.5])
def test_eval_trace(): q1 = Qubit('10110') q2 = Qubit('01010') d = Density([q1, 0.6], [q2, 0.4]) t = Tr(d) assert t.doit() == 1 # extreme bits t = Tr(d, 0) assert t.doit() == (0.4 * Density([Qubit('0101'), 1]) + 0.6 * Density([Qubit('1011'), 1])) t = Tr(d, 4) assert t.doit() == (0.4 * Density([Qubit('1010'), 1]) + 0.6 * Density([Qubit('0110'), 1])) # index somewhere in between t = Tr(d, 2) assert t.doit() == (0.4 * Density([Qubit('0110'), 1]) + 0.6 * Density([Qubit('1010'), 1])) #trace all indices t = Tr(d, [0, 1, 2, 3, 4]) assert t.doit() == 1 # trace some indices, initialized in # non-canonical order t = Tr(d, [2, 1, 3]) assert t.doit() == (0.4 * Density([Qubit('00'), 1]) + 0.6 * Density([Qubit('10'), 1])) # mixed states q = (1 / sqrt(2)) * (Qubit('00') + Qubit('11')) d = Density([q, 1.0]) t = Tr(d, 0) assert t.doit() == (0.5 * Density([Qubit('0'), 1]) + 0.5 * Density([Qubit('1'), 1]))
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. Examples ======== >>> from sympy.physics.quantum import qapply, Ket, Bra >>> b = Bra('b') >>> k = Ket('k') >>> A = k * b >>> A |k><b| >>> qapply(A * b.dual / (b * b.dual)) |k> >>> qapply(k.dual * A / (k.dual * k), dagger=True) <b| >>> qapply(k.dual * A / (k.dual * k)) <k|*|k><b|/<k|k> """ 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.expand() # 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): c_part, nc_part = e.args_cnc() c_mul = Mul(*c_part) nc_mul = Mul(*nc_part) if isinstance(nc_mul, Mul): result = c_mul * qapply_Mul(nc_mul, **options) else: result = c_mul * qapply(nc_mul, **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 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.Half, S.Half), JzKet(S.Half, Rational(-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.Half, Rational(-1, 2)) * Dagger( JzKet(S.Half, Rational(-1, 2))) t = Tr(d, [1]) assert t.doit() == JzKet(S.Half, S.Half) * Dagger(JzKet(S.Half, S.Half))
def test_fidelity(): # test with kets up = JzKet(S.Half, S.Half) down = JzKet(S.Half, Rational(-1, 2)) updown = (S.One / sqrt(2)) * up + (S.One / sqrt(2)) * down # check with matrices up_dm = represent(up * Dagger(up)) down_dm = represent(down * Dagger(down)) updown_dm = represent(updown * Dagger(updown)) assert abs(fidelity(up_dm, up_dm) - 1) < 1e-3 assert fidelity(up_dm, down_dm) < 1e-3 assert abs(fidelity(up_dm, updown_dm) - (S.One / sqrt(2))) < 1e-3 assert abs(fidelity(updown_dm, down_dm) - (S.One / sqrt(2))) < 1e-3 # check with density up_dm = Density([up, 1.0]) down_dm = Density([down, 1.0]) updown_dm = Density([updown, 1.0]) assert abs(fidelity(up_dm, up_dm) - 1) < 1e-3 assert abs(fidelity(up_dm, down_dm)) < 1e-3 assert abs(fidelity(up_dm, updown_dm) - (S.One / sqrt(2))) < 1e-3 assert abs(fidelity(updown_dm, down_dm) - (S.One / sqrt(2))) < 1e-3 # check mixed states with density updown2 = sqrt(3) / 2 * up + S.Half * down d1 = Density([updown, 0.25], [updown2, 0.75]) d2 = Density([updown, 0.75], [updown2, 0.25]) assert abs(fidelity(d1, d2) - 0.991) < 1e-3 assert abs(fidelity(d2, d1) - fidelity(d1, d2)) < 1e-3 # using qubits/density(pure states) state1 = Qubit("0") state2 = Qubit("1") state3 = S.One / sqrt(2) * state1 + S.One / sqrt(2) * state2 state4 = sqrt(Rational(2, 3)) * state1 + S.One / sqrt(3) * state2 state1_dm = Density([state1, 1]) state2_dm = Density([state2, 1]) state3_dm = Density([state3, 1]) assert fidelity(state1_dm, state1_dm) == 1 assert fidelity(state1_dm, state2_dm) == 0 assert abs(fidelity(state1_dm, state3_dm) - 1 / sqrt(2)) < 1e-3 assert abs(fidelity(state3_dm, state2_dm) - 1 / sqrt(2)) < 1e-3 # using qubits/density(mixed states) d1 = Density([state3, 0.70], [state4, 0.30]) d2 = Density([state3, 0.20], [state4, 0.80]) assert abs(fidelity(d1, d1) - 1) < 1e-3 assert abs(fidelity(d1, d2) - 0.996) < 1e-3 assert abs(fidelity(d1, d2) - fidelity(d2, d1)) < 1e-3 # TODO: test for invalid arguments # non-square matrix mat1 = [[0, 0], [0, 0], [0, 0]] mat2 = [[0, 0], [0, 0]] raises(ValueError, lambda: fidelity(mat1, mat2)) # unequal dimensions mat1 = [[0, 0], [0, 0]] mat2 = [[0, 0, 0], [0, 0, 0], [0, 0, 0]] raises(ValueError, lambda: fidelity(mat1, mat2)) # unsupported data-type x, y = 1, 2 # random values that is not a matrix raises(ValueError, lambda: fidelity(x, y))
def test_get_prob(): x, y = symbols("x y") d = Density([Ket(0), x], [Ket(1), y]) probs = (d.get_prob(0), d.get_prob(1)) assert probs[0] == x and probs[1] == y
def test_get_state(): x, y = symbols("x y") d = Density([Ket(0), x], [Ket(1), y]) states = (d.get_state(0), d.get_state(1)) assert states[0] == Ket(0) and states[1] == Ket(1)
def test_states(): d = Density([Ket(0), 0.5], [Ket(1), 0.5]) states = d.states() assert states[0] == Ket(0) and states[1] == Ket(1)