Digital quantum simulation for spin and fermionic systems

The qat.fermion module contains tools for the study of spin and fermionic systems. Please see the main page for more information on how to use the module.

The Hamiltonians classes

SpinHamiltonian, FermionHamiltonian and ElectronicStructureHamiltonian are essentially Observable classes with additional functionalities. Let us detail the additional features they contain.

SpinHamiltonian

class qat.fermion.hamiltonians.SpinHamiltonian(nqbits: int, terms: List[Term], constant_coeff: float = 0.0, do_clean_up: bool = True)

Implementation of a spin Hamiltonian.

Parameters
  • nqbits (int) – the total number of qubits

  • terms (List[Term]) – the list of terms

  • constant_coeff (float) – constant term

nbqbits

the total number of qubits

Type

int

terms

the list of terms

Type

List[Term]

constant_coeff

constant term

Type

float

matrix

the corresponding matrix (None by default, can be set by calling get_matrix method)

Type

np.ndarray

Example

from qat.core import Term
from qat.fermion import SpinHamiltonian

hamiltonian = SpinHamiltonian(2, [Term(0.3, "X", [0]), Term(-0.4, "ZY", [0, 1])])

print(f"H = {hamiltonian}")
print(f"H matrix: {hamiltonian.get_matrix()}")
H = 0.3 * (X|[0]) +
-0.4 * (ZY|[0, 1])
H matrix: [[0. +0.j  0. +0.4j 0.3+0.j  0. +0.j ]
 [0. -0.4j 0. +0.j  0. +0.j  0.3+0.j ]
 [0.3+0.j  0. +0.j  0. +0.j  0. -0.4j]
 [0. +0.j  0.3+0.j  0. +0.4j 0. +0.j ]]
copy()

Deepcopy the current class.

Returns

Copy of the SpinHamiltonian.

Return type

SpinHamiltonian

dag() SpinHamiltonian

Compute the conjugate transpose of the Hamiltonian.

Returns

Conjugate transpose of the SpinHamiltonian operator

Return type

SpinHamiltonian

get_matrix(sparse: bool = False) ndarray

This function returns the matrix corresponding to \(H\) in the computational basis.

Parameters
  • sparse (Optional[bool]) – Whether to return in sparse representation.

  • False. (Defaults to) –

Returns

The matrix of the SpinHamiltonian.

Return type

np.ndarray

Warning

This method should not be used if the SpinHamiltonian is too large.

FermionHamiltonian

class qat.fermion.hamiltonians.FermionHamiltonian(nqbits: int, terms: List[Term], constant_coeff: float = 0.0, do_clean_up: bool = True, normal_order: bool = True)

Implementation of a fermionic Hamiltonian.

Parameters
  • nqbits (int) – The total number of qubits

  • terms (List[Term]) – The list of terms

  • constant_coeff (float) – Constant term

  • do_clean_up (bool, optional) – If the terms should be simplified. Default to True.

  • normal_order (bool, optional) – If the fermionic terms should be normal (or Wick) ordered. Default to True. True is recommended always.

nbqbits

The total number of qubits

Type

int

terms

The list of terms

Type

List[Term]

constant_coeff

Constant term.

Type

float

matrix

The corresponding matrix (None by default, can be set by calling get_matrix method).

Type

np.ndarray

normal_order

If the fermionic terms should be normal (or Wick) ordered.

Type

bool

Note

Fermionic Hamiltonians are by default automatically normally ordered.

Example

from qat.core import Term
from qat.fermion import FermionHamiltonian

hamiltonian = FermionHamiltonian(2, [Term(0.3, "Cc", [0, 1]), Term(1.4, "CcCc", [0, 1, 1, 0])])
print(f"H = {hamiltonian}")
print(f"H matrix: {hamiltonian.get_matrix()}")
H = 0.3 * (Cc|[0, 1]) +
1.4 * (Cc|[0, 0]) +
1.4 * (CCcc|[0, 1, 0, 1])
H matrix: [[0. +0.j 0. +0.j 0. +0.j 0. +0.j]
 [0. +0.j 0. +0.j 0. +0.j 0. +0.j]
 [0. +0.j 0.3+0.j 1.4+0.j 0. +0.j]
 [0. +0.j 0. +0.j 0. +0.j 0. +0.j]]
copy()

Deepcopy the current class.

Returns

Copy of the FermionHamiltonian.

Return type

FermionHamiltonian

dag() FermionHamiltonian

Compute the conjugate transpose of the Hamiltonian.

Returns

Conjugate transpose of the Hamiltonian.

Return type

FermionHamiltonian

get_matrix(sparse: bool = False) ndarray

This function returns the matrix corresponding to \(H\) in the computational basis.

Parameters
  • sparse (Optional[bool]) – Whether to return in sparse representation.

  • False. (Defaults to) –

Returns

The matrix of the FermionHamiltonian.

Return type

numpy.ndarray

Warning

This method should not be used if the Hamiltonian is too large.

to_electronic()

Converts a fermionic Hamiltonian to a electronic-structure Hamiltonian. This can be done only if the Hamiltonian contains only single and double interaction operators (i.e. only “Cc” and “CCcc” fermionic operators).

Returns

Electronic-structure Hamiltonian.

Return type

ElectronicStructureHamiltonian

to_spin(method: Optional[str] = 'jordan-wigner')

Maps the fermionic Hamiltonian to a spin Hamiltonian.

Parameters

method (str, optional) –

Method to use for the transformation to a spin representation. Available methods are :

  • ”jordan-wigner” : Jordan-Wigner transform (default),

  • ”bravyi-kitaev” : Bravyi-Kitaev transform,

  • ”parity” : Parity transform.

Returns

Hamiltonian in spin representation.

Return type

SpinHamiltonian

ElectronicStructureHamiltonian

class qat.fermion.hamiltonians.ElectronicStructureHamiltonian(hpq: ndarray, hpqrs: Optional[ndarray] = None, constant_coeff: float = 0.0, do_clean_up: bool = True)

A container for the electronic-structure Hamiltonian, defined as

\[H = \sum_{pq} h_{pq}a_p^\dagger a_q + \frac{1}{2} \sum_{pqrs} h_{pqrs}a_p^\dagger a_q^\dagger a_r a_s + c \mathbb{I}\]
Parameters
  • hpq (np.ndarray) – Array \(h_{pq}\). Must be 2D.

  • hpqrs (np.ndarray) – Array \(h_{pqrs}\). Must be 4D.

  • constant_coeff (float) – Constant coefficient \(c.\)

hpq

Array \(h_{pq}\).

Type

np.ndarray

hpqrs

Array \(h_{pqrs}\).

Type

np.ndarray

constant_coeff

Constant coefficient \(c\).

Type

float

Example

import numpy as np
from qat.fermion import ElectronicStructureHamiltonian

h_pq = 0.2 * np.array([[0, 1], [1, 0]])
h_pqrs = np.zeros((2, 2, 2, 2))
h_pqrs[0, 1, 1, 0] = 0.7
h_pqrs[1, 0, 0, 1] = 0.7
hamiltonian = ElectronicStructureHamiltonian(h_pq, h_pqrs, -6)

print(f"H = {hamiltonian}")
eigvals = np.linalg.eigvalsh(hamiltonian.get_matrix())

print(f"eigenvalues = {eigvals}")
H = -6 * I^2 +
0.2 * (Cc|[0, 1]) +
0.2 * (Cc|[1, 0]) +
-0.7 * (CCcc|[0, 1, 0, 1])
eigenvalues = [-6.2 -6.  -5.8 -5.3]
copy()

Deepcopy the current class.

Returns

Copy of the ElectronicStructureHamiltonian.

Return type

ElectronicStructureHamiltonian

dag() ElectronicStructureHamiltonian

Compute the conjugate transpose of the Hamiltonian.

Returns

Conjugate transpose of the Hamiltonian.

Return type

ElectronicStructureHamiltonian

get_matrix(sparse: bool = False) ndarray

This function returns the matrix corresponding to \(H\) in the computational basis.

Parameters
  • sparse (Optional[bool]) – Whether to return in sparse representation.

  • False. (Defaults to) –

Returns

The matrix of the FermionHamiltonian.

Return type

numpy.ndarray

Warning

This method should not be used if the Hamiltonian is too large.

to_fermion() FermionHamiltonian

Convert current ElectronicStructureHamiltonian to a FermionHamiltonian.

Returns

Fermionic Hamiltonian.

Return type

FermionHamiltonian

to_spin(method: Optional[str] = 'jordan-wigner')

Maps the fermionic Hamiltonian to a spin Hamiltonian.

Parameters

method (str, optional) –

Method to use for the transformation to a spin representation. Available methods are :

  • ”jordan-wigner” : Jordan-Wigner transform (default),

  • ”bravyi-kitaev” : Bravyi-Kitaev transform,

  • ”parity” : Parity transform.

Returns

Hamiltonian in spin representation.

Return type

SpinHamiltonian

Atomic and molecular studies study

MolecularHamiltonian

class qat.fermion.chemistry.wrapper.MolecularHamiltonian(one_body_integrals: ndarray, two_body_integrals: ndarray, constant_coeff: ndarray)

MolecularHamiltonian helper class. It represents the electronic-structure Hamiltonian defined using one- and two-body integrals.

This electronic-structure Hamiltonian is defined by:

\[H=\sum_{uv\sigma}I_{uv}c^{\dagger}_{u\sigma}c_{v\sigma}+\frac{1}{2}\sum_{uvwx}\sum_{\sigma \sigma'} I_{uvwx}c^{\dagger}_{u\sigma}c^{\dagger}_{v\sigma'}c_{w\sigma'}c_{x\sigma}+r\mathbb{I}\]

with \(r\) the core repulsion constant, and with \(I_{uv}\) and \(I_{uvwx}\) the one- and two-body integrals defined by:

\[ \begin{align}\begin{aligned}I_{uv} = \int dr \phi^{*}_{u}(r)h_{1}[\phi_{v}(r)]\\I_{uvwx} = \int dr dr' \phi^{*}_{u}(r)\phi^{*}_{v}(r')v[\phi_{w}(r)\phi_{x}(r')]\end{aligned}\end{align} \]

Here, \(\{\phi_{i}(r)\}_{i=0...N-1}\) is the single-particle basis, with \(N\) the size, which depends on the basis chosen. \(h_{1} = h_{kin} + h_{pot}\) is the one-body Hamiltonian, and \(v\) the Coulomb operator.

Note

This electronic-structure Hamiltonian definition is different than the one used in ElectronicStructureHamiltonian.

Parameters
  • one_body_integrals (np.ndarray) – One-body integral \(I_{uv}\).

  • two_body_integrals (np.ndarray) – Two-body integral \(I_{uvwx}\).

  • constant_coeff (np.ndarray) – Constant coefficient \(r\) (core repulsion).

nqbits

The total number of qubits.

Type

int

one_body_integrals

One-body integral \(I_{uv}\).

Type

np.ndarray

two_body_integrals

Two-body integral \(I_{uvwx}\).

Type

np.ndarray

constant_coeff

Constant coefficient \(r\) (core repulsion).

Type

np.ndarray

Example

import numpy as np
from qat.fermion.chemistry import MolecularHamiltonian

# Initialize random one- and two-body integrals, and a constant
one_body_integral = np.random.randn(2, 2)
two_body_integral = np.random.randn(2, 2, 2, 2)
constant = np.random.rand()

# Define the MolecularHamiltonian
mol_h = MolecularHamiltonian(one_body_integral, two_body_integral, constant)

print(mol_h)
 MolecularHamiltonian(
 - constant_coeff : 0.4458217765541531
 - integrals shape
    * one_body_integrals : (2, 2)
    * two_body_integrals : (2, 2, 2, 2)
)
get_electronic_hamiltonian() ElectronicStructureHamiltonian

Converts the MolecularHamiltonian to an ElectronicStructureHamiltonian. To do so, it converts from \(I_{uv},I_{uvwx}\) to \(h_{pq},h_{pqrs}\), with

\[ \begin{align}\begin{aligned}h_{u\sigma, v\sigma'} = I_{u, v} \delta_{\sigma, \sigma'}\\h_{u\sigma_1, v\sigma_2, w\sigma_2', x\sigma_1'} = I_{uvwx} \left((1-\delta_{\sigma,\sigma'}) + \delta_{\sigma,\sigma'} (1-\delta_{u,v})(1-\delta_{w,x}) \right)\end{aligned}\end{align} \]

and where the one- and two-body integrals are defined as:

\[I_{uv}\equiv(u|h|v)=\int\mathrm{d}r\phi_{u}^{*}(r)T\phi_{v}(r)\]
\[I_{uvwx}\equiv(ux|vw)=\iint\mathrm{d}r_{1}\mathrm{d}r_{2}\phi_{u}^{*}(r_{1})\phi_{x}(r_{1})v(r_{12})\phi_{v}^{*}(r_{2})\phi_{w}(r_{2})\]

with \(T\) (resp. \(v\)) the one- (resp. two-) body potentials, and \(\phi_u(r)\) is the molecular orbital wavefunction.

The \(h\) integrals are used to construct hamiltonians of the ElectronicStructureHamiltonian type.

Returns

ElectronicStructureHamiltonian Electronic structure hamiltonian.

select_active_space(noons: List[float], n_electrons: int, threshold_1: Optional[float] = 0.02, threshold_2: Optional[float] = 0.001) Tuple[MolecularHamiltonian, List[int], List[int]]

Selects the right active space and freezes core electrons according to their NOONs \(n_i\).

This function is an implementation of the Complete Active Space (CAS) approach. It divides orbital space into sets of active and inactive orbitals, the occupation number of the latter remaining unchanged during the computation.

The active space indices are defined as:

\[\mathcal{A} = \{i, n_i \in [\varepsilon_2, 2 - \varepsilon_1[\} \cup \{i, n_i \geq 2-\varepsilon_1, 2(i+1)\geq N_e \}\]

The inactive occupied orbitals are defined as:

\[\mathcal{O} = \{i, n_i \geq 2 -\varepsilon_1, 2(i+1) < N_e \}\]

The restriction of the one- and two-body integrals (and update of the core energy) is then carried out according to:

\[\forall u,v \in \mathcal{A},\; I^{(a)}_{uv} = I_{uv} + \sum_{i\in \mathcal{O}} 2 I_{i,u,v,i} - I_{i,u,i,v}\]
\[\forall u,v,w,x \in \mathcal{A}, I^{(a)}_{uvwx} = I_{uvwx}\]
\[E_\mathrm{core}^{(a)} = E_\mathrm{core} + \sum_{i\in\mathcal{O}} I_{ii} + \sum_{ij\in\mathcal{O}} 2 I_{ijji} - I_{ijij}\]
Parameters
  • noons (List[float]) – the natural-orbital occupation numbers \(n_i\), sorted in descending order (from high occupations to low occupations)

  • n_electrons (int) – The number of electrons \(N_e\).

  • threshold_1 (Optional[float]) – The upper threshold \(\varepsilon_1\) on the NOON of an active orbital.

  • threshold_2 (Optional[float]) – The lower threshold \(\varepsilon_2\) on the NOON of an active orbital.

Returns

  • the molecular Hamiltonian in active space \(H^{(a)}\)

  • the list of indices corresponding to the active orbitals, \(\mathcal{A}\)

  • the list of indices corresponding to the occupied orbitals, \(\mathcal{O}\)

Return type

Tuple[MolecularHamiltonian, List[int], List[int]]

transform_basis(transformation_matrix: ndarray) MolecularHamiltonian

Change one and two body integrals (indices p, q…) to new basis (indices i, j…) using transformation U such that

\[\hat{c}_{i}=\sum_{q}U_{qi}c_{q}\]

i.e

\[ \begin{align}\begin{aligned}\hat{I}_{ij} =\sum_{pq}U_{pi}I_{pq}U_{jq}^{\dagger}\\\hat{I}_{ijkl}=\sum_{pqrs}U_{pi}U_{qj}I_{pqrs}U_{kr}^{\dagger}U_{ls}^{\dagger}\end{aligned}\end{align} \]
Parameters

transformation_matrix (np.array) – transformation matrix \(U\)

Returns

MolecularHamiltonian updated to the new basis.

Return type

molecular_hamiltonian (MolecularHamiltonian)

MoleculeInfo

class qat.fermion.chemistry.wrapper.MoleculeInfo(hamiltonian: MolecularHamiltonian, n_electrons: int, noons: Union[ndarray, List[float]], orbital_energies: ndarray)

MoleculeInfo helper class. This class is a even higher level version of the MolecularHamiltonian.

Parameters
  • hamiltonian (MolecularHamiltonian) – The MolecularHamiltonian of the studied molecule.

  • n_electrons (int) – Number of electrons.

  • noons (Union[np.ndarray, List[float]]) – Natural orbital occupation number.

  • orbital_energies (np.ndarray) – Orbital energies.

nqbits

The total number of qubits.

Type

int

one_body_integrals

One-body integrals \(I_{uv}\).

Type

np.ndarray

two_body_integrals

Two-body integrals \(I_{uvwx}\).

Type

np.ndarray

constant_coeff

Constant coefficient \(r\) (core repulsion).

Type

np.ndarray

hamiltonian

The MolecularHamiltonian of the studied molecule.

Type

MolecularHamiltonian

n_electrons

Number of electrons.

Type

int

noons

Natural orbital occupation number.

Type

Union[np.ndarray, List[float]]

orbital_energies

Orbital energies.

Type

np.ndarray

Example

import numpy as np
from qat.fermion.chemistry import MolecularHamiltonian, MoleculeInfo

# For illustration purpose, initialize random one- and two-body integrals, and a constant
one_body_integral = np.random.randn(2, 2)
two_body_integral = np.random.randn(2, 2, 2, 2)
constant = np.random.rand()
noons = list(np.random.randn(10))
orbital_energies = list(np.random.randn(10))

# Define the MolecularHamiltonian
mol_h = MolecularHamiltonian(one_body_integral, two_body_integral, constant)

# Define MoleculeInfo
molecule = MoleculeInfo(
    mol_h,
    n_electrons=4,
    noons=noons,
    orbital_energies=orbital_energies
)

print(molecule)
MoleculeInfo(
 - MolecularHamiltonian(
    * constant_coeff : 0.39602295629162243
    * integrals shape
       ** one_body_integrals : (2, 2)
       ** two_body_integrals : (2, 2, 2, 2)
   )
 - n_electrons = 4
 - noons = [-0.186582129027263, -0.6319046952566567, 1.2045296799168843, -0.3710105342909814, 0.06325102218392561, -0.9871636300821159, 2.223023481244258, 1.1106468318529636, -0.6471906814774011, 0.3262667857408898]
 - orbital energies = [1.7337245287275505, -0.24174492479485374, -0.4590523786306649, 0.4280186393763065, 1.925966057671262, -0.12121361475661792, -0.4346778898905092, -0.4371028614926137, 0.9183126448475859, -0.6436276083065122]
)
restrict_active_space(threshold_1: Optional[float] = 0.02, threshold_2: Optional[float] = 0.001)

Same method as the MolecularHamiltonian method select_active_space(), except it also modifies all the molecule parameters accordingly (NOONs, orbital energies, and number of electrons).

For more information, see select_active_space() documentation.

Parameters
  • threshold_1 (Optional[float]) – The upper threshold \(\varepsilon_1\) on the NOON of an active orbital.

  • threshold_2 (Optional[float]) – The lower threshold \(\varepsilon_2\) on the NOON of an active orbital.

Common many-body Hamiltonians

We present here Hamiltonian constructors for common many-body Hamiltonians.

The Hubbard model

qat.fermion.hamiltonians.make_hubbard_model(t_mat: ndarray, U: float, mu: float) ElectronicStructureHamiltonian

Constructs Hubbard model

\[H = \sum_{ij,\sigma} t_{ij} c^\dagger_i c_j + U \sum_i n_{i\uparrow} n_{i \downarrow} - \mu \sum_i n_i\]
Parameters
  • t_mat (np.ndarray) – Hopping matrix (n_sites x n_sites).

  • U (float) – Hubbard U.

  • mu (float) – Chemical potential.

Returns

The Hubbard Hamiltonian.

Return type

ElectronicStructureHamiltonian

Notes

Spin-orbital labeling convention: \(i \equiv (k, \sigma) = 2 k + \sigma\) with \(i\): site index and \(\sigma\): spin index.

The single-impurity Anderson Model

qat.fermion.hamiltonians.make_anderson_model(u: float, mu: float, v: ndarray, epsilon: ndarray) ElectronicStructureHamiltonian

Returns the canonical second quantized form

\[H_{\mathrm{CSQ}} = \sum_{p,q} h_{pq} f_p^\dagger f_q + \frac{1}{2}\sum_{p,q,r,s} h_{pqrs} f_p^\dagger f_q^\dagger f_r f_s\]

of a single impurity coupled with \(n_b\) bath modes Anderson model Hamiltonian

\[\begin{split}H_{\mathrm{SIAM}} = U c_{\uparrow}^\dagger c_{\uparrow} c_{\downarrow}^\dagger c_{\downarrow} - \mu(c_{\uparrow}^\dagger c_{\uparrow}+c_{\downarrow}^\dagger c_{\downarrow}) + \sum_{i=1..n_b} \sum_{\sigma=\uparrow,\downarrow} V_i (c_{\sigma}^\dagger a_{i,\sigma} + \mathrm{h.c.}) \\ + \sum_{i=1..n_b} \sum_{\sigma=\uparrow,\downarrow} \epsilon_i a_{i,\sigma}^\dagger a_{i,\sigma}.\end{split}\]
Parameters
  • U (float) – Coulomb repulsion intensity.

  • mu (float) – Chemical potential.

  • V (np.ndarray) – Tunneling energies. This vector has the same size as the number of bath mode.

  • epsilon (np.ndarray) – Bath modes energies. This vector has the same size as the number of bath mode.

Returns

ElectronicStructureHamiltonian object constructed from \(h_{pq}\) (matrix of size \((2n_b+2) \times (2n_b+2)\)) and \(h_{pqrs}\) (4D tensor with size \(2n_b+2\) in each dimension)

Note

Convention: \(f_0\) corresponds to \(c_{\uparrow}\) (annihilation in the ‘up’ mode of the impurity), \(f_1\) corresponds to \(c_{\downarrow}\) (annihilation in the ‘down’ mode of the impurity), \(f_2\) corresponds to \(a_{1,\uparrow}\) (annihilation in the ‘up’ mode of the 1st bath mode), \(f_3\) corresponds to \(a_{1,\downarrow}\) (annihilation in the ‘down’ mode of the 1st bath mode), and so on.

The generalized impurity Hamiltonian

qat.fermion.hamiltonians.make_embedded_model(u: float, mu: float, D: ndarray, lambda_c: ndarray, t_loc: Optional[ndarray] = None, int_kernel: Optional[ndarray] = None, grouping: Optional[str] = 'spins') ElectronicStructureHamiltonian

Returns the canonical second quantized form

\[H_{\mathrm{CSQ}} = \sum_{p,q} h_{pq} f_p^\dagger f_q + \frac{1}{2}\sum_{p,q,r,s} h_{pqrs} f_p^\dagger f_q^\dagger f_r f_s + c\mathbb{I}\]

of an embedded hamiltonian

\[\begin{split}H_{\mathrm{emb}} = U \sum \limits_{i,j,k,l=1}^{2M} I_{ijkl} f^{\dagger}_i f_j f^{\dagger}_k f_l - \mu \sum \limits_{i=1}^{M} f^{\dagger}_{i} f_{j} + \sum \limits_{i, j=1}^{M} t^{\mathrm{loc}}_{ij} f^{\dagger}_i f_j \\ + \sum \limits_{i,j=1}^{M} (D_{ij} f^{\dagger}_{i} f_{M+j} + \mathrm{h.c.}) \\ + \sum \limits_{i,j=1}^{M} \lambda^c_{ij} f_{M+i} f^{\dagger}_{M+j}\end{split}\]

where \(M\) is the number of orbitals (imp+bath). Indices here correspond to the spin-orbitals ordering referred to as ‘cluster’ (see below).

Parameters
  • U (float) – Onsite repulsion on impurity sites.

  • mu (float) – Chemical potential.

  • D (np.ndarray) – Hopping matrix (i.e. hybridization) between the correlated orbitals and the uncorrelated bath.

  • lambda_c (np.ndarray) – Hopping matrix of the uncorrelated sites.

  • t_loc (Optional[np.ndarray]) – Hopping matrix of the correlated sites.

  • int_kernel (Optional[np.ndarray]) – Array \(I\) with 1 at position \(i, j, k, l\) where \(U\) must be put (conv. for associated term: \(c^{\dagger}c^{\dagger}cc\)). Defaults to None, in which case \(U\) is put before terms \(c^{\dagger}_{2i}c^{\dagger}_{2i+1}c_{2i}c_{2i+1}, i=1..M/2\) if grouping is ‘clusters’, \(c^{\dagger}_{i}c^{\dagger}_{i+M}c_{i}c_{i+M}, i=1..M/2\) if grouping is ‘spins’. This array must be a 4D array.

  • grouping (Optional[str]) – Defines how spin-orbitals indices are ordered (see below), defaults to ‘spins’.

Returns

ElectronicStructureHamiltonian

The two grouping strategies are the following:

  • “clusters”: the first \(M\) orbitals SO are \((\uparrow, \mathrm{imp}_0), (\downarrow, \mathrm{imp}_0),..., (\uparrow, \mathrm{imp}_{M-1}), (\downarrow, \mathrm{imp}_{M-1})\) and the last \(M\) orbitals are bath orbitals with similar ordering.

  • “spins”: the first \(M\) orbitals are \((\uparrow, \mathrm{imp}_0), (\uparrow, \mathrm{imp}_1), ..., (\uparrow, \mathrm{bath}_{M-2}), (\uparrow, \mathrm{bath}_{M-1})\) and the last \(M\) orbitals are down orbitals with similar ordering.

The spin-fermion transforms

Jordan-Wigner transform

qat.fermion.transforms.transform_to_jw_basis(fermion_hamiltonian: Union[FermionHamiltonian, ElectronicStructureHamiltonian]) SpinHamiltonian

Transform to Jordan-Wigner (JW) basis.

Parameters

fermion_hamiltonian (Union[FermionHamiltonian, ElectronicStructureHamiltonian]) – The fermionic hamiltonian.

Returns

Hamiltonian in spin representation.

Return type

SpinHamiltonian

Examples:

from qat.core import Term
from qat.fermion import FermionHamiltonian
from qat.fermion.transforms import transform_to_jw_basis

hamiltonian = FermionHamiltonian(
    2, [Term(0.3, "Cc", [0, 1]), Term(1.4, "CcCc", [0, 1, 1, 0])])

spin_hamiltonian = transform_to_jw_basis(hamiltonian)

print(f"H = {hamiltonian} \n")
print(f"H(spin) = {spin_hamiltonian}")
H = 0.3 * (Cc|[0, 1]) +
1.4 * (Cc|[0, 0]) +
1.4 * (CCcc|[0, 1, 0, 1]) 

H(spin) = (0.35+0j) * I^2 +
-0.075j * (YX|[0, 1]) +
(0.075+0j) * (YY|[0, 1]) +
(0.075+0j) * (XX|[0, 1]) +
0.075j * (XY|[0, 1]) +
(-0.35+0j) * (ZZ|[0, 1]) +
(-0.35+0j) * (Z|[0]) +
(0.35+0j) * (Z|[1])

Bravyi-Kitaev transform

qat.fermion.transforms.transform_to_bk_basis(fermion_hamiltonian: Union[FermionHamiltonian, ElectronicStructureHamiltonian]) SpinHamiltonian

Transform to Bravyi-Kitaev (BK) basis.

Parameters

fermion_hamiltonian (Union[FermionHamiltonian, ElectronicStructureHamiltonian]) – The fermionic hamiltonian.

Returns

Hamiltonian in BK spin representation.

Return type

SpinHamiltonian

Examples:

from qat.core import Term
from qat.fermion import FermionHamiltonian
from qat.fermion.transforms import transform_to_bk_basis

hamiltonian = FermionHamiltonian(
    2, [Term(0.3, "Cc", [0, 1]), Term(1.4, "CcCc", [0, 1, 1, 0])])

spin_hamiltonian = transform_to_bk_basis(hamiltonian)

print(f"H = {hamiltonian} \n")
print(f"H(spin) = {spin_hamiltonian}")
H = 0.3 * (Cc|[0, 1]) +
1.4 * (Cc|[0, 0]) +
1.4 * (CCcc|[0, 1, 0, 1]) 

H(spin) = (0.35+0j) * I^2 +
-0.075j * (Y|[0]) +
(-0.075+0j) * (XZ|[0, 1]) +
(0.075+0j) * (X|[0]) +
0.075j * (YZ|[0, 1]) +
(-0.35+0j) * (Z|[1]) +
(-0.35+0j) * (Z|[0]) +
(0.35+0j) * (ZZ|[0, 1])

Parity basis transform

qat.fermion.transforms.transform_to_parity_basis(fermion_hamiltonian: Union[FermionHamiltonian, ElectronicStructureHamiltonian]) SpinHamiltonian

Transform to parity basis.

Parameters

fermion_hamiltonian (Union[FermionHamiltonian, ElectronicStructureHamiltonian]) – The fermionic hamiltonian.

Returns

Hamiltonian in parity spin representation.

Return type

SpinHamiltonian

Examples:

from qat.core import Term
from qat.fermion import FermionHamiltonian
from qat.fermion.transforms import transform_to_parity_basis

hamiltonian = FermionHamiltonian(
    2, [Term(0.3, "Cc", [0, 1]), Term(1.4, "CcCc", [0, 1, 1, 0])])

spin_hamiltonian = transform_to_parity_basis(hamiltonian)

print(f"H = {hamiltonian} \n")
print(f"H(spin) = {spin_hamiltonian}")
H = 0.3 * (Cc|[0, 1]) +
1.4 * (Cc|[0, 0]) +
1.4 * (CCcc|[0, 1, 0, 1]) 

H(spin) = (0.35+0j) * I^2 +
-0.075j * (Y|[0]) +
(-0.075+0j) * (XZ|[0, 1]) +
(0.075+0j) * (X|[0]) +
0.075j * (YZ|[0, 1]) +
(-0.35+0j) * (Z|[1]) +
(-0.35+0j) * (Z|[0]) +
(0.35+0j) * (ZZ|[0, 1])

Fermionic ansatz circuits

Low-Depth Circuit Ansatz (LDCA)

qat.fermion.circuits.make_ldca_circ(nb_fermionic_modes: int, ncycles: int, eigstate_ind: Optional[int] = 0, slater: Optional[bool] = False) Circuit

Construct a LDCA circuit (see article by P. Dallaire-Demers et al. (2019)), applying ncycles layers of matchgates routines on nb_fermionic_modes qubits.

Parameters
  • nb_fermionic_modes (int) – Number of qubits.

  • ncycles (int) – Number of LDCA cycles.

  • eigstate_ind (int, optional) – Eigenstate index. Defaults to 0.

  • slater (Optional[bool]) – Whether to only include excitation-preserving rotations. Defaults to False.

Returns

Circuit

Multi-Reference (MR) ansatz

qat.fermion.circuits.make_mr_circ() Circuit

Builds a small, one-parameter Multi-Reference (MR) circuit on 4 qubits inspired from Sugisaki et al. article (2019) to prepare states in natural orbitals.

Returns

Circuit

Multi-Reference, Excitation-Preserving (MREP) ansatz

qat.fermion.circuits.make_mrep_circ(n_fsim_cycles: Optional[int] = 4, set_phi_to_0: Optional[bool] = False) Circuit

Constructs the 8-qubit Multi-Reference Excitation Preserving (MREP) ansatz that combines the multi-reference routine of Sugisaki et al. article (2019) with some fSim nearest-neighbour cycles. The second angles of the fSim gates (phi) may be taken to 0.

Parameters
  • n_fsim_cycles (int, optional) – Number of fSim cycles, defaults to 4.

  • set_phi_to_0 (bool, optional) – Whether to set all second angles in the fSim gates to 0 (True) or not (False). Defaults to False.

Returns

Circuit

General hardware-efficient ansatz

qat.fermion.circuits.make_general_hwe_circ(nqbits: int, n_cycles: int = 1, rotation_gates: ~typing.Optional[~typing.List[~qat.lang.AQASM.gates.Gate]] = None, entangling_gate: ~qat.lang.AQASM.gates.Gate = <qat.lang.AQASM.gates.PredefGate object>) Circuit

Constructs an ansatz made of \(n_{\mathrm{cycles}}\) layers of so-called thinly-dressed routines, that is to say entanglers surrounded by four one-qubit rotations are applied on nearest-neighbour qubits in an odd/even alternating pattern.

This circuit is typically of the hardware-efficient class.

Parameters
  • nqbits (int) – Number of qubits of the circuit.

  • n_cycles (int) – Number of layers.

  • rotation_gates (List[Gate]) – Parametrized rotation gates to include around the entangling gate. Defaults to \(RY\). Must be of arity 1.

  • entangling_gate (Gate) – The 2-qubit entangler. Must be of arity 2. Defaults to \(CNOT\).

Returns

Circuit

The 8-parameter circuit ansatz

qat.fermion.circuits.make_shallow_circ() Circuit

Builds the 8-parameter circuit proposed in Keen et al. article (2019). This is a 4-qubit circuit.

Returns

Circuit

Compressed LDCA ansatz (only for QLM users)

qat.fermion.circuits.make_compressed_ldca_circ(nb_fermionic_modes: int, ncycles: int, eigstate_ind: Optional[int] = 0, slater: Optional[bool] = False) Circuit

Builds a compressed version of the LDCA ansatz circuit.

The new pattern was obtained using qat.synthopline.

Parameters
  • nb_fermionic_modes (int) – Number of qubits.

  • ncycles (int) – Number of LDCA cycles.

  • eigstate_ind (Optional[int]) – Eigenstate index. Defaults to 0.

  • slater (Optional[bool]) – Whether to only include excitation-preserving rotations. Defaults to False.

Return: Circuit

Quantum phase estimation

qat.fermion.phase_estimation.perform_phase_estimation(H_el: ElectronicStructureHamiltonian, n_phase_bits: int, n_trotter_steps: int, init_vec: Optional[str] = None, n_adiab_steps: Optional[int] = 0, E_target: Optional[float] = 0, size_interval: Optional[float] = 2.0, basis_transform: Optional[str] = 'jordan-wigner', qpu=None, n_shots: Optional[int] = 0, verbose: Optional[bool] = False) Tuple[float, float]

Perform quantum phase estimation (QPE) on an ElectronicStructureHamiltonian. This Hamiltonian is transformed to the computational basis via a Jordan-Wigner transformation and approximated via first order trotterization. Other transformations like parity and Bravyi-Kitaev are also possible.

When providing an initial state one can specify it either as a string composed of zeros and ones, or as a QRoutine which will produce it. The QPE is meant to start from an eigenstate of the Hamiltonian, however, knowing apriori even one eigenstate of the system may be challenging. Therefore, this function comes with adiabatic state preparation - an optional preliminary step to create an eigenstate of the Hamiltonian \(H\). This step consists in performing QPE n_adiab_steps number of times, but not to read the phase bits (it is set to only one), but rather to collapse the system to an eigenstate (read from the data bits). The first of the series of QPE executions starts from the lowest energy eigenstate of the Hamiltonian composed of \(h_{pq}\). Then, \(h_{pq}\) is linearly transformed to \(H\) and at each new step we start from the eigenstate of the Hamiltonian of the previous step. This guarantees that when the main QPE routine starts, it will do so from an eigenstate of the full \(H\).

Usually, energies lie outside the range \((-\frac{2\pi}{t}, 0)\). However, this range can be adjusted by searching inside the window \((E_{target} - \frac{\Delta}{2}, E_{target} + \frac{\Delta}{2})\) with \(E_{target}\) and \(\Delta\) specified by E_target and size_interval, respectively. It is suggested to always start from a large size interval and unbiased target energy like \(0\) thus enclosing many of the eigenenergies including the desired one. One can then narrow the window around an already found eigenenergy for a better precision. Working with a window not enclosing an eigenenergy would still evaluate to a result, but it may be misleading.

Warning

  • Regarding the adiabatic state preparation, if the lowest energy eigenstate of the first-step Hamiltonian \(h_{pq}\) is also an eigenstate of the whole \(H\), the system will remain in it until the end of the whole adiabatic stage. Hence, this eigenstate may not be the one of the lowest energy anymore.

  • As a rule of thumb, if small changes to the interval cause considerable deviations in the energy, that’s a sign that the window is too small or a different target energy may be better.

Parameters
  • H_el (ElectronicStructureHamiltonian) – An electronic-structure Hamiltonian.

  • n_phase_bits (int) – Number of qubits for the phase evaluation. The larger it is, the more accurate is the result.

  • n_trotter_steps (int) – Number of first order trotterization steps. For good phase estimation it should also increase if n_phase_bits is increased.

  • init_vec (Optional[str]) – Initial vector specified in the computational basis as a string - ‘01101’ for example. Starting from |0..0> an X will be applied to the respective qubits so as to produce the provided vector. This vector will enter the adiabatic state preparation routine if n_adiab_steps is not 0 or will be given straight to the main QPE routine.

  • n_adiab_steps (Optional[int]) – Number of steps to pass from the part of the Hamiltonian containing only c_p^dagger * c_p terms (which is diagonal and fast to deal with) to the Hamiltonian of interest.

  • E_target (Optional[float]) – Expected energy. If unknown, set to 0.

  • size_interval (Optional[float]) – Size \(\Delta\) of the interval one thinks the value of the energy is in: \(E \in [E_\mathrm{target}-\Delta/2, E_\mathrm{target}+\Delta/2]\) If no idea take \(\Delta =2 E_\mathrm{max}\), with \(E_\mathrm{max}\) an upper bound of the energy.

  • basis_transform (Optional[str]) – Transformation to go from qat.fermion.hamiltonians.ElectronicStructureHamiltonian into a qat.fermion.hamiltonians.SpinHamiltonian: one can use the “jordan-wigner” (default), “bravyi-kitaev” or “parity” transformations.

  • qpu (Optional[QPU]) – QPU to use for computation. Will use by default the default installed QPU.

Returns

  • Energy found,

  • associated probability.

Return type

Tuple[float, float]

Note

Usually, energies lie outside the range \((-\frac{2\pi}{t}, 0)\). However, this range can be adjusted by specifying the arguments E_target and size_interval thus searching inside the window \((E_{t} - \frac{\Delta}{2}, E_{target} + \frac{size\_interval}{2})\), where \(E_{t}\) and \(\Delta\) stand for . We suggest to always start from a large size interval and unbiased target energy like 0 thus enclosing many of the eigenenergies including the desired one. One can then narrow the window around an already found eigenenergy for a better precision. Experience shows that working with a window not enclosing an eigenenergy makes the QPE still output a result, but it is misleading.

Quantum subspace expansion

qat.fermion.chemistry.qse.apply_quantum_subspace_expansion(hamiltonian: SpinHamiltonian, state_prep_circ: Circuit, expansion_operators: List[Observable], qpu: QPUHandler, nbshots: int = 0, threshold: float = 1e-15, return_matrices: bool = False) Tuple[float, Optional[ndarray], Optional[ndarray]]

Apply quantum subspace expansion (QSE) to the given Hamiltonian.

QSE is a method that improves the quality of noisy results at the cost of additional measurements that help write the Hamiltonian in the small subspace where it can be diagonalized classically.

If \(\langle \Psi^\star | \hat{H} | \Psi^\star \rangle\) is the VQE result, the projected Hamiltonian matrix is built from

\[H^{(s)}_{i,j} = \langle \Psi^\star | \hat{O}_i^\dagger \hat{H} \hat{O}_j | \Psi^\star \rangle\]

where \(\hat{O}_i\) is an element of expansion_operators.

Then the generalized eigenvalue problem

\[H^{(s)} \vec{x} = E S \vec{x}\]

is solved, with \(S\) the overlap matrix:

\[S_{i,j} = \langle \Psi^\star | \hat{O}_i^\dagger \hat{O}_j | \Psi^\star \rangle\]
Parameters
  • hamiltonian (SpinHamiltonian) – The Hamiltonian in its spin representation.

  • state_prep_circ (Circuit) – The state preparation circuit.

  • expansion_operators (list<SpinHamiltonian>) – The set of operators \({O_i}_i\) generating the subspace of interest.

  • qpu (QPUHandler) – The QPU.

  • nbshots (int, optional) – The number of shots. Defaults to 0: infinite number of shots.

  • threshold (float, optional) – The numerical threshold.

  • return_matrices (bool, optional) – If set to True, the function returns the matrices \(H^{(s)}\) and \(S\). Defaults to False.

Returns

Improved energy provided by the QSE procedure. matrix_h (Optional[np.ndarray]): The subspace Hamiltonian \(H^{(s)}\). Only if return_matrices is True. matrix_s (Optional[np.ndarray]): The overlap matrix \(S\). Only if return_matrices is True.

Return type

e_qse (float)

Example:

import numpy as np
from qat.core import Term
from qat.fermion import SpinHamiltonian
from qat.lang.AQASM import Program, RY, CNOT, RZ
from qat.qpus import get_default_qpu
from qat.plugins import SeqOptim

# We instantiate the Hamiltonian we want to approximate the ground state energy of
hamiltonian = SpinHamiltonian(2, [Term(1, op, [0, 1]) for op in ["XX", "YY", "ZZ"]])

# We construct the variational circuit (ansatz)
prog = Program()
reg = prog.qalloc(2)
theta = [prog.new_var(float, '\\theta_%s'%i) for i in range(3)]
RY(theta[0])(reg[0])
RY(theta[1])(reg[1])
RZ(theta[2])(reg[1])
CNOT(reg[0], reg[1])
circ = prog.to_circ()

# Construct a (variational) job with the variational circuit and the observable
job = circ.to_job(observable=hamiltonian,
                nbshots=0)

# We now build a stack that can handle variational jobs
qpu = get_default_qpu()

optimizer = SeqOptim(ncycles=10, x0=[0, 0.5, 0])
stack = optimizer | qpu

# We submit the job and print the optimized variational energy (the exact GS energy is -3)
result = stack.submit(job)
E_min = -3
print("E(VQE) = %s (err = %s %%)"%(result.value, 100*abs((result.value-E_min)/E_min)))
e_vqe = result.value

# We use the optimal parameters found by VQE
opt_circ = circ.bind_variables(eval(result.meta_data["parameter_map"]))

expansion_operators = [SpinHamiltonian(2, [], 1.0),
                    SpinHamiltonian(2, [Term(1., "ZZ", [0, 1])])]

from qat.fermion.chemistry.qse import apply_quantum_subspace_expansion
e_qse = apply_quantum_subspace_expansion(hamiltonian,
                                        opt_circ,
                                        expansion_operators,
                                        qpu,
                                        return_matrices=False)

print("E(QSE) = %s (err = %s %%)"%(e_qse, 100*abs((e_qse-E_min)/E_min)))
E(VQE) = -3.0 (err = 0.0 %)
E(QSE) = -3.0 (err = 0.0 %)

Unitary Coupled-Cluster (UCC)

qat.fermion.chemistry.ucc.transform_integrals_to_new_basis(one_body_integrals: ndarray, two_body_integrals: ndarray, u_mat: ndarray) Tuple[ndarray, ndarray]

Change one and two body integrals (indices p, q…) to new basis (indices i, j…) using transformation U such that

\[\hat{c}_{i}=\sum_{q}U_{qi}c_{q}\]

i.e

\[\hat{I}_{ij} =\sum_{pq}U_{pi}I_{pq}U_{jq}^{\dagger} \hat{I}_{ijkl}=\sum_{pqrs}U_{pi}U_{qj}I_{pqrs}U_{kr}^{\dagger}U_{ls}^{\dagger}\]
Parameters
  • one_body_integrals (np.ndarray) – One-body integrals \(I_{pq}\).

  • two_body_integrals (np.ndarray) – Two-body integrals \(I_{pqrs}\).

  • u_mat (np.ndarray) – Transformation matrix \(U\).

Returns

  • h_hat_ij (np.ndarray): One-body integrals \(\hat{I}_{ij}\).

  • h_hat_ijkl (np.ndarray): Two-body integrals \(\hat{I}_{ijkl}\).

Return type

Tuple[np.ndarray, np.ndarray]

qat.fermion.chemistry.ucc.compute_active_space_integrals(one_body_integrals: ndarray, two_body_integrals: ndarray, active_indices: List[int], occupied_indices: List[int]) Tuple[ndarray, ndarray, float]

Restrict one- and two-body integrals for given list of active indices.

\[ \begin{align}\begin{aligned}\forall u,v\in \mathcal{A},\; I^{(a)}_{uv} = I_{uv} + \sum_{i\in \mathcal{O}} 2 I_{i,u,v,i} - I_{i,u,i,v}\\\forall u,v,w,x \in \mathcal{A}, I^{(a)}_{uvwx} = I_{uvwx}\\c^{(a)} = c + \sum_{i\in\mathcal{O}} I_{ii} + \sum_{ij\in\mathcal{O}} 2I_{ijji} - I_{ijij}\end{aligned}\end{align} \]
Parameters
  • one_body_integrals (np.ndarray) – Array of one-body integrals \(I_{uv}\). Must be 2D.

  • two_body_integrals (np.ndarray) – Array of two-body integrals \(I_{uvwx}\). Must be 4D.

  • active_indices (List[int]) – Active indices.

  • occupied_indices (List[int]) – Occupied indices.

Returns

  • 2D array of one-body integrals \(I_{uv}^{(a)}\),

  • 4D array of two-body integrals \(I_{uvwx}^{(a)}\),

  • core constant \(c^{(a)}\).

Return type

Tuple[np.ndarray, np.ndarray, float]

qat.fermion.chemistry.ucc.convert_to_h_integrals(one_body_integrals: ndarray, two_body_integrals: ndarray) Tuple[ndarray, ndarray]

Convert from \(I_{uv},I_{uvwx}\) to \(h_{pq},h_{pqrs}\), with

\[ \begin{align}\begin{aligned}h_{u\sigma, v\sigma'} = I_{u, v} \delta_{\sigma, \sigma'}\\h_{u\sigma_1, v\sigma_2, w\sigma_2', x\sigma_1'} = I_{uvwx} \left((1-\delta_{\sigma,\sigma'}) + \delta_{\sigma,\sigma'} (1-\delta_{u,v})(1-\delta_{w,x}) \right)\end{aligned}\end{align} \]

and where the one- and two-body integrals are defined as:

\[I_{uv}\equiv(u|h|v)=\int\mathrm{d}r\phi_{u}^{*}(r)T\phi_{v}(r)\]
\[I_{uvwx}\equiv(ux|vw)=\iint\mathrm{d}r_{1}\mathrm{d}r_{2}\phi_{u}^{*}(r_{1})\phi_{x}(r_{1})v(r_{12})\phi_{v}^{*}(r_{2})\phi_{w}(r_{2})\]

with \(T\) (resp. \(v\)) the one- (resp. two-) body potentials, and \(\phi_u(r)\) is the molecular orbital wavefunction.

The \(h\) integrals are used to construct hamiltonians of the ElectronicStructureHamiltonian type.

Parameters
  • one_body_integrals (np.ndarray) – Array of one-body integrals \(I_{uv}\). Must be 2D.

  • two_body_integrals (np.ndarray) – Array of two-body integrals \(I_{uvwx}\). Must be 4D.

Returns

  • the \(h_{pq}\) integrals,

  • the \(h_{pqrs}\) integrals.

Return type

Tuple[np.ndarray, np.ndarray]

qat.fermion.chemistry.ucc.construct_ucc_ansatz(cluster_ops: List[SpinHamiltonian], ket_hf: int, n_steps: int = 1) Program

Builds the parametric state preparation circuit implementing the provided cluster operator.

The returned function maps \(\vec{\theta}\) to a QRoutine describing \(Q\) such as:

\[Q \vert \vec{0} \rangle = \vert \mathrm{UCC} (\vec{\theta}) \rangle = e^{T(\vec{\theta})} \vert \mathrm{HF}\rangle\]
Parameters
  • cluster_ops (List[SpinHamiltonian]) – The cluster operators iT (note the i factor).

  • ket_hf (int) – The Hartree-Fock state in integer representation.

  • n_steps (int) – Number of trotter steps.

Returns

The parametric program implementing the UCCSD method.

Return type

Program

qat.fermion.chemistry.ucc.select_active_orbitals(noons: List[float], n_electrons: int, threshold_1: Optional[float] = 0.02, threshold_2: Optional[float] = 0.001) Tuple[List[int], List[int]]

Selects the right active space and freezes core electrons according to their NOONs.

This function is an implementation of the Complete Active Space (CAS) approach. It divides orbital space into sets of active and inactive orbitals, the occupation number of the latter remaining unchanged during the computation.

Parameters
  • noons (np.ndarray) – The natural orbital occupation numbers in descending order (from high occupations to low occupations)

  • n_electrons (int) – The number of electrons.

  • threshold_1 (Optional[float]) – The upper threshold \(\varepsilon_1\) on the NOON of an active orbital. Defaults to 0.02.

  • threshold_2 (Optional[float]) – The lower threshold \(\varepsilon_2\) on the NOON of an active orbital. Defaults to 0.001.

Returns

  • active_so (List[int]): The list of active spatial orbitals.

  • inactive_occupied_so (List[int]): The list of core spatial orbitals.

Return type

Tuple[List[int], List[int]]

qat.fermion.chemistry.ucc.guess_init_params(two_body_integrals: ndarray, n_electrons: int, orbital_energies: List[float], noons: Optional[List[float]] = None) List[float]

Find initial parameters using Møller-Plesset perturbation theory.

The trial parametrization is efficiently improved upon the Hartree-Fock solution (which would set every initial parameter to zero) thanks to the following formula identifying the UCC parameters in the Møller-Plesset (MP2) solution :

\[\theta_a^i = 0\]
\[\theta_{a, b}^{i, j} = \frac{h_{a, b, i, j} - h_{a, b, j, i}}{\epsilon_i + \epsilon_j -\epsilon_a - \epsilon_b}\]

where \(h_{p, q, r, s}\) is the 2-electron molecular orbital integral, and \(\epsilon_i\) is the orbital energy.

Parameters
  • two_body_integrals (np.ndarray) – 4D array of two-body integrals \(I_{uvwx}\).

  • n_electrons (int) – The number of active electrons of the system.

  • noons (List[float]) – the natural-orbital occupation numbers \(n_i\), sorted in descending order (from high occupations to low occupations) (doubled due to spin degeneracy).

  • orbital_energies (List[float]) – The energies of the molecular orbitals \(\epsilon_i\) (doubled due to spin degeneracy).

Returns

The list of initial coefficients \(\{\theta_{a}^{i}, a \in \mathcal{I}', i \in \mathcal{O}' \} \cup \{\theta_{ab}^{ij}, a>b, i>j, a,b \in \mathcal{I}', i,j \in \mathcal{O}'\}\),

Return type

theta_list (List[float])

qat.fermion.chemistry.ucc.get_hf_ket(n_electrons: int, nqbits: int) int

Get Hartree-Fock state stored as a vector with right-to-left orbitals indexing.

Parameters
  • n_electrons (int) – The number of active electrons of the system.

  • nqbits – The number of qubits.

Returns

Hartree-Fock state.

Return type

int

qat.fermion.chemistry.ucc.get_cluster_ops(n_electrons: int, nqbits: Optional[int] = None, noons: Optional[List[float]] = None) List[FermionHamiltonian]

Compute the cluster operators.

Parameters
  • n_electrons (int) – The number of active electrons of the system.

  • nqbits (Optional[int]) – The number of qubits.

  • noons (Optional[List[float]]) – The natural-orbital occupation numbers \(n_i\), sorted in descending order (from high occupations to low occupations) (doubled due to spin degeneracy).

Returns

The list of cluster operators \(\{T_{a}^{i}, a \in \mathcal{I}', i \in \mathcal{O}' \} \cup \{T_{ab}^{ij}, a>b, i>j, a,b \in \mathcal{I}', i,j \in \mathcal{O}'\}\)

Return type

List[FermionHamiltonian]

Note

This function accepts as input the number of qubits or the noons. One of them is needed for the computation of the cluster operators. n_electrons and n_qbits must be pair.

Utility functions

qat.fermion.trotterisation.make_trotterisation_routine(hamiltonian: Union[SpinHamiltonian, FermionHamiltonian, ElectronicStructureHamiltonian], n_trotter_steps: int, final_time: Optional[float] = 1.0, method: Optional[str] = 'jordan-wigner') QRoutine

This function first trotterizes the evolution operator \(e^{-i H t}\) of a Hamiltonian \(H\) using a first order approximation. If the Hamiltonian is fermionic, it is converted to its spin representation.

Parameters
  • hamiltonian (Union[SpinHamiltonian, FermionHamiltonian, ElectronicStructureHamiltonian]) – Hamiltonian to trotterize.

  • n_trotter_steps (int) – Number \(n\) of Trotter steps.

  • final_time (Optional[float]) – Time \(t\) in the evolution operator.

  • method (Optional[str]) – Method to use for the transformation to a spin representation. Other available methods include "bravyi-kitaev" and "parity". Defaults to "jordan-wigner".

Returns

Gates to apply to perform the time evolution of the chemical Hamiltonian with trotterisation.

Return type

QRoutine

Notes

  • In the fermionic case :

    \[e^{-i H t} \approx \prod_{k=1}^{n} \left( \prod_{pq} e^{-i \frac{t}{n} h_{pq} c_p^\dagger c_q} \prod_{pqrs} e^{-\frac{i}{2}\frac{t}{n} h_{pqrs} e^{-i c_p^\dagger c_q^\dagger c_r c_s} } \right)\]

    This operator is then mapped to a product of Pauli operators via a Jordan-Wigner transformation and the resulting QRoutine is returned.

  • The QRoutine implements a first order Trotter approximation, but higher order approximations are possible.

qat.fermion.chemistry.pyscf_tools.perform_pyscf_computation(geometry: list, basis: str, spin: int, charge: int, run_fci: bool = False)

Perform various calculations using PySCF. This function is a helper function meant to kickstart molecule studies. Its use is completely optional, and using other methods or packages is entirely possible.

This function will compute:

  • The reduced density matrix,

  • The orbital energies,

  • The nuclear repulsion constant,

  • The number of electrons,

  • The one- and two-body integrals,

  • The groundstate energies obtained through Hartree-Fock and 2nd order Möller-Plesset perturbation approach,

  • (Optional) The groundstate energy using the full configuration interaction (full CI) approach.

Note

  • The FCI computation is very expensive for big molecules. Enable it only for small molecules !

Parameters
  • geometry (list) –

    Defines the molecular structure. The internal format is PySCF format:

    atom = [[atom1, (x, y, z)],
            [atom2, (x, y, z)],
            ...
            [atomN, (x, y, z)]]
    

  • basis (str) – Defines the basis set.

  • spin (int) – 2S, number of alpha electrons - number beta electrons to control multiplicity. If spin is None, multiplicity

  • molecule. (will be guessed based on the neutral) –

  • charge (int) – Charge of molecule. Affects the electron numbers.

  • run_fci (bool, optional) – Whether the groundstates energies should also be computed using a full CI approach. Defaults to False.

Returns

  • rdm1 (np.ndarray): Reduced density matrix.

  • orbital_energies (list): List of orbital energies.

  • nuclear_repulsion (float): Nuclear repulsion constant.

  • nels (int): Number of electrons.

  • one_body_integrals (np.ndarray): One-body integral.

  • two_body_integrals (np.ndarray): Two-body integral.

  • info (dict): Dictionary containing the Hartree-Fock and 2nd order Möller-Plesset computed ground state energies (and optionally the Full CI energy if run_fci is set to True).

Return type

Tuple[np.ndarray, list, float, int, np.ndarray, np.ndarray, dict]

Plugins