Scalar multiplication


Multiplying a state by a scalar modifies the coefficient appropriately:

julia> k = d" (1+3.4im)| 0 > "
Ket{KroneckerDelta,1,Complex{Float64}} with 1 state(s):
  1.0 + 3.4im | 0 ⟩

julia> k/2
Ket{KroneckerDelta,1,Complex{Float64}} with 1 state(s):
  0.5 + 1.7im | 0 ⟩

Addition and Subtraction


States can be also be added and subtracted:

julia> d" | 0 > + | 0 > == 2| 0 > "
true

julia> d" | 0 > - | 0 > == 0| 0 > "
true

julia> d" 1/√3 * (| 0 > + | 1 > - | 2 >) "
Ket{KroneckerDelta,1,Float64} with 3 state(s):
  0.5773502691896258 | 0 ⟩
  -0.5773502691896258 | 2 ⟩
  0.5773502691896258 | 1 ⟩

One can conviently sum over an iterable of labels by using Julia's sum function in conjunction with the ket/bra function:

julia> 1/√5 * sum(ket, 1:5)
Ket{KroneckerDelta,1,Float64} with 5 state(s):
  0.4472135954999579 | 2 ⟩
  0.4472135954999579 | 3 ⟩
  0.4472135954999579 | 5 ⟩
  0.4472135954999579 | 4 ⟩
  0.4472135954999579 | 1 ⟩

Normalization


In general, QuDirac objects do not automatically normalize themselves.

We can normalize a state in-place by using the normalize! function:

julia> k = sum(i -> d" float(i)| i > ", 1:3)
Ket{KroneckerDelta,1,Float64} with 3 state(s):
  3.0 | 3 ⟩
  2.0 | 2 ⟩
  1.0 | 1 ⟩

julia> normalize!(k)
Ket{KroneckerDelta,1,Float64} with 3 state(s):
  0.8017837257372732 | 3 ⟩
  0.5345224838248488 | 2 ⟩
  0.2672612419124244 | 1 ⟩

julia> norm(k)
1.0

The normalize function (without the trailing "!") is also provided, which normalizes a copy of the state instead of modifying the original.


Getting a State's Dual


One can use the ctranspose function to construct the dual of a given state:

julia> k = d" (im)| 0 > "
Ket{KroneckerDelta,1,Complex{Int64}} with 1 state(s):
  0 + 1im | 0 ⟩

julia> k'
Bra{KroneckerDelta,1,Complex{Int64}} with 1 state(s):
  0 - 1im ⟨ 0 |

julia> k'' == k
true

For efficiency's sake, Bras are views onto their Kets, not copies. Thus, mutating a Bra will result in the mutation of the underlying Ket:

julia> k = d" 2.3| 1 > + 4.5| 2 > "
Ket{KroneckerDelta,1,Float64} with 2 state(s):
  4.5 | 2 ⟩
  2.3 | 1 ⟩

julia> b = k'
Bra{KroneckerDelta,1,Float64} with 2 state(s):
  4.5 ⟨ 2 |
  2.3 ⟨ 1 |

julia> normalize!(b) # in-place operation mutates b, which mutates k
Bra{KroneckerDelta,1,Float64} with 2 state(s):
  0.8904346821960807 ⟨ 2 |
  0.4551110597891079 ⟨ 1 |

julia> k
Ket{KroneckerDelta,1,Float64} with 2 state(s):
  0.8904346821960807 | 2 ⟩
  0.4551110597891079 | 1 ⟩

If you would like a copy of a state instead, you can explicitly construct one via the copy function:

julia> k = d" | 1 > + | 2 > ";

julia> b = copy(k)';

julia> scale!(3,b)
Bra{KroneckerDelta,1,Int64} with 2 state(s):
  3 ⟨ 2 |
  3 ⟨ 1 |

julia> k
Ket{KroneckerDelta,1,Int64} with 2 state(s):
  1 | 2 ⟩
  1 | 1 ⟩

Tensor Product


One can take a tensor product of states simply by multiplying them:

julia> d" | 0 > * | 0 > "
Ket{KroneckerDelta,2,Int64} with 1 state(s):
  1 | 0,0 ⟩

As you might notice, a tensor product of Kets is itself a Ket, and the result of the above is the same as if we input ket(0,0). Taking the tensor product of more complicated states illustrates the tensor product's cartesian properties:

julia> d" normalize!(sum(i -> (i^2)| i >, 0:3) * sum(i -> (i/2)| i >, -3:3)) "
Ket{KroneckerDelta,2,Float64} with 18 state(s):
  -0.5154323951168185 | 3,-3 ⟩
  0.3436215967445456 | 3,2 ⟩
  -0.019090088708030313 | 1,-1 ⟩
  0.22908106449636376 | 2,3 ⟩
  -0.07636035483212125 | 2,-1 ⟩
  0.019090088708030313 | 1,1 ⟩
  ⁞

Inner Product


Similarly to the tensor product, the inner product can be taken simply by multiplying Bras with Kets:

julia> bra(0) * ket(1)
0

julia> d" < 0 | 1 > " # same as above
0

julia> k = d" 1/√2 * ( | 0,0 > + | 1,1 > ) "; k'*k
0.9999999999999998

julia> d" < 0,0 | * k "
0.7071067811865475

Acting a Bra on a specific Ket factor and vice versa


It is sometimes useful to take the inner product between a Bra and a specific factor of a Ket. A math example might be:

| ψ ⟩ =   c₁ | 0, 1 ⟩ + c₂ | 1, 0 ⟩

⟨ 0₂ | ψ ⟩ =  c₁ ⟨ 0₂ | 0, 1 ⟩ + c₂ ⟨ 0₂ | 1, 0 ⟩

          =  c₁ ⟨ 0 | 1 ⟩| 0 ⟩ + c₂ ⟨ 0 | 0 ⟩| 1 ⟩

If these states are orthonormal, our final result is

⟨ 0₂ | ψ ⟩ = 0 | 0 ⟩ + c₂ | 1 ⟩ 

          = c₂ | 1 ⟩ 

QuDirac supports this operation through the use of the act_on function:

julia> ψ = d" normalize!( | 0,1 > + 2.0| 1,0 > ) "
Ket{KroneckerDelta,2,Float64} with 2 state(s):
  0.4472135954999579 | 0,1 ⟩
  0.8944271909999159 | 1,0 ⟩

julia> act_on(d" < 0 | ", ψ, 2) # ⟨ 0₂ | ψ ⟩
Ket{KroneckerDelta,1,Float64} with 2 state(s):
  0.8944271909999159 | 1 ⟩

julia> act_on(d" < 0 | ", ψ, 1) # ⟨ 0₁ | ψ ⟩
Ket{KroneckerDelta,1,Float64} with 2 state(s):
  0.4472135954999579 | 1 ⟩

This does, of course, work even when the Bra is a superposition of states:

julia> ϕ = d" 1/√2 * (< 0 | + < 1 |) "
Bra{KroneckerDelta,1,Float64} with 2 state(s):
  0.7071067811865475 ⟨ 0 |
  0.7071067811865475 ⟨ 1 |

julia> act_on(ϕ, ψ, 2)
Ket{KroneckerDelta,1,Float64} with 2 state(s):
  0.3162277660168379 | 0 ⟩
  0.6324555320336758 | 1 ⟩

Additionally, one can call act_on(k::Ket, b::Bra, i) to compute ⟨ b | kᵢ ⟩:

julia> act_on(d" | 0 > ", ψ', 2)
Bra{KroneckerDelta,1,Float64} with 1 state(s):
  0.8944271909999159 ⟨ 1 |

As you can see, the above calculations assume an orthonormal inner product for the involved states. This behavior is indicated by the state's type (e.g. KroneckerDelta in Ket{KroneckerDelta,1}). QuDirac has support for other kinds of inner products as well. To learn more, see the Working with Inner Products section.