Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ TensorKit = "07d1fe3e-3e46-537d-9eac-e9e13d0d4cec"
TensorKitTensors = "41b62e7d-e9d1-4e23-942c-79a97adf954b"

[compat]
Documenter = "1"
Documenter = "1.9"
julia = "1.10"
2 changes: 1 addition & 1 deletion docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ DocMeta.setdocmeta!(

operatorpages = [
"spinoperators.md", "bosonoperators.md", "fermionoperators.md",
"tjoperators.md", "hubbardoperators.md", "quantumgates.md",
"hubbardoperators.md", "tjoperators.md", "quantumgates.md",
]
makedocs(;
modules = [TensorKitTensors],
Expand Down
68 changes: 67 additions & 1 deletion docs/src/bosonoperators.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,75 @@
```@meta
CollapsedDocStrings = true
CurrentModule = TensorKitTensors.BosonOperators
```

# Boson operators

Operators for a truncated bosonic mode, keeping at most `cutoff` bosons per site.
The truncation is not optional: every function in this module takes `cutoff` as a *required* keyword argument, e.g. `b_num(; cutoff = 4)`.

## Conventions

### Basis ordering

The local space is spanned by the occupation-number states in *ascending* order, so that its dimension is ``\mathrm{cutoff} + 1``:

```math
|0⟩,\; |1⟩,\; …,\; |\mathrm{cutoff}⟩ \quad \text{(row/column 1 = vacuum)}
```

In this basis the creation and annihilation operators have the usual matrix elements, with the square root set by the occupation of the *higher* of the two states,

```math
⟨n-1 | b^- | n⟩ = ⟨n | b^+ | n-1⟩ = √n,
\qquad n = 1, …, \mathrm{cutoff}
```

so that ``b^- |0⟩ = 0`` and ``b^+ |\mathrm{cutoff}⟩ = 0``, while ``n = b^+ b^-`` is diagonal with eigenvalues ``0, 1, …, \mathrm{cutoff}``.

!!! warning "The truncated commutator"
Truncating the mode breaks the canonical commutation relation in the top state:

```math
[b^-, b^+] = 1 - (\mathrm{cutoff}{+}1)\,|\mathrm{cutoff}⟩⟨\mathrm{cutoff}|
```

i.e. ``⟨\mathrm{cutoff} | [b^-, b^+] | \mathrm{cutoff}⟩ = -\mathrm{cutoff}`` instead of ``1``.
Every operator of this module is exact *within* the truncated space; only relations that involve states above the cutoff are affected.

### Symmetry sectors

| Symmetry | Physical meaning | Sector label | Single-site block structure |
|---|---|---|---|
| `Trivial` | none | — | full ``(\mathrm{cutoff}{+}1)×(\mathrm{cutoff}{+}1)`` matrix |
| `U1Irrep` | boson-number conservation | charge ``n ∈ \{0, …, \mathrm{cutoff}\}`` | ``\mathrm{cutoff}{+}1`` one-dimensional blocks; ``b^+``, ``b^-`` not individually representable |

!!! note "U(1) charge = occupation number"
The ``U(1)`` charge is the boson number itself, and the charge sectors are ordered as `0:cutoff`, which coincides with the occupation-number basis.
The basis transformation onto the symmetric space is therefore the identity, and every symmetric operator is elementwise equal to its `Trivial` counterpart.

Only the boson-number conserving operators are representable: [`b_num`](@ref), [`b_plus_b_min`](@ref), [`b_min_b_plus`](@ref) and [`b_hopping`](@ref).
The operators that change the boson number — [`b_plus`](@ref), [`b_min`](@ref), [`b_plus_b_plus`](@ref) and [`b_min_b_min`](@ref) — throw an `ArgumentError` when requested with `U1Irrep`.

## Operator overview

| Function | Alias | Sites | Supported symmetries |
|---|---|---|---|
| [`boson_space`](@ref) | — | — | `Trivial`, `U1Irrep` |
| [`basis_transform`](@ref) | — | — | `Trivial`, `U1Irrep` |
| [`b_plus`](@ref) | `b⁺` | 1 | `Trivial` |
| [`b_min`](@ref) | `b⁻` | 1 | `Trivial` |
| [`b_num`](@ref) | `n` | 1 | `Trivial`, `U1Irrep` |
| [`b_plus_b_plus`](@ref) | `b⁺b⁺` | 2 | `Trivial` |
| [`b_plus_b_min`](@ref) | `b⁺b⁻` | 2 | `Trivial`, `U1Irrep` |
| [`b_min_b_plus`](@ref) | `b⁻b⁺` | 2 | `Trivial`, `U1Irrep` |
| [`b_min_b_min`](@ref) | `b⁻b⁻` | 2 | `Trivial` |
| [`b_hopping`](@ref) | `b_hop` | 2 | `Trivial`, `U1Irrep` |

Note that the bosonic hopping operator is the *sum* ``b^+_1 b^-_2 + b^-_1 b^+_2``, in contrast with the fermionic one of [`FermionOperators`](fermionoperators.md), where the anticommutation sign turns the same hermitian combination into a difference.

## API

```@autodocs
Modules = [TensorKitTensors.BosonOperators]
Modules = [BosonOperators]
```
73 changes: 72 additions & 1 deletion docs/src/fermionoperators.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,80 @@
```@meta
CollapsedDocStrings = true
CurrentModule = TensorKitTensors.FermionOperators
```

# Fermion operators

Operators for a single *spinless* fermionic mode per site, i.e. a two-dimensional local space that is either empty or occupied.

## Conventions

### Basis ordering

The local space is spanned by the empty and the occupied state, in that order:

```math
|0⟩,\; |1⟩ \quad \text{(row/column 1 = empty, 2 = occupied)}
```

The space is *always* fermionically graded, `Vect[fℤ₂](0 => 1, 1 => 1)`, even for `Trivial` symmetry: the grading by the fermion parity ``(-1)^n`` is what makes TensorKit insert the anticommutation signs when operators on different sites are contracted, and is therefore not optional.
Here `Trivial` refers only to the absence of an *additional* symmetry.

Because a `TensorMap` on a graded space only has parity-preserving blocks, the parity-odd single-site operators ``f^+`` and ``f^-`` are not representable at all — they have no allowed block.
This module therefore provides no `f_plus` or `f_min`, only their parity-even two-site combinations and the number operator ``n = f^+ f^- = \mathrm{diag}(0, 1)``.

The two-site operators carry the signs picked up by anticommuting the fermionic operators past each other, with ``|1,1⟩ = f^+_1 f^+_2 |0,0⟩`` as the reference state.
They act as

```math
f^+_1 f^-_2 : \; +|1,0⟩ ← |0,1⟩, \qquad
f^-_1 f^+_2 : \; -|0,1⟩ ← |1,0⟩
```

```math
f^+_1 f^+_2 : \; +|1,1⟩ ← |0,0⟩, \qquad
f^-_1 f^-_2 : \; -|0,0⟩ ← |1,1⟩
```

that is, `f_min_f_plus == -adjoint(f_plus_f_min)` and `f_min_f_min == -adjoint(f_plus_f_plus)`.
Consequently the hermitian hopping operator is the *difference*

```math
f_\mathrm{hop} = f^+_1 f^-_2 - f^-_1 f^+_2 = f^+_1 f^-_2 + (f^+_1 f^-_2)^† .
```

The corresponding bosonic operator of [`BosonOperators`](bosonoperators.md) is a *sum*, because ``b^-_1 b^+_2`` is the plain adjoint of ``b^+_1 b^-_2`` and does not pick up a sign.

### Symmetry sectors

| Symmetry | Physical meaning | Sector label | Single-site space |
|---|---|---|---|
| `Trivial` | fermion parity only (always present) | ``fℤ₂`` charge ``n \bmod 2`` | `Vect[fℤ₂](0 => 1, 1 => 1)` |
| `U1Irrep` | particle-number conservation | ``fℤ₂ ⊠ U(1)`` charge ``(n \bmod 2,\, n)`` | `Vect[fℤ₂ ⊠ U1Irrep]((0, 0) => 1, (1, 1) => 1)` |

!!! note "The U(1) refinement is free"
The particle number refines the parity grading without reordering the basis, so the basis transformation onto the symmetric space is the identity and every symmetric operator is elementwise equal to its `Trivial` counterpart.

[`f_num`](@ref), [`f_plus_f_min`](@ref), [`f_min_f_plus`](@ref) and [`f_hopping`](@ref) conserve the particle number and are available for both symmetries.
The pair operators [`f_plus_f_plus`](@ref) and [`f_min_f_min`](@ref) change it by ``±2``: they preserve the parity, and hence exist as `Trivial` operators, but throw an `ArgumentError` when requested with `U1Irrep`.

## Operator overview

| Function | Alias | Sites | Supported symmetries |
|---|---|---|---|
| [`fermion_space`](@ref) | — | — | `Trivial`, `U1Irrep` |
| [`basis_transform`](@ref) | — | — | `Trivial`, `U1Irrep` |
| [`f_num`](@ref) | `n` | 1 | `Trivial`, `U1Irrep` |
| [`f_plus_f_min`](@ref) | `f⁺f⁻` | 2 | `Trivial`, `U1Irrep` |
| [`f_min_f_plus`](@ref) | `f⁻f⁺` | 2 | `Trivial`, `U1Irrep` |
| [`f_plus_f_plus`](@ref) | `f⁺f⁺` | 2 | `Trivial` |
| [`f_min_f_min`](@ref) | `f⁻f⁻` | 2 | `Trivial` |
| [`f_hopping`](@ref) | `f_hop` | 2 | `Trivial`, `U1Irrep` |

There is deliberately no single-site `f_plus`/`f_min`, see the basis-ordering section above.

## API

```@autodocs
Modules = [TensorKitTensors.FermionOperators]
Modules = [FermionOperators]
```
119 changes: 118 additions & 1 deletion docs/src/hubbardoperators.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,126 @@
```@meta
CollapsedDocStrings = true
CurrentModule = TensorKitTensors.HubbardOperators
```

# Hubbard operators

Creation, annihilation, number and spin operators for Hubbard-type models, defined on the four-dimensional local space of a spinful fermion, with independent symmetries for the particle number and for the spin.

## Conventions

### Basis ordering

A Hubbard site carries four states,

```math
|0⟩, \quad |↑⟩ = e^†_↑ |0⟩, \quad
|↓⟩ = e^†_↓ |0⟩, \quad
|↑↓⟩ = e^†_↑ e^†_↓ |0⟩,
```

of which the two singly occupied ones are fermionic.
The local space is therefore always graded by `FermionParity`, and since a graded space groups its basis vectors per sector, in the order of `sectors(V)`, the *index* order is not the order in which the states are listed above but the parity-sorted one:

```math
\underbrace{|0⟩,\; |↑↓⟩}_{\text{parity even}}, \quad
\underbrace{|↑⟩,\; |↓⟩}_{\text{parity odd}}
```

This is the order in which `block(op, FermionParity(0))` and `block(op, FermionParity(1))` list their rows and columns — with `Trivial` particle and spin symmetry both blocks are ``2 × 2``, so there is no single ``4 × 4`` matrix — and it is the reference order of [`basis_transform`](@ref), which is consequently the identity for `Trivial` symmetry.

For the symmetric versions the dense order follows the sector order of the target space and is in general *not* the reference order.
Rather than relying on it, read it off from the basis transformation: numbering the reference states ``(1, 2, 3, 4) = (|0⟩, |↑↓⟩, |↑⟩, |↓⟩)``, column ``j`` of `basis_transform(P, S)` has its single nonzero entry in the row that is the dense index of reference state ``j``:

```julia
U = convert(Array, basis_transform(U1Irrep, SU2Irrep))
i_up = findfirst(==(1), U[:, 3]) # dense index of |↑⟩
```

### Symmetry sectors

The particle-number and the spin symmetry are chosen independently.
The local sector type is always `FermionParity ⊠ (particle) ⊠ (spin)` with the `Trivial` factors omitted and the spin factor last, so e.g. `hubbard_space(U1Irrep, SU2Irrep)` is `Vect[FermionParity ⊠ U1Irrep ⊠ SU2Irrep]((0, 0, 0) => 1, (1, 1, 1//2) => 1, (0, 2, 0) => 1)`.
The supported sectors are `Trivial`, `U1Irrep` and `SU2Irrep` on both axes; anything else, `Z2Irrep` included, throws an `ArgumentError`.

The particle symmetry labels the parity-even pair (empty, doubly occupied):

| Symmetry | Physical meaning | Sector label | Charges of the local states |
|---|---|---|---|
| `Trivial` | none | — | empty and doubly occupied share the parity-even sector, with multiplicity 2 |
| `U1Irrep` | particle-number conservation | ``n ∈ \{0, 1, 2\}`` | empty ``↦ 0``, singly occupied ``↦ 1``, doubly occupied ``↦ 2`` |
| `SU2Irrep` | ``η``-pairing SU(2) | ``η ∈ \{0, 1/2\}`` | (doubly occupied, empty) is the ``η = 1/2`` doublet, ordered by descending ``η^z = (n-1)/2``; singly occupied has ``η = 0`` |

The spin symmetry labels the parity-odd pair (the two singly occupied states):

| Symmetry | Physical meaning | Sector label | Charges of the local states |
|---|---|---|---|
| `Trivial` | none | — | the two singly occupied states share the parity-odd sector, with multiplicity 2 |
| `U1Irrep` | ``S^z`` conservation | ``m ∈ \{-1/2, 0, +1/2\}`` | ``↑ ↦ +1/2``, ``↓ ↦ -1/2``, empty and doubly occupied ``↦ 0`` |
| `SU2Irrep` | full spin SU(2) | ``s ∈ \{0, 1/2\}`` | the singly occupied states form the ``s = 1/2`` doublet (descending ``m``); empty and doubly occupied are ``s = 0`` |

!!! note "Charge conventions"
The `U1Irrep` particle charge is the particle number ``n``, not the particle-hole symmetric ``n - 1``, and the `U1Irrep` spin charge is ``S^z = ±1/2``, not ``2S^z = ±1``.
The ``η``-pairing `SU2Irrep` is built on ``η^z = (n-1)/2`` instead, so the particle number itself is *not* an ``η``-SU(2) scalar: this is why [`e_num`](@ref) and [`ud_num`](@ref) are unavailable with `SU2Irrep` particle symmetry, while the particle-hole symmetric [`half_ud_num`](@ref) ``= (n_↑ - 1/2)(n_↓ - 1/2)`` — which equals ``+1/4`` on the ``η`` doublet and ``-1/4`` on the singly occupied states — is available for every symmetry combination.

!!! note "Staggered gauge for SU(2) particle symmetry"
The ``η``-pairing SU(2) only commutes with the Hubbard Hamiltonian after the staggered gauge transformation ``e_{j,σ} → i^j e_{j,σ}`` on a bipartite lattice.
Accordingly, with `SU2Irrep` particle symmetry the operators of this module act on site ``k`` with the additional gauge factor ``G^{k-1}``, where ``G = i^n = \mathrm{diag}(1, -1, i, i)`` in the reference order above.
Operators that commute with ``G`` are returned without it, so they stay real-representable and remain elementwise equal to their `Trivial` counterparts.
Of the operators available with `SU2Irrep` particle symmetry, [`e_hopping`](@ref) is the only one that does not commute with ``G``: it is therefore genuinely complex, requires a complex `eltype`, and is the gauge-transformed version of its `Trivial` counterpart — it generates the same physics on a bipartite lattice, but is **not** elementwise equal to it.

The ``η``-pairing SU(2) and the gauge transformation that makes it a symmetry of the Hubbard model are due to C. N. Yang and S. C. Zhang, *SO₄ symmetry in a Hubbard model*, Mod. Phys. Lett. B **4**, 759 (1990), [doi:10.1142/S0217984990000933](https://doi.org/10.1142/S0217984990000933); see also C. N. Yang, *η pairing and off-diagonal long-range order in a Hubbard model*, Phys. Rev. Lett. **63**, 2144 (1989), [doi:10.1103/PhysRevLett.63.2144](https://doi.org/10.1103/PhysRevLett.63.2144).

## Operator overview

The two symmetry columns are independent conditions: an operator is available for exactly those combinations that satisfy both.
*any* means all three supported sectors, `Trivial`, `U1Irrep` and `SU2Irrep`; every other request throws an `ArgumentError`.

| Function | Alias | Sites | Particle symmetry | Spin symmetry |
|---|---|---|---|---|
| [`hubbard_space`](@ref) | — | — | any | any |
| [`basis_transform`](@ref) | — | — | any | any |
| [`e_num`](@ref) | `n` | 1 | `Trivial`, `U1Irrep` | any |
| [`u_num`](@ref) | `nꜛ` | 1 | `Trivial`, `U1Irrep` | `Trivial`, `U1Irrep` |
| [`d_num`](@ref) | `nꜜ` | 1 | `Trivial`, `U1Irrep` | `Trivial`, `U1Irrep` |
| [`ud_num`](@ref) | `nꜛꜜ` | 1 | `Trivial`, `U1Irrep` | any |
| [`half_ud_num`](@ref) | — | 1 | any | any |
| [`h_num`](@ref) | `nʰ` | 1 | `Trivial`, `U1Irrep` | any |
| [`S_x`](@ref) | `Sˣ` | 1 | any | `Trivial` |
| [`S_y`](@ref) | `Sʸ` | 1 | any | `Trivial` |
| [`S_z`](@ref) | `Sᶻ` | 1 | any | `Trivial`, `U1Irrep` |
| [`S_plus`](@ref) | `S⁺` | 1 | any | `Trivial` |
| [`S_min`](@ref) | `S⁻` | 1 | any | `Trivial` |
| [`u_plus_u_min`](@ref) | `u⁺u⁻` | 2 | `Trivial`, `U1Irrep` | `Trivial`, `U1Irrep` |
| [`u_min_u_plus`](@ref) | `u⁻u⁺` | 2 | `Trivial`, `U1Irrep` | `Trivial`, `U1Irrep` |
| [`d_plus_d_min`](@ref) | `d⁺d⁻` | 2 | `Trivial`, `U1Irrep` | `Trivial`, `U1Irrep` |
| [`d_min_d_plus`](@ref) | `d⁻d⁺` | 2 | `Trivial`, `U1Irrep` | `Trivial`, `U1Irrep` |
| [`e_plus_e_min`](@ref) | `e⁺e⁻` | 2 | `Trivial`, `U1Irrep` | any |
| [`e_min_e_plus`](@ref) | `e⁻e⁺` | 2 | `Trivial`, `U1Irrep` | any |
| [`e_hopping`](@ref) | `e_hop` | 2 | any | any |
| [`u_min_d_min`](@ref) | `u⁻d⁻` | 2 | `Trivial` | `Trivial`, `U1Irrep` |
| [`d_min_u_min`](@ref) | `d⁻u⁻` | 2 | `Trivial` | `Trivial`, `U1Irrep` |
| [`u_plus_d_plus`](@ref) | `u⁺d⁺` | 2 | `Trivial` | `Trivial`, `U1Irrep` |
| [`d_plus_u_plus`](@ref) | `d⁺u⁺` | 2 | `Trivial` | `Trivial`, `U1Irrep` |
| [`u_min_u_min`](@ref) | `u⁻u⁻` | 2 | `Trivial` | `Trivial` |
| [`u_plus_u_plus`](@ref) | `u⁺u⁺` | 2 | `Trivial` | `Trivial` |
| [`d_min_d_min`](@ref) | `d⁻d⁻` | 2 | `Trivial` | `Trivial` |
| [`d_plus_d_plus`](@ref) | `d⁺d⁺` | 2 | `Trivial` | `Trivial` |
| [`singlet_plus`](@ref) | `singlet⁺` | 2 | `Trivial` | any |
| [`singlet_min`](@ref) | `singlet⁻` | 2 | `Trivial` | any |
| [`S_plus_S_min`](@ref) | `S⁺S⁻` | 2 | any | `Trivial`, `U1Irrep` |
| [`S_min_S_plus`](@ref) | `S⁻S⁺` | 2 | any | `Trivial`, `U1Irrep` |
| [`S_exchange`](@ref) | `SS` | 2 | any | any |
| [`singlet_plus_singlet_min_3site`](@ref) | `Δ⁺ij_Δjk` | 3 | `Trivial`, `U1Irrep` | any |
| [`singlet_plus_singlet_min_4site`](@ref) | `Δ⁺ij_Δkl` | 4 | `Trivial`, `U1Irrep` | any |

Note that the singlet-pair terms [`singlet_plus_singlet_min_3site`](@ref) and [`singlet_plus_singlet_min_4site`](@ref) are available with `U1Irrep` particle symmetry even though [`singlet_plus`](@ref) and [`singlet_min`](@ref) individually are not: only the product ``Δ^† Δ`` conserves the particle number.
[`S_y`](@ref) requires a complex `eltype` for every symmetry combination, and [`e_hopping`](@ref) requires one with `SU2Irrep` particle symmetry; all other operators honour any `eltype`, and the basis transformations have exact integer entries so that they never degrade the precision of the result.

The t-J restriction of this module, obtained by projecting out the doubly occupied state, is [`TJOperators`](tjoperators.md).

## API

```@autodocs
Modules = [TensorKitTensors.HubbardOperators]
Modules = [HubbardOperators]
```
8 changes: 4 additions & 4 deletions docs/src/quantumgates.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ A qubit lives on a two-dimensional space with computational basis ordered as ``\

Multi-qubit gates act on ``V^{⊗n}`` with the first qubit as the leftmost tensor factor, so a controlled gate uses the *first* qubit(s) as control and the *last* as target.

Within a fused block, the index of ``|b_1 b_2 \ldots b_n⟩`` is ``k = 1 + \sum_i b_i 2^{i-1}``, i.e. the *first* qubit runs fastest.
Within a fused block, the index of ``|b_1 b_2 … b_n⟩`` is ``k = 1 + \sum_i b_i 2^{i-1}``, i.e. the *first* qubit runs fastest.
Consequently `block(gate, Trivial())` agrees element-for-element with the gate matrices as tabulated by e.g. Qiskit, identifying the first tensor factor with qubit 0.

### Element types
Expand All @@ -32,7 +32,7 @@ The universal single-qubit gate [`u3`](@ref) takes the three angles `theta`, `ph

| Symmetry | Physical meaning | Sector label | Representable gates |
|---|---|---|---|
| `Trivial` | none | — | all gates, full ``2^n \times 2^n`` matrix |
| `Trivial` | none | — | all gates, full ``2^n × 2^n`` matrix |
| `Z2Irrep` | excitation-number parity | charge ``n \bmod 2`` per qubit | only gates that conserve the number of ``1``'s modulo 2 |
| `U1Irrep` | excitation-number conservation | charge ``∈ \{0, 1\}`` per qubit | only gates that conserve the number of ``1``'s |

Expand All @@ -57,7 +57,7 @@ crx = controlled(rotation_x(; theta = π / 2))

## Gate overview

| Function | Alias(es) | Sites | Compatible symmetries |
| Function | Alias(es) | Sites | Supported symmetries |
|---|---|---|---|
| [`qubit_space`](@ref) | — | — | `Trivial`, `Z2Irrep`, `U1Irrep` |
| [`pauli_x`](@ref) | `X` | 1 | `Trivial` |
Expand Down Expand Up @@ -90,7 +90,7 @@ crx = controlled(rotation_x(; theta = π / 2))
| [`toffoli`](@ref) | `TOFFOLI`, `CCX` | 3 | `Trivial` |
| [`fredkin`](@ref) | `FREDKIN`, `CSWAP` | 3 | `Trivial`, `Z2Irrep`, `U1Irrep` |

The adjoint gates ``S^\dagger`` and ``T^\dagger`` are obtained as `s_gate()'` and `t_gate()'`.
The adjoint gates ``S^†`` and ``T^†`` are obtained as `s_gate()'` and `t_gate()'`.

## API

Expand Down
Loading
Loading