mindquantum.hiqfermion

transform

class mindquantum.hiqfermion.transforms.Transform(operator, n_qubits=None)[source]

Class for transforms of fermionic and qubit operators. Methods jordan_wigner, parity, bravyi_kitaev, bravyi_kitaev_tree, bravyi_kitaev_superfast make transform of fermionic operators to qubit ones, they are initialized by FermionOperator, return QubitOperator. Note method reversed_jordan_wigner makes transform of qubit operator to fermionic one, it is initialized by QubitOperator, returns FermionOperator.

Parameters
  • operator (Union[FermionOperator, QubitOperator]) – The input FermionOperator or QubitOperator that need to do transform.

  • n_qubits (int) – The total qubits of this operator. Default: None

Examples

>>> from mindquantum.ops import FermionOperator
>>> op1 = FermionOperator('1^')
>>> op1
1.0 [1^]
>>> from mindquantum.hiqfermion.transforms.transform import Transform
>>> op_transform = Transform(op1)
>>> op_transform.jordan_wigner()
0.5 [Z0 X1] +
-0.5j [Z0 Y1]
>>> op_transform.parity()
0.5 [Z0 X1] +
-0.5j [Y1]
>>> op_transform.bravyi_kitaev()
0.5 [Z0 X1] +
-0.5j [Y1]
>>> op_transform.ternary_tree()
0.5 [X0 Z1] +
-0.5j [Y0 X2]
>>> op2 = FermionOperator('1^', 'a')
>>> Transform(op2).jordan_wigner()
0.5*a [Z0 X1] +
-0.5*I*a [Z0 Y1]
bravyi_kitaev()[source]

Apply Bravyi-Kitaev transform.

The Bravyi-Kitaev basis is a middle between Jordan-Wigner and parity transform. That is, it balances the locality of occupation and parity information for improved simulation efficiency. In this scheme, qubits store the parity of a set of \(2^x\) orbitals, where \(x \ge 0\). A qubit of index j always stores orbital \(j\). For even values of \(j\), this is the only orbital that it stores, but for odd values of \(j\), it also stores a certain set of adjacent orbitals with index less than \(j\). For the occupation transformation, we follow the formular:

\[b_{i} = \sum{[\beta_{n}]_{i,j}} f_{j},\]

where \(\beta_{n}\) is the \(N\times N\) square matrix, \(N\) is the total qubit number. The qubits index are divide into three sets, the parity set, the update set and flip set. The parity of this set of qubits has the same parity as the set of orbitals with index less than \(j\), and so we will call this set of qubit indices the “parity set” of index \(j\), or \(P(j)\).

the update set of index \(j\), or \(U(j)\) contains the set of qubits (other than qubit \(j\)) that must be updated when the occupation of orbital \(j\) This is the set of qubits in the Bravyi-Kitaev basis that store a partial sum including orbital \(j\). the flip set of index \(j\), or \(F(j)\) contains the set of BravyiKitaev qubits determines whether qubit \(j\) has the same parity or inverted parity with respect to orbital \(j\).

Please see some detail explanation in the paper (THE JOURNAL OF CHEMICAL PHYSICS 137, 224109 (2012)).

Implementation from https://arxiv.org/pdf/quant-ph/0003137.pdf and “A New Data Structure for Cumulative Frequency Tables” by Peter M. Fenwick.

Returns

QubitOperator, qubit operator after bravyi_kitaev transformation.

bravyi_kitaev_superfast()[source]

Apply Bravyi-Kitaev Superfast transform. Implementation from https://arxiv.org/pdf/1712.00446.pdf

Note that only hermitian operators of form

\[C + \sum_{p, q} h_{p, q} a^\dagger_p a_q + \sum_{p, q, r, s} h_{p, q, r, s} a^\dagger_p a^\dagger_q a_r a_s\]

where \(C\) is a constant, be transformed.

Returns

QubitOperator, qubit operator after bravyi_kitaev_superfast.

jordan_wigner()[source]

Apply Jordan-Wigner transform. The Jordan-Wigner transform holds the initial occupation number locally. which change the formular of fermion operator into qubit operator following the equation.

\[ \begin{align}\begin{aligned}a^\dagger_{j}\rightarrow \sigma^{-}_{j} X \prod_{i=0}^{j-1}\sigma^{Z}_{i}\\a_{j}\rightarrow \sigma^{+}_{j} X \prod_{i=0}^{j-1}\sigma^{Z}_{i},\end{aligned}\end{align} \]

where the \(\sigma_{+}= \sigma^{X} + i \sigma^{Y}\) and \(\sigma_{-} = \sigma^{X} - i\sigma^{Y}\) is the Pauli spin raising and lowring operator.

Returns

QubitOperator, qubit operator after jordan_wigner transformation.

parity()[source]

Apply parity transform. The parity transform stores the initial occupation number nonlocally. with the formular:

\[\left|f_{M−1}, f_{M−2},\cdots, f_0\right> → \left|q_{M−1}, q_{M−2},\cdots, q_0\right>,\]

where

\[q_{m} = \left|\left(\sum_{i=0}^{m-1}f_{i}\right) mod\ 2 \right>\]

Basically, this formular could be written as this,

\[p_{i} = \sum{[\pi_{n}]_{i,j}} f_{j},\]

where \(\pi_{n}\) is the \(N\times N\) square matrix, \(N\) is the total qubit number. The operator changes follows the following equation as:

\[ \begin{align}\begin{aligned}a^\dagger_{j}\rightarrow\frac{1}{2}\left(\prod_{i=j+1}^N \left(\sigma_i^X X\right)\right)\left( \sigma^{X}_{j}-i\sigma_j^Y\right) X \sigma^{Z}_{j-1}\\a_{j}\rightarrow\frac{1}{2}\left(\prod_{i=j+1}^N \left(\sigma_i^X X\right)\right)\left( \sigma^{X}_{j}+i\sigma_j^Y\right) X \sigma^{Z}_{j-1}\end{aligned}\end{align} \]
Returns

QubitOperator, qubits operator after parity transformation.

reversed_jordan_wigner()[source]

Apply reversed Jordan-Wigner transform.

Returns

FermionOperator, fermion operator after reversed_jordan_wigner transformation.

ternary_tree()[source]

Apply Ternary tree transform. Implementation from https://arxiv.org/pdf/1910.10746.pdf.

Returns

QubitOperator, qubit operator after ternary_tree transformation.

Quantum unitary coupled cluster.

mindquantum.hiqfermion.ucc.get_qubit_hamiltonian(mol)[source]

Get the qubit hamiltonian of a molecular data.

Parameters

mol (MolecularData) – molecular data.

Returns

QubitOperator, qubit operator of this molecular.

mindquantum.hiqfermion.ucc.quccsd_generator(n_qubits=None, n_electrons=None, anti_hermitian=True, occ_orb=None, vir_orb=None, generalized=False)[source]

Generate qubit-UCCSD (qUCCSD) ansatz using qubit-excitation operators.

Note

Currently, unrestricted version is implemented, i.e., excitations from the same spatial-orbital but with different spins will use distinct variational parameters.

Parameters
  • n_qubits (int) – Number of qubits (spin-orbitals). Default: None.

  • n_electrons (int) – Number of electrons (occupied spin-orbitals). Default: None.

  • anti_hermitian (bool) – Whether to subtract the hermitian conjugate to form anti-Hermitian operators. Default: True.

  • occ_orb (list) – Indices of manually assigned occupied spatial orbitals. Default: None.

  • vir_orb (list) – Indices of manually assigned virtual spatial orbitals. Default: None.

  • generalized (bool) – Whether to use generalized excitations which do not distinguish occupied or virtual orbitals (qUCCGSD). Default: False.

Returns

Generator of the qUCCSD operators.

Return type

QubitExcitationOperator

Examples

>>> from mindquantum.hiqfermion.ucc import quccsd_generator
>>> quccsd_generator()
0
>>> quccsd_generator(4, 2)
-1.0*q_s_0 [Q0^ Q2] +
-1.0*q_s_2 [Q0^ Q3] +
-1.0*q_d_0 [Q1^ Q0^ Q3 Q2] +
-1.0*q_s_1 [Q1^ Q2] +
-1.0*q_s_3 [Q1^ Q3] +
1.0*q_s_0 [Q2^ Q0] +
1.0*q_s_1 [Q2^ Q1] +
1.0*q_s_2 [Q3^ Q0] +
1.0*q_s_3 [Q3^ Q1] +
1.0*q_d_0 [Q3^ Q2^ Q1 Q0]
>>> q_op = quccsd_generator(occ_orb=[0], vir_orb=[1], generalized=True)
>>> q_qubit_op = q_op.to_qubit_operator()
>>> print(str(q_qubit_op)[:315])
0.125*I*q_d_4 + 0.125*I*q_d_7 + 0.125*I*q_d_9 [X0 X1 X2 Y3] +
0.125*I*q_d_4 - 0.125*I*q_d_7 - 0.125*I*q_d_9 [X0 X1 Y2 X3] +
0.25*I*q_d_12 + 0.25*I*q_d_5 + 0.5*I*q_s_0 - 0.5*I*q_s_3 [X0 Y1] +
-0.125*I*q_d_4 + 0.125*I*q_d_7 - 0.125*I*q_d_9 [X0 Y1 X2 X3] +
0.125*I*q_d_4 + 0.125*I*q_d_7 - 0.125*I*q_d_9 [X0 Y1 Y2 Y3] +
mindquantum.hiqfermion.ucc.uccsd0_singlet_generator(n_qubits=None, n_electrons=None, anti_hermitian=True, occ_orb=None, vir_orb=None, generalized=False)[source]

Generate UCCSD operators using CCD0 ansatz for molecular systems.

Note

Manually assigned occ_orb or vir_orb are indices of spatial orbitals instead of spin-orbitals. They will override n_electrons and n_qubits. This is to some degree similar to the active space, therefore can reduce the number of variational parameters. However, it may not reduce the number of required qubits, since Fermion excitation operators are non-local, i.e., \(a_{7}^{\dagger} a_{0}\) involves not only the 0th and 7th qubit, but also the 1st, 2nd, … 6th qubit.

Parameters
  • n_qubits (int) – Number of qubits (spin-orbitals). Default: None.

  • n_electrons (int) – Number of electrons (occupied spin-orbitals). Default: None.

  • anti_hermitian (bool) – Whether to subtract the hermitian conjugate to form anti-Hermitian operators. Default: True.

  • occ_orb (list) – Indices of manually assigned occupied spatial orbitals. Default: None.

  • vir_orb (list) – Indices of manually assigned virtual spatial orbitals. Default: None.

  • generalized (bool) – Whether to use generalized excitations which do not distinguish occupied or virtual orbitals (UCCGSD). Default: False.

Returns

FermionOperator, Generator of the UCCSD operators that uses CCD0 ansatz.

Examples

>>> from mindquantum.hiqfermion.ucc.uccsd0 import uccsd0_singlet_generator
>>> uccsd0_singlet_generator(4, 2)
-1.0*d0_s_0 [0^ 2] +
2.0*d0_d_0 [1^ 0^ 3 2] +
-1.0*d0_s_0 [1^ 3] +
1.0*d0_s_0 [2^ 0] +
1.0*d0_s_0 [3^ 1] +
-2.0*d0_d_0 [3^ 2^ 1 0]
>>> uccsd0_singlet_generator(4, 2, generalized=True)
1.0*d0_s_0 - 1.0*d0_s_1 [0^ 2] +
1.0*d0_d_0 [1^ 0^ 2 1] +
-1.0*d0_d_0 [1^ 0^ 3 0] +
-2.0*d0_d_1 [1^ 0^ 3 2] +
1.0*d0_s_0 - 1.0*d0_s_1 [1^ 3] +
-1.0*d0_s_0 + 1.0*d0_s_1 [2^ 0] +
-1.0*d0_d_0 [2^ 1^ 1 0] +
1.0*d0_d_2 [2^ 1^ 3 2] +
1.0*d0_d_0 [3^ 0^ 1 0] +
-1.0*d0_d_2 [3^ 0^ 3 2] +
-1.0*d0_s_0 + 1.0*d0_s_1 [3^ 1] +
2.0*d0_d_1 [3^ 2^ 1 0] +
-1.0*d0_d_2 [3^ 2^ 2 1] +
1.0*d0_d_2 [3^ 2^ 3 0]
>>> uccsd0_singlet_generator(6, 2, occ_orb=[0], vir_orb=[1])
-1.0*d0_s_0 [0^ 2] +
2.0*d0_d_0 [1^ 0^ 3 2] +
-1.0*d0_s_0 [1^ 3] +
1.0*d0_s_0 [2^ 0] +
1.0*d0_s_0 [3^ 1] +
-2.0*d0_d_0 [3^ 2^ 1 0]
mindquantum.hiqfermion.ucc.uccsd_singlet_generator(n_qubits, n_electrons, anti_hermitian=True)[source]

Create a singlet UCCSD generator for a system with n_electrons

This function generates a FermionOperator for a UCCSD generator designed to act on a single reference state consisting of n_qubits spin orbitals and n_electrons electrons, that is a spin singlet operator, meaning it conserves spin.

Parameters
  • n_qubits (int) – Number of spin-orbitals used to represent the system, which also corresponds to number of qubits in a non-compact map.

  • n_electrons (int) – Number of electrons in the physical system.

  • anti_hermitian (bool) – Flag to generate only normal CCSD operator rather than unitary variant, primarily for testing

Returns

FermionOperator, Generator of the UCCSD operator that builds the UCCSD wavefunction.

Examples

>>> from mindquantum.hiqfermion.ucc import uccsd_singlet_generator
>>> uccsd_singlet_generator(4, 2)
-s_0 [0^ 2] +
-d1_0 [0^ 2 1^ 3] +
-s_0 [1^ 3] +
-d1_0 [1^ 3 0^ 2] +
s_0 [2^ 0] +
d1_0 [2^ 0 3^ 1] +
s_0 [3^ 1] +
d1_0 [3^ 1 2^ 0]
mindquantum.hiqfermion.ucc.uccsd_singlet_get_packed_amplitudes(single_amplitudes, double_amplitudes, n_qubits, n_electrons)[source]

Convert amplitudes for use with singlet UCCSD

The output list contains only those amplitudes that are relevant to singlet UCCSD, in an order suitable for use with the function uccsd_singlet_generator.

Parameters
  • single_amplitudes (numpy.ndarray) – \(N\times N\) array storing single excitation amplitudes corresponding to \(t_{i,j} * (a_i^\dagger a_j - \text{H.C.})\)

  • double_amplitudes (numpy.ndarray) – \(N\times N\times N\times N\) array storing double excitation amplitudes corresponding to \(t_{i,j,k,l} * (a_i^\dagger a_j a_k^\dagger a_l - \text{H.C.})\)

  • n_qubits (int) – Number of spin-orbitals used to represent the system, which also corresponds to number of qubits in a non-compact map.

  • n_electrons (int) – Number of electrons in the physical system.

Returns

ParameterResolver, List storing the unique single and double excitation amplitudes for a singlet UCCSD operator. The ordering lists unique single excitations before double excitations.

Examples

>>> import numpy as np
>>> from mindquantum.hiqfermion.ucc import uccsd_singlet_get_packed_amplitudes
>>> n_qubits, n_electrons = 4, 2
>>> np.random.seed(42)
>>> ccsd_single_amps = np.random.random((4, 4))
>>> ccsd_double_amps = np.random.random((4, 4, 4, 4))
>>> uccsd_singlet_get_packed_amplitudes(ccsd_single_amps, ccsd_double_amps,
...                                     n_qubits, n_electrons)
{'s_0': 0.6011150117432088, 'd1_0': 0.7616196153287176}