SpinAdaptedSecondQuantization.jl
SpinAdaptedSecondQuantization.jl is a Julia package for doing symbolic second quantization mainly targeted at quantum chemistry methods, such as post Hartree-Fock methods.
To install the package open a Julia terminal, type a ] to open the package mode, then write add SpinAdaptedSecondQuantization to install.
(v1.8) pkg> add SpinAdaptedSecondQuantizationJulia version 1.8 or higher is required.
The package can then be loaded
julia> using SpinAdaptedSecondQuantizationBasic Structure
The core type of the package is the Expression type. This contains an array of Term types. A term is a product that can contain a combination of various elements. These are:
- A
scalarmultiplier - An array of
operators - An array of
tensors - An array of
Kronecker deltas - An array of
summation indices - A dictionary of
index constraints
Indices
Most types will contain indices. For convenience these are represented as integers, but they are purely symbolic. When constrained to a certain index space they will be printed with names according to the semi-standard names for general molecular orbital (MO) indices pqrstuv, abcdefg for virtual and ijklmno for occupied.
By default indices are unconstrained and will be printed as subscript indices ₁₂...
julia> E(1, 2)E_₁₂
Usually, however, one wants to constrain indices to a certain subspace of indices. By default the available spaces are GeneralOrbital, VirtualOrbital and OccupiedOrbital which can be used for example like:
julia> E(1, 2) * constrain(1 => VirtualOrbital, 2 => OccupiedOrbital)E_ai
The shorthand functions electron(...), virtual(...) and occupied(...) can also be used
julia> E(1, 2) * electron(1, 2)E_pqjulia> E(1, 2) * E(3, 4) * virtual(1, 3) * occupied(2, 4)E_ai E_bj
Operators
A small variety of operator types is supplied by default listed in the following table.
| Operator Type | constructor(s) |
|---|---|
| SingletExcitationOperator | E(p, q), e(p, q, r, s) |
| FermionOperator | fermion(p, σ), fermiondag(p, σ) |
| BosonOperator | boson(), bosondag() |
| TripletExcitationOperator | τ(p, q) |
A few examples are:
julia> E(1, 2) * electron(1, 2)E_pqjulia> E(1, 2) * E(3, 4) * virtual(1, 3) * occupied(2, 4)E_ai E_bjjulia> e(1, 2, 3, 4) * electron(1, 2, 3, 4)E_pq E_rs - δ_qr E_psjulia> fermion(1, α) * occupied(1)a⁻_iαjulia> fermiondag(2, β) * virtual(2)a†_aβjulia> bosondag() * boson()b† b⁻julia> τ(1, 2) * electron(1, 2)T_pq
Tensors
The simplest tensor type is the RealTensor which has a name and an array of indices.
Examples:
julia> real_tensor("h", 1, 2) * electron(1, 2)h_pqjulia> real_tensor("g", 1, 2, 3, 4) * electron(1, 2, 3, 4)g_pqrs
Some other types of tensors are supported with various symmetries in the indices. Supported tensor types are listed below
| Tensor type | constructor | symmetries |
|---|---|---|
| RealTensor | real_tensor(name, p, ...) | No symmetries |
| ParticleSymmetricTensor | psym_tensor(name, p, ...) | g₁₂₃₄ <-> g₃₄₁₂ |
| RealSymmetricTensor | rsym_tensor(name, p, ...) | g₁₂₃₄ <-> g₂₁₃₄ <-> g₄₃₂₁ <-> ... |
rsym_tensor is typically used when full real orbital symmetry of integrals is required, such as 2-fold symmetry of hpq and 8-fold symmetry of gpqrs. psym_tensor is useful when doing coupled cluster theory, where symmetries within index pairs do not exist because the integrals are T1 transformed.
Examples of symmetric tensors:
julia> rsym_tensor("g", 4, 3, 1, 2) * electron(1, 2, 3, 4)g_pqrsjulia> psym_tensor("g", 4, 3, 1, 2) * electron(1, 2, 3, 4)g_pqsr
Kronecker Deltas
Kronecker deltas are used to constrain indices to be equal, otherwise the term would be zero. They can have two or more indices each.
Example:
julia> delta(1, 2)δ_₁₂julia> δ(1, 2) # Unicode version equivalent to the one aboveδ_₁₂julia> d1 = δ(1, 2)δ_₁₂julia> d2 = δ(2, 3)δ_₂₃julia> d1 * d2 # Compacts delta expression since all are equalδ_₁₂₃
Summation Indices
A term can represent a sum over all values of a specific (or many) indices. A sum can be constructed using the summation function, or the unicode aliases ∑ or Σ.
Example:
julia> a = summation(E(1, 2) * electron(1, 2), [1])∑_q(E_qp)julia> b = ∑(E(1, 2) * electron(1, 2), 1:2) # Unicode∑_pq(E_pq)julia> a * b # Automatically renames summation indices to not collide∑_qrs(E_qp E_rs)
Hartree-Fock energy expression
As a simple example of use of the package, here is how to derive the HF energy expression.
julia> h = ∑(real_tensor("h", 1, 2) * E(1, 2) * electron(1, 2), 1:2)∑_pq(h_pq E_pq)julia> g = 1//2 * simplify( ∑(psym_tensor("g", 1:4...) * e(1:4...) * electron(1:4...), 1:4) )- 1/2 ∑_pqr(g_prrq E_pq) + 1/2 ∑_pqrs(g_pqrs E_pq E_rs)julia> H = h + g + real_tensor("h_nuc")h_nuc + ∑_pq(h_pq E_pq) - 1/2 ∑_pqr(g_prrq E_pq) + 1/2 ∑_pqrs(g_pqrs E_pq E_rs)julia> E_hf = simplify_heavy(hf_expectation_value(H))h_nuc + 2 ∑_i(h_ii) + 2 ∑_ij(g_iijj) - ∑_ij(g_ijji)