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, run
(v1.8) pkg> add SpinAdaptedSecondQuantization
Julia version 1.8 or higher is required.
The package can then be loaded
using SpinAdaptedSecondQuantization
Basic 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
scalar
multiplier - An array of
operators
- An array of
tensors
- An array of
Kronecker deltas
- An array of
summation indices
- A dictionary of
orbital constraints
Orbital Indices
Most types will contain orbital indices. For convenience these are represented as integers, but they are purely symbolic. When printed, they will be given names according to the semi-standard names for general molecular orbital (MO) indices pqrstuvw
. This means that index 1 will be printed as 'p', 2 as 'q' and 8 as 'w'. For indices larger than 8 the names wrap around and are numbered with subscripts so index 9 will be printed as 'p₁' and index 78 as 'u₉'.
Operators
A small variety of operator types is supported, see (REF TO LIST) for full list.
A few examples are:
julia> E(1, 2)
E_pq
julia> E(1, 2) * E(3, 4)
E_pq E_rs
julia> e(1, 2, 3, 4)
- δ_qr E_ps + E_pq E_rs
julia> fermion(1, α)
a⁻_pα
julia> fermiondag(2, β)
a†_qβ
Tensors
The simplest tensor type is the RealTensor
which has a name and an array of MO-indices.
Example:
julia> real_tensor("h", 1, 2)
h_pq
julia> real_tensor("g", 1, 2, 3, 4)
g_pqrs
Some other types of tensors are supported with various symmetries in the indices. See (REF TO LIST) for full list.
Kronecker Deltas
Kronecker deltas constrain indices to be equal or else the term would be zero. They can have two or more indices each.
Example:
julia> delta(1, 2)
δ_pq
julia> δ(1, 2) # Unicode version equivalent to the one above
δ_pq
julia> d1 = δ(1, 2)
δ_pq
julia> d2 = δ(2, 3)
δ_qr
julia> d1 * d2 # Compacts delta expression since all are equal
δ_pqr
Summation Indices
A term can represent a sum over all values of a specific (or many) MO-indices.
Example:
julia> a = summation(E(1, 2), [1])
∑_p(E_pq)
julia> b = ∑(E(1, 2), 1:2) # Unicode
∑_pq(E_pq)
julia> a * b # Automatically renames summation indices to not collide
∑_prs(E_pq E_rs)
Orbital Constraints
An important feature is the ability to tell a term whether an MO-index belongs to a particular subset of all orbitals, specifically whether it belongs to the occupied or virtual set.
Example:
julia> occupied(1)
C(p∈o)
julia> virtual(2)
C(q∈v)
here the constraints are printed explicitly as that is all that exists in the term, but if the index shows up other places in the term, the indices are colored to indicate the constraints instead:
julia> a = E(1, 2) * virtual(1) * occupied(2)
E_pq
Where occupied orbitals are colored green and virtual are colored blue. If you would rather not have colored prints (printing to file, no color support in terminal, preferance, ...) you can easily turn this off:
julia> SpinAdaptedSecondQuantization.disable_color()
julia> a
E_pq C(p∈v, q∈o)
julia> SASQ.enable_color() # Can use acronym SASQ instead of full module name
julia> a
E_pq
Constraints will transfer to other indices through Kronecker deltas and will produce a zero-term if they are unsatisfiable:
julia> a = δ(1, 2) * occupied(1) # Now q is also occupied
δ_pq
julia> b = δ(2, 3) * virtual(3)
δ_qr
julia> a * b # gives 0 as they cannot be occupied and virtual
0
Hartree-Fock energy expression
julia> h = ∑(rsym_tensor("h", 1, 2) * E(1, 2), 1:2)
∑_pq(h_pq E_pq)
julia> g = simplify(∑(rsym_tensor("g", 1:4...) * e(1:4...), 1:4))
- ∑_pqr(g_prqr E_pq) + ∑_pqrs(g_pqrs E_pq E_rs)
julia> H = h + g
∑_pq(h_pq E_pq) - ∑_pqr(g_prqr E_pq) + ∑_pqrs(g_pqrs E_pq E_rs)
julia> E_hf = simplify_heavy(act_on_ket(H, 0))
2 ∑_i(h_ii) + 4 ∑_ij(g_iijj) - 2 ∑_ij(g_ijij)