Advanced Topics¶
Engines¶
TenCirChem offers a set of different engines for the simulation of static molecular properties and is summarized below.
Engine |
Compatible class |
State representation |
UCC factor expansion |
Note |
---|---|---|---|---|
|
|
CI vector |
Yes |
Most efficient |
|
|
CI vector |
Yes |
Efficient and memory-friendly |
|
|
statevector |
No |
Noiseless tensor network contraction |
|
|
density matrix |
No |
with gate noise |
|
|
statevector |
No |
with measurement noise |
|
|
density matrix |
No |
with gate and measurement noise |
|
|
statevector |
Yes |
Experimental |
|
|
CI vector |
Yes |
Experimental |
“CI vector” means Configuration Interaction vector. It is a more efficient representation than statevector for chemical applications because CI vector exploits particle number conservation symmetry. This is one of the major reasons why TenCirChem is so fast.
“UCC factor expansion” means expanding the excitation operator exponential into a polynomial form
where \(G\) is the excitation operator
The expansion allows fast simulation of \(e^{\theta G}\), without decomposing \(e^{\theta G}\) into quantum gates. It is not difficult to derive the equation noticing that
In the special case of \(G^2=-I\), the famous formula \(e^{\theta G}=\cos\theta + \sin\theta G\) is recovered. The idea was proposed by the Fermionic Quantum Emulator based on lots of preceding works. UCC factor expansion is another major reason why TenCirChem is so fast.
For UCC tasks, "civector"
engine is most efficient,
however, it is also memory consuming due to cached intermediates.
"civector-large"
is similar to "civector"
except that caching is only used in a limited scope.
In general, it is recommended to use the "civector"
engine until the host/GPU memory runs out,
and then switch to the "civector-large"
engine.
In the UCC
class "civector"
is the default engine for calculations with <= 16 qubits
and "civector-large"
is used for larger scale calculation.
In the HEA
class the noiseless "tensornetwork"
engine is used by default.
To override the default engine, pass the string to the engine
argument of the UCC
class:
from tencirchem import UCCSD
from tencirchem.molecule import h4
uccsd = UCCSD(h4, engine="civector-large")
print(uccsd.kernel())
print(uccsd.energy(engine="tensornetwork"))
Backends¶
TenCirChem supports NumPy, CuPy and JAX backend, in combination with a set of different Engines. Note that the supported backends differ slightly between TenCirChem and TensorCircuit.
To simplify installation, the default backend is NumPy.
When calculation scales up and GPU is available, CuPy becomes an efficient replacement of NumPy.
JAX offers auto-differentiation (AD) and just-in-time compilation (JIT) over NumPy and CuPy. It is the only available backend for the dynamics module. For static algorithms, JAX is sometimes faster than NumPy and CuPy due to JIT compilation.
For UCC tasks, TenCirChem typically does not rely on the AD for gradients and JIT time is usually much longer than the actual run time. Sometimes JIT will also result in out-of-memory error. Thus, in general NumPy and CuPy are the recommended backends over JAX.
For noisy circuit simulation, the performance of NumPy and JAX is comparable. For quantum dynamics simulation, the JAX backend is usually preferred.
TenCirChem shares the same API with TensorCircuit for setting backends and data types, at runtime:
from tencirchem import set_backend, set_dtype
set_backend("numpy")
set_dtype("complex64")
TenCirChem by default uses complex128
data type, which is different from the TensorCircuit convention.
complex128
is highly recommended with NumPy backend or float64
-friendly GPU.
Boson Encoding¶
An important step in quantum dynamics simulation is to map bosons into qubits. TenCirChem supports 4 different ways to encode boson DOFs into qubits.
No encoding. A boson DOF is directly truncated to a two-level system.
Unary encoding. Use one qubit to represent one boson level.
Binary encoding. Uses bitstring to represent one boson level.
Gray code encoding. An improved version of binary encoding.
A detailed description of the unary encoding and gray encoding, with other variants, can be found in this article .
In the following table we make a quick comparison of the encodings for a 4-level boson.
Boson level |
No encoding |
Unary encoding |
Binary encoding |
Gray encoding |
---|---|---|---|---|
0 |
0 |
0001 |
00 |
00 |
1 |
1 |
0010 |
01 |
01 |
2 |
Truncated |
0100 |
10 |
11 |
3 |
Truncated |
1000 |
11 |
10 |
Unary encoding costs \(\mathcal{O}(N)\) qubits where \(N\) is boson level, yet the circuit is shallower. Gray encoding costs \(\mathcal{O}(\textrm{log}N)\) qubits, yet the circuit depth is deeper. Gray encoding is strictly better than binary encoding.
The TimeEvolution
class by default uses Gray code encoding.
Other encodings can be specified through the boson_encoding
argument of the TimeEvolution
class.