Skip to content

Commit

Permalink
[Docs] Basic Operations on Quantum Objects (#145)
Browse files Browse the repository at this point in the history
* [Docs] Qobj

* [Docs] Qobj functions

* [Docs] CUDA extension

* minor change

* update document for `CUDA` extension

* update document for `Qobj`

* update document for functions operating on `Qobj`

* add `rand_ket` to docs

* add `rand_unitary` and `isunitary` to docs
  • Loading branch information
ytdHuang authored Aug 31, 2024
1 parent d469d80 commit 9f1a887
Show file tree
Hide file tree
Showing 3 changed files with 553 additions and 4 deletions.
285 changes: 284 additions & 1 deletion docs/src/users_guide/QuantumObject/QuantumObject.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,286 @@
# [Quantum Objects (Qobj)](@id doc:Qobj)

This page is still under construction, please visit [API](@ref doc-API) first.
## Introduction
The key difference between classical and quantum mechanics is the use of operators instead of numbers as variables. Moreover, we need to specify state vectors and their properties. Therefore, in computing the dynamics of quantum systems, we need a data structure that encapsulates the properties of a quantum operator and ket/bra vectors. The quantum object structure, [`QuantumObject`](@ref), accomplishes this using array representation.

[`QuantumObject`](@ref) supports general `Julia` arrays (`Base.AbstractArray`). For example, it can be:
- `Base.Vector` (dense vector)
- `Base.Matrix` (dense matrix)
- `SparseArrays.SparseVector` (sparse vector)
- `SparseArrays.SparseMatrixCSC` (sparse matrix)
- `CUDA.CuArray` (dense GPU vector / matrix)
- `CUDA.CUSPARSE.CuSparseVector` (sparse GPU vector)
- `CUDA.CUSPARSE.CuSparseMatrixCSC` (sparse GPU matrix)
- `CUDA.CUSPARSE.CuSparseMatrixCSR` (sparse GPU matrix)
- and even more ...

We can create a [`QuantumObject`](@ref) with a user defined data set by passing an array of data into the [`QuantumObject`](@ref):

```@setup Qobj
using QuantumToolbox
```

```@example Qobj
QuantumObject([1, 2, 3, 4, 5])
```

We can also use the same function [`Qobj`](@ref) in [`QuTiP` (`Python`)](https://github.com/qutip/qutip):

```@example Qobj
Qobj([1, 2, 3, 4, 5])
```

```@example Qobj
Qobj([1 2 3 4 5])
```

```@example Qobj
Qobj(rand(4, 4))
```

```@example Qobj
Qobj(rand(4, 4), dims = [2, 2])
```

```@example Qobj
Qobj(rand(4, 4), type = SuperOperator)
```

!!! note "Difference between `dims` and `size`"
Notice that `type`, `dims`, and `size` will change according to the input `data`. Although `dims` and `size` appear to be the same, `dims` keep tracking the dimension of individual Hilbert spaces of a multipartite system, while `size` does not. We refer the reader to the section [tensor products and partial traces](@ref doc:Tensor-products) for more information.

## States and operators

Manually specifying the data for each quantum object is inefficient. Even more so when most objects correspond to commonly used types such as the ladder operators of a harmonic oscillator, the Pauli spin operators for a two-level system, or state vectors such as Fock states. Therefore, `QuantumToolbox` includes predefined functions to construct variety of states and operators (you can click the function links and see the corresponding docstring):

### States
- [`zero_ket`](@ref): zero ket vector
- [`fock`](@ref) or [`basis`](@ref): fock state ket vector
- [`fock_dm`](@ref): density matrix of a fock state
- [`coherent`](@ref): coherent state ket vector
- [`rand_ket`](@ref): random ket vector
- [`coherent_dm`](@ref): density matrix of a coherent state
- [`thermal_dm`](@ref): density matrix of a thermal state
- [`maximally_mixed_dm`](@ref): density matrix of a maximally mixed state
- [`rand_dm`](@ref): random density matrix
- [`spin_state`](@ref): spin state
- [`spin_coherent`](@ref): coherent spin state
- [`bell_state`](@ref): Bell state
- [`singlet_state`](@ref): two particle singlet state
- [`triplet_states`](@ref): list of the two particle triplet states
- [`w_state`](@ref): `n`-qubit W-state
- [`ghz_state`](@ref): `n`-qudit GHZ state

### Operators
- [`eye`](@ref) or [`qeye`](@ref): identity operator
- [`destroy`](@ref): lowering (destruction) operator
- [`create`](@ref): raising (creation) operator
- [`projection`](@ref): projection operator
- [`displace`](@ref): displacement operator
- [`squeeze`](@ref): single-mode squeeze operator
- [`num`](@ref): bosonic number operator
- [`phase`](@ref): single-mode Pegg-Barnett phase operator
- [`QuantumToolbox.position`](@ref): position operator
- [`QuantumToolbox.momentum`](@ref): momentum operator
- [`rand_unitary`](@ref): random unitary operator
- [`sigmax`](@ref): Pauli-``X`` operator
- [`sigmay`](@ref): Pauli-``Y`` operator
- [`sigmaz`](@ref): Pauli-``Z`` operator
- [`sigmap`](@ref): Pauli ladder (``\sigma_+``) operator
- [`sigmam`](@ref): Pauli ladder (``\sigma_-``) operator
- [`jmat`](@ref): high-order Spin-`j` operator
- [`spin_Jx`](@ref): ``S_x`` operator for Spin-`j`
- [`spin_Jy`](@ref): ``S_y`` operator for Spin-`j`
- [`spin_Jz`](@ref): ``S_z`` operator for Spin-`j`
- [`spin_Jm`](@ref): ``S_-`` operator for Spin-`j`
- [`spin_Jp`](@ref): ``S_+`` operator for Spin-`j`
- [`spin_J_set`](@ref): a set of Spin-`j` operators ``(S_x, S_y, S_z)``
- [`fdestroy`](@ref): fermion destruction operator
- [`fcreate`](@ref): fermion creation operator
- [`commutator`](@ref): commutator or anti-commutator
- [`tunneling`](@ref): tunneling operator
- [`qft`](@ref): discrete quantum Fourier transform matrix

As an example, we give the output for a few of these functions:

```@example Qobj
basis(5, 3)
```

```@example Qobj
coherent(5, 0.5 - 0.5im)
```

```@example Qobj
destroy(4)
```

```@example Qobj
sigmaz()
```

## Qobj fields (attributes)

We have seen that a structure [`QuantumObject`](@ref) has several fields (attributes), such as `data`, `type` and `dims`. These can be accessed in the following way:

```@example Qobj
a = destroy(4)
a.data
```

```@example Qobj
a[2, 3] # the indexing in Julia starts from `1`
```

```@example Qobj
a.type
```

```@example Qobj
a.dims
```

In general, the properties of a [`QuantumObject`](@ref) can be retrieved using several functions with inputting [`QuantumObject`](@ref):

```@example Qobj
size(a)
```

```@example Qobj
shape(a) # synonym of size(a)
```

```@example Qobj
length(a)
```

```@example Qobj
eltype(a) # element type
```

```@example Qobj
println(isket(a)) # ket
println(isbra(a)) # bra
println(isoper(a)) # operator
println(isoperket(a)) # operator-ket
println(isoperbra(a)) # operator-bra
println(issuper(a)) # super operator
println(ishermitian(a)) # Hermitian
println(isherm(a)) # synonym of ishermitian(a)
println(issymmetric(a)) # symmetric
println(isposdef(a)) # positive definite (and Hermitian)
println(isunitary(a)) # unitary
```

### `data` conversions

As we mentioned above, `QuantumObject.data` supports general `Julia` arrays. The conversion between different type of `QuantumObject.data` is done using the standard type-conversion for arrays in `Julia`:

```@example Qobj
v_d = basis(2, 0)
```

```@example Qobj
Vector{Int64}(v_d)
```

```@example Qobj
v_s = SparseVector(v_d)
```

```@example Qobj
SparseVector{Float64}(v_s)
```

```@example Qobj
x_s = sigmax()
```

```@example Qobj
SparseMatrixCSC{Int64}(x_s)
```

```@example Qobj
Matrix{Float64}(x_s)
```

To convert between dense and sparse arrays, one can also use [`dense_to_sparse`](@ref) and [`sparse_to_dense`](@ref):

```@example Qobj
x_d = sparse_to_dense(x_s)
```

```@example Qobj
dense_to_sparse(x_d)
```

!!! note "Convert to GPU arrays"
See [CUDA extension](@ref doc:CUDA) for more details.

`QuantumToolbox` will do conversion when needed to keep everything working in any format. However these conversions could slow down computation and it is recommended to keep to one format family where possible.

## Qobj math

The rules for mathematical operations on [`QuantumObject`](@ref) are similar to the standard scalar, vector, and matrix arithmetic:

```@example Qobj
a = destroy(4)
```

```@example Qobj
a' # synonym of adjoint(a)
```

```@example Qobj
a + 5
```

```@example Qobj
a' * a
```

```@example Qobj
a ^ 3
```

```@example Qobj
x = sigmax()
```

```@example Qobj
x / sqrt(2)
```

```@example Qobj
x ^ 3 == x
```

```@example Qobj
# type \approx + <TAB> to get symbol ≈
x ^ 3 ≈ x
```

Of course, like matrices, multiplying two objects of incompatible `dims` or `size` throws an error:
```@repl Qobj
a * x
```

Note that there is a special case for multiplying [`Ket`](@ref) and [`Bra`](@ref), which results in outer product ``|u\rangle \otimes \langle v|``:

```@example Qobj
u = Qobj([1, 2, 3])
```

```@example Qobj
v = Qobj([4, 5, 6])
v'
```

```@example Qobj
u * v'
```

Of course, if you switch the order of multiplication, it becomes inner product ``\langle v | u \rangle``:
```@example Qobj
v' * u
```
106 changes: 105 additions & 1 deletion docs/src/users_guide/QuantumObject/QuantumObject_functions.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,107 @@
# [Functions operating on Qobj](@id doc:Functions-operating-on-Qobj)

This page is still under construction, please visit [API](@ref doc-API) first.
`QuantumToolbox` also provide functions (methods) that operates on [`QuantumObject`](@ref).

You can click the function links and see the corresponding docstring for more information.

## Linear algebra and attributes

Here is a table that summarizes all the supported linear algebra functions and attribute functions operating on a given [`QuantumObject`](@ref) `Q`:

| **Description** | **Function call** | **Synonyms** |
|:----------------|:------------------|:-------------|
| conjugate | [`conj(Q)`](@ref conj) | - |
| transpose | [`transpose(Q)`](@ref transpose) | [`trans(Q)`](@ref trans) |
| conjugate transposition | [`adjoint(Q)`](@ref adjoint) | [`Q'`](@ref adjoint), [`dag(Q)`](@ref dag) |
| partial transpose | [`partial_transpose(Q, mask)`](@ref partial_transpose) | - |
| dot product | [`dot(Q1, Q2)`](@ref dot) | - |
| generalized dot product | [`dot(Q1, Q2, Q3)`](@ref dot) | [`matrix_element(Q1, Q2, Q3)`](@ref matrix_element) |
| trace | [`tr(Q)`](@ref tr) | - |
| partial trace | [`ptrace(Q, sel)`](@ref ptrace) | - |
| singular values | [`svdvals(Q)`](@ref svdvals) | - |
| standard vector `p`-norm or [Schatten](https://en.wikipedia.org/wiki/Schatten_norm) `p`-norm | [`norm(Q, p)`](@ref norm) | - |
| normalization | [`normalize(Q, p)`](@ref normalize) | [`unit(Q, p)`](@ref unit) |
| normalization (in-place) | [`normalize!(Q, p)`](@ref normalize!) | - |
| matrix inverse | [`inv(Q)`](@ref inv) | - |
| matrix square root | [`sqrt(Q)`](@ref sqrt) | [`√(Q)`](@ref sqrt), [`sqrtm(Q)`](@ref sqrtm) |
| matrix logarithm | [`log(Q)`](@ref log) | [`logm(Q)`](@ref logm) |
| matrix exponential | [`exp(Q)`](@ref exp) | [`expm(Q)`](@ref expm) |
| matrix sine | [`sin(Q)`](@ref sin) | [`sinm(Q)`](@ref sinm) |
| matrix cosine | [`cos(Q)`](@ref cos) | [`cosm(Q)`](@ref cosm) |
| diagonal elements | [`diag(Q)`](@ref diag) | - |
| projector | [`proj(Q)`](@ref proj) | - |
| purity | [`purity(Q)`](@ref purity) | - |
| permute | [`permute(Q, order)`](@ref permute) | - |
| remove small elements | [`tidyup(Q, tol)`](@ref tidyup) | - |
| remove small elements (in-place) | [`tidyup!(Q, tol)`](@ref tidyup!) | - |
| get data | [`get_data(Q)`](@ref get_data) | - |
| get coherence | [`get_coherence(Q)`](@ref get_coherence) | - |

## Eigenvalue decomposition

- [`eigenenergies`](@ref): return eigenenergies (eigenvalues)
- [`eigenstates`](@ref): return [`EigsolveResult`](@ref) (contains eigenvalues and eigenvectors)
- [`eigvals`](@ref): return eigenvalues
- [`eigen`](@ref): using dense eigen solver and return [`EigsolveResult`](@ref) (contains eigenvalues and eigenvectors)
- [`eigsolve`](@ref): using sparse eigen solver and return [`EigsolveResult`](@ref) (contains eigenvalues and eigenvectors)
- [`eigsolve_al`](@ref): using the Arnoldi-Lindblad eigen solver and return [`EigsolveResult`](@ref) (contains eigenvalues and eigenvectors)

## Examples

```@setup Qobj_Function
using QuantumToolbox
```

```@example Qobj_Function
ψ = normalize(basis(4, 1) + basis(4, 2))
```

```@example Qobj_Function
ψ'
```

```@example Qobj_Function
ρ = coherent_dm(5, 1)
```

```@example Qobj_Function
diag(ρ)
```

```@example Qobj_Function
get_data(ρ)
```

```@example Qobj_Function
norm(ρ)
```

```@example Qobj_Function
sqrtm(ρ)
```

```@example Qobj_Function
tr(ρ)
```

```@example Qobj_Function
eigenenergies(ρ)
```

```@example Qobj_Function
result = eigenstates(ρ)
```

```@example Qobj_Function
λ, ψ = result
λ # eigenvalues
```

```@example Qobj_Function
ψ # eigenvectors
```

```@example Qobj_Function
λ, ψ, T = result
T # transformation matrix
```
Loading

0 comments on commit 9f1a887

Please sign in to comment.