Fermion Gaussian State (FGS) Simulator¶
This tutorial demonstrates how to use the Fermion Gaussian State (FGS) simulator implemented in tensorcircuit-ng. The FGS simulator allows for efficient simulation of non-interacting fermionic systems, which is particularly useful for studying free fermions on lattices.
Introduction¶
The FGS simulator efficiently handles systems governed by quadratic Hamiltonians of the form:
Instead of working with the full \(2^N\)-dimensional Hilbert space, the FGS simulator uses the correlation matrix formalism which scales polynomially with system size.
Setup¶
[1]:
import numpy as np
import tensorcircuit as tc
# Set the backend (using numpy for this tutorial)
tc.set_backend("numpy")
tc.set_dtype("complex128")
[1]:
('complex128', 'float64')
Creating an FGS Simulator Instance¶
We can initialize an FGS simulator in several ways:
By specifying occupied sites in a product state
From the groudn state of a given Hamiltonian
Directly with the alpha matrix
[2]:
# Method 1: Initialize with occupied sites
# Create a 4-site system with sites 0 and 2 occupied
sim1 = tc.FGSSimulator(L=4, filled=[0, 2])
print("Initialized FGS with filled sites [0, 2]")
# Method 2: Initialize from a Hamiltonian (ground state)
# Create a simple hopping Hamiltonian
L = 4
hc = np.zeros([2 * L, 2 * L])
# Add hopping terms between neighboring sites
for i in range(L - 1):
# chi * (c_i^\dagger c_j + h.c.)
hc[i, i + 1] = 1.0
hc[i + L + 1, i + L] = -1.0
sim2 = tc.FGSSimulator(L=4, hc=hc)
print("Initialized FGS from Hamiltonian ground states")
# Check the alpha matrix of the first simulator
alpha = sim1.get_alpha()
print(f"Alpha matrix shape: {alpha.shape}")
Initialized FGS with filled sites [0, 2]
Initialized FGS from Hamiltonian ground states
Alpha matrix shape: (8, 4)
Single-particle Green’s Functions¶
We can compute correlation functions, which are related to single-particle Green’s functions:
[3]:
# Get the correlation matrix
cmatrix = sim1.get_cmatrix()
print(f"Correlation matrix shape: {cmatrix.shape}")
# Check occupation numbers (diagonal elements)
print("Occupation numbers:")
for i in range(sim1.L):
print(f"Site {i}: {1-cmatrix[i, i].real:.3f}")
# Compute off-diagonal correlations
print("\nSelected off-diagonal correlations:")
print(f"<c_0 c_0^†> = {sim1.expectation_2body(0, 0):.3f}")
print(f"<c_0 c_1^†> = {sim1.expectation_2body(0, 1):.3f}")
print(f"<c_2 c_3^†> = {sim1.expectation_2body(2, 3):.3f}")
Correlation matrix shape: (8, 8)
Occupation numbers:
Site 0: 1.000
Site 1: 0.000
Site 2: 1.000
Site 3: 0.000
Selected off-diagonal correlations:
<c_0 c_0^†> = 0.000+0.000j
<c_0 c_1^†> = 0.000+0.000j
<c_2 c_3^†> = 0.000+0.000j
Time Evolution¶
The FGS simulator supports evolution under quadratic Hamiltonians:
Hopping terms: \(\chi c_i^\dagger c_j + h.c.\)
Chemical potential terms: \(\chi c_i^\dagger c_i\)
Superconducting pairing terms: \(\chi c_i^\dagger c_j^\dagger + h.c.\)
Let’s demonstrate hopping:
[4]:
# Create a new simulator
sim = tc.FGSSimulator(L=4, filled=[0])
print("Initial state:")
cmatrix_init = sim.get_cmatrix()
for i in range(sim.L):
print(f"Site {i} occupation: {1-cmatrix_init[i, i].real:.3f}")
# Apply hopping between sites 0 and 1 with strength 1.0 for time π/2
# This should transfer the fermion from site 0 to site 1
sim.evol_hp(0, 1, np.pi)
print("\nAfter hopping evolution:")
cmatrix_final = sim.get_cmatrix()
for i in range(sim.L):
print(f"Site {i} occupation: {1-cmatrix_final[i, i].real:.3f}")
Initial state:
Site 0 occupation: 1.000
Site 1 occupation: 0.000
Site 2 occupation: 0.000
Site 3 occupation: 0.000
After hopping evolution:
Site 0 occupation: 0.000
Site 1 occupation: 1.000
Site 2 occupation: 0.000
Site 3 occupation: 0.000
Entanglement Measures¶
The FGS simulator can efficiently compute entanglement measures like von Neumann entropy and Renyi entropy:
[5]:
# Create a simple entangled state
sim_ent = tc.FGSSimulator(L=4, filled=[0, 2])
# Apply a hopping that creates entanglement
sim_ent.evol_hp(0, 1, np.pi / 4)
# Compute entanglement entropy for different subsystems
# Tracing out sites [2, 3] means we look at the entanglement of sites [0, 1]
entropy_01 = sim_ent.entropy([2, 3])
print(f"Entanglement entropy of sites [0,1]: {entropy_01.real:.6f}")
# Tracing out sites [1, 2, 3] means we look at the entanglement of site [0]
entropy_0 = sim_ent.entropy([1, 2, 3])
print(f"Entanglement entropy of site [0]: {entropy_0.real:.6f}")
# Compute Renyi entropy (n=2) for the same subsystems
renyi_01 = sim_ent.renyi_entropy(2, [2, 3])
print(f"Renyi-2 entropy of sites [0,1]: {renyi_01.real:.6f}")
renyi_0 = sim_ent.renyi_entropy(2, [1, 2, 3])
print(f"Renyi-2 entropy of site [0]: {renyi_0.real:.6f}")
Entanglement entropy of sites [0,1]: -0.000000
Entanglement entropy of site [0]: 0.416496
Renyi-2 entropy of sites [0,1]: -0.000000
Renyi-2 entropy of site [0]: 0.287682
Measurements and Post-selection¶
The FGS simulator supports both projective measurements and post-selection:
[10]:
# Create a superposition state
sim_meas = tc.FGSSimulator(L=2, filled=[0])
# Put site 0 in an equal superposition of occupied and unoccupied
# This is a simplified example - in practice, creating such states requires specific evolutions
sim_meas.evol_hp(0, 1, np.pi / 4)
print("Before measurement:")
cmatrix = sim_meas.get_cmatrix()
for i in range(sim_meas.L):
print(f"Site {i} occupation probability: {1-cmatrix[i, i].real:.3f}")
# Simulate a measurement on site 0 with a random outcome
# In practice, you would use a random number generator
# Here we manually specify the outcome for reproducibility
outcome = sim_meas.cond_measure(0, status=0.1) # Should likely result in 0 (unoccupied)
print(f"\nMeasurement outcome for site 0: {outcome}")
print("After measurement:")
cmatrix_post = sim_meas.get_cmatrix()
for i in range(sim_meas.L):
print(f"Site {i} occupation probability: {1-cmatrix_post[i, i].real:.3f}")
# Demonstrate post-selection (conditioning on a specific outcome)
sim_post = tc.FGSSimulator(L=2, filled=[0])
sim_post.evol_hp(0, 1, np.pi / 4)
print("\nBefore post-selection:")
print(f"Site 0 occupation: {1-sim_post.get_cmatrix()[0, 0].real:.3f}")
# Post-select on site 0 being occupied (keep=1)
sim_post.post_select(0, keep=1)
print("After post-selecting site 0 as occupied:")
print(f"Site 0 occupation: {1-sim_post.get_cmatrix()[0, 0].real:.3f}")
Before measurement:
Site 0 occupation probability: 0.854
Site 1 occupation probability: 0.146
Measurement outcome for site 0: 0.0
After measurement:
Site 0 occupation probability: 0.000
Site 1 occupation probability: 1.000
Before post-selection:
Site 0 occupation: 0.854
After post-selecting site 0 as occupied:
Site 0 occupation: 1.000
Advanced Example: Kitaev Chain¶
Let’s simulate a simple Kitaev chain, which includes both hopping and pairing terms:
[11]:
def kitaev_chain(L, mu, Delta, J):
"""
Create the Hamiltonian matrix for a Kitaev chain
H = -J Σ (c_i^†c_{i+1} + h.c.) - μ Σ c_i^†c_i + Δ Σ (c_i c_{i+1} + h.c.)
"""
hc = np.zeros([2 * L, 2 * L], dtype=complex)
# Chemical potential term
for i in range(L):
hc[i, i] = -mu
hc[i + L, i + L] = mu
# Hopping terms
for i in range(L - 1):
hc[i, i + 1] = -J
hc[i + L + 1, i + L] = J
# Pairing terms
for i in range(L - 1):
hc[i, i + 1 + L] = Delta
hc[i + 1, i + L] = -Delta
hc[i + L + 1, i] = Delta
hc[i + L, i + 1] = -Delta
return hc
# Parameters for the Kitaev chain
L = 6
mu = 1.0 # Chemical potential
Delta = 0.5 # Pairing amplitude
J = 1.0 # Hopping amplitude
# Create the Hamiltonian
kitaev_hc = kitaev_chain(L, mu, Delta, J)
# Initialize the ground state of the Kitaev chain
sim_kitaev = tc.FGSSimulator(L=L, hc=kitaev_hc)
print("Kitaev chain ground state:")
cmatrix = sim_kitaev.get_cmatrix()
print("Site occupation numbers:")
for i in range(L):
print(f" Site {i}: {1-cmatrix[i, i].real:.4f}")
# Calculate entanglement entropy for half the chain
half_chain_entropy = sim_kitaev.entropy(list(range(L // 2, L)))
print(f"\nEntanglement entropy of half the chain: {half_chain_entropy.real:.6f}")
Kitaev chain ground state:
Site occupation numbers:
Site 0: 0.8166
Site 1: 0.8837
Site 2: 0.8746
Site 3: 0.8746
Site 4: 0.8837
Site 5: 0.8166
Entanglement entropy of half the chain: 0.349326
Conclusion¶
This tutorial demonstrated the main features of the FGS simulator:
Initialization methods
Computation of correlation functions
Time evolution under quadratic Hamiltonians
Entanglement measures
Measurements and post-selection
Application to a physical model (Kitaev chain)
The FGS simulator provides an efficient way to study non-interacting fermionic systems, avoiding the exponential cost of full Hilbert space simulation while still capturing important physical properties like entanglement and correlation functions.