-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[dev] allow MPS states for testing purposes #56
Merged
Merged
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
A full comparison of all backends with MPS or MPO internal states against exact statevector simulations can be done using the following script: Comparison Scriptimport numpy as np
# Qiskit imports
from qiskit.circuit import QuantumCircuit
from qiskit.circuit.library import XXPlusYYGate
from qiskit.quantum_info import Statevector
# MPF quimb-based imports
from qiskit_addon_mpf.backends.quimb_layers import (
LayerModel as QuimbLayerModel,
)
from qiskit_addon_mpf.backends.quimb_layers import (
LayerwiseEvolver as QuimbLayerwiseEvolver,
)
from qiskit_addon_mpf.backends.quimb_tebd import MPOState as QuimbMPOState
# MPF tenpy-based imports
from qiskit_addon_mpf.backends.tenpy_layers import (
LayerModel as TenpyLayerModel,
)
from qiskit_addon_mpf.backends.tenpy_layers import (
LayerwiseEvolver as TenpyLayerwiseEvolver,
)
from qiskit_addon_mpf.backends.tenpy_tebd import (
MPOState as TenpyMPOState,
)
from qiskit_addon_mpf.backends.tenpy_tebd import (
MPS_neel_state as tenpy_MPS_neel_state,
)
# qiskit-quimb imports
from qiskit_quimb import quimb_circuit
# quimb imports
from quimb.tensor import TEBD as QuimbTEBDEngine
from quimb.tensor import CircuitMPS, MPO_identity, SpinHam1D, TensorNetwork
from quimb.tensor import MPS_neel_state as quimb_MPS_neel_state
# tenpy imports
from tenpy.algorithms import TEBDEngine as TenpyTEBDEngine
from tenpy.models import XXZChain2
from tenpy.networks.site import SpinHalfSite
############################
##### Circuit Builders #####
############################
def gen_ext_field_layer(n, hz):
qc = QuantumCircuit(n)
for q in range(n):
qc.rz(-hz[q], q)
return qc
def trotter_step(qc, q0, q1, Jxx, Jz):
qc.rzz(Jz, q0, q1)
qc.append(XXPlusYYGate(2.0 * Jxx), [q0, q1])
def gen_odd_coupling_layer(n, Jxx, Jz, J):
qc = QuantumCircuit(n)
for q in range(0, n, 2):
trotter_step(qc, q, q + 1, J[q] * Jxx, J[q] * Jz)
return qc
def gen_even_coupling_layer(n, Jxx, Jz, J):
qc = QuantumCircuit(n)
for q in range(1, n - 1, 2):
q0 = q
q1 = (q + 1) % n
if q1 < q0:
qc.barrier()
trotter_step(qc, q0, q1, J[q0] * Jxx, J[q0] * Jz)
return qc
############################
##### Model Parameters #####
############################
np.random.seed(0)
# constants
L = 4
W = 0.5
epsilon = 0.5
J = np.random.rand(L - 1) + W * np.ones(L - 1)
# ZZ couplings
Jz = 1.0
# XX and YY couplings
Jxx = epsilon
# base coupling
# external field
hz = 0.000000001 * np.array([(-1) ** i for i in range(L)])
N = 10
dt = 0.05
odd_coupling_layer = gen_odd_coupling_layer(L, Jxx, Jz, J)
even_coupling_layer = gen_even_coupling_layer(L, Jxx, Jz, J)
ext_field_layer = gen_ext_field_layer(L, hz)
############################
##### Tenpy Parameters #####
############################
tenpy_model_opts = {
"bc_MPS": "finite",
"conserve": "Sz",
"sort_charge": False,
}
class ConserveXXZChain2(XXZChain2):
"""TeNPy's XXZChain2 hard-codes Sz conservation. This subclass makes it configurable."""
def init_sites(self, model_params):
conserve = model_params.get("conserve", "Sz", str)
sort_charge = model_params.get("sort_charge", True, bool)
return SpinHalfSite(conserve=conserve, sort_charge=sort_charge) # use predefined Site
# This is the full model that we want to simulate. It is used for the "exact" time evolution
# (which is approximated via a fourth-order Suzuki-Trotter formula).
tenpy_exact_model = ConserveXXZChain2(
{
"L": L,
"Jz": 4.0 * Jz * J,
"Jxx": 4.0 * Jxx * J,
"hz": 2.0 * hz,
**tenpy_model_opts,
}
)
tenpy_layers = [
TenpyLayerModel.from_quantum_circuit(odd_coupling_layer, **tenpy_model_opts),
TenpyLayerModel.from_quantum_circuit(even_coupling_layer, **tenpy_model_opts),
TenpyLayerModel.from_quantum_circuit(ext_field_layer, keep_only_odd=True, **tenpy_model_opts),
TenpyLayerModel.from_quantum_circuit(ext_field_layer, keep_only_odd=False, **tenpy_model_opts),
TenpyLayerModel.from_quantum_circuit(ext_field_layer, keep_only_odd=False, **tenpy_model_opts),
TenpyLayerModel.from_quantum_circuit(ext_field_layer, keep_only_odd=True, **tenpy_model_opts),
TenpyLayerModel.from_quantum_circuit(even_coupling_layer, **tenpy_model_opts),
TenpyLayerModel.from_quantum_circuit(odd_coupling_layer, **tenpy_model_opts),
]
############################
##### Quimb Parameters #####
############################
# Initialize the builder for a spin 1/2 chain
builder = SpinHam1D(S=1 / 2)
# Add XX and YY couplings for neighboring sites
for i in range(L - 1):
builder[i, i + 1] += 2.0 * Jxx * J[i], "-", "+"
builder[i, i + 1] += 2.0 * Jxx * J[i], "+", "-"
# Add ZZ couplings for neighboring sites
for i in range(L - 1):
builder[i, i + 1] += 4.0 * Jz * J[i], "Z", "Z"
# Add the external Z-field (hz) to each site
for i in range(L):
builder[i] += -2.0 * hz[i], "Z"
# Build the local Hamiltonian
quimb_exact_model = builder.build_local_ham(L)
quimb_layers = [
QuimbLayerModel.from_quantum_circuit(odd_coupling_layer, cyclic=False),
QuimbLayerModel.from_quantum_circuit(even_coupling_layer, cyclic=False),
QuimbLayerModel.from_quantum_circuit(ext_field_layer, keep_only_odd=True, cyclic=False),
QuimbLayerModel.from_quantum_circuit(ext_field_layer, keep_only_odd=False, cyclic=False),
QuimbLayerModel.from_quantum_circuit(ext_field_layer, keep_only_odd=False, cyclic=False),
QuimbLayerModel.from_quantum_circuit(ext_field_layer, keep_only_odd=True, cyclic=False),
QuimbLayerModel.from_quantum_circuit(even_coupling_layer, cyclic=False),
QuimbLayerModel.from_quantum_circuit(odd_coupling_layer, cyclic=False),
]
############################
##### Reference Values #####
############################
### Qiskit ###
odd_coupling_layer = gen_odd_coupling_layer(L, dt * Jxx, dt * Jz, J)
even_coupling_layer = gen_even_coupling_layer(L, dt * Jxx, dt * Jz, J)
onsite_layer = gen_ext_field_layer(L, dt * hz)
layers = [
odd_coupling_layer,
even_coupling_layer,
onsite_layer,
onsite_layer,
even_coupling_layer,
odd_coupling_layer,
]
trotter_circ = QuantumCircuit(L)
for layer in layers:
trotter_circ = trotter_circ.compose(layer)
trotter_circ = trotter_circ.repeat(N)
init_circ = QuantumCircuit(L)
init_circ.x(1)
init_circ.x(3)
full_circ = init_circ.copy()
full_circ = full_circ.compose(trotter_circ)
init_state_vec = Statevector(init_circ)
full_state_vec = Statevector(full_circ)
print("Qiskit Statevector", full_state_vec.inner(init_state_vec))
quimb_init_circ = quimb_circuit(init_circ)
quimb_full_circ = quimb_circuit(full_circ.decompose())
print("Quimb Circuit ", TensorNetwork((quimb_full_circ.psi.H, quimb_init_circ.psi)).contract())
quimb_circ_mps = CircuitMPS(L)
quimb_circ_mps.apply_gates(quimb_full_circ.gates)
print("Quimb Circuit MPS ", TensorNetwork((quimb_circ_mps.psi.H, quimb_init_circ.psi)).contract())
### Tenpy ###
tenpy_trunc_options = {
"trunc_params": {
"chi_max": 100,
"svd_min": 1e-15,
"trunc_cut": None,
},
"preserve_norm": False,
"order": 2,
}
tenpy_initial_state = tenpy_MPS_neel_state(tenpy_exact_model.lat)
tenpy_ref_state = tenpy_initial_state.copy()
tenpy_ref_evo = TenpyTEBDEngine(tenpy_ref_state, tenpy_exact_model, tenpy_trunc_options)
for _ in range(N):
tenpy_ref_evo.run_evolution(1, dt)
print("Builtin TeNPy MPS ", tenpy_ref_state.overlap(tenpy_initial_state))
### Quimb ###
quimb_trunc_options = {
"max_bond": 100,
"cutoff": 1e-15,
"cutoff_mode": "rel",
"method": "svd",
"renorm": False,
}
quimb_initial_state = quimb_MPS_neel_state(L)
quimb_ref_state = quimb_initial_state.copy()
quimb_ref_evo = QuimbTEBDEngine(
quimb_ref_state, quimb_exact_model, dt=dt, split_opts=quimb_trunc_options
)
for _ in range(N):
quimb_ref_evo.step(order=2)
# NOTE: quimb's Tensor.overlap method computes <other|self> while tenpy does <self|other>. Thus, we
# need to conjugate the result below, where we have kept the LHS and RHS objects identical to tenpy.
print("Builtin Quimb MPS ", quimb_ref_evo.pt.overlap(quimb_initial_state).conjugate())
############################
##### Tenpy Results #####
############################
### MPS ###
tenpy_mps_state = tenpy_initial_state.copy()
tenpy_mps_evo = TenpyLayerwiseEvolver(
evolution_state=tenpy_mps_state, layers=tenpy_layers, options=tenpy_trunc_options
)
for _ in range(N):
tenpy_mps_evo.run_evolution(1, dt)
print("Custom TeNPy MPS ", tenpy_mps_state.overlap(tenpy_initial_state))
### MPO ###
tenpy_mpo_state = TenpyMPOState.initialize_from_lattice(tenpy_exact_model.lat)
tenpy_mpo_evo = TenpyLayerwiseEvolver(
evolution_state=tenpy_mpo_state, layers=tenpy_layers, options=tenpy_trunc_options
)
tenpy_mpo_evo.conjugate = True
for _ in range(N):
tenpy_mpo_evo.run_evolution(1, dt)
print("Custom TeNPy MPO ", tenpy_mpo_state.overlap(tenpy_initial_state))
############################
##### Quimb Results #####
############################
### MPS ###
quimb_mps_state = quimb_initial_state.copy()
quimb_mps_evo = QuimbLayerwiseEvolver(
evolution_state=quimb_mps_state, layers=quimb_layers, dt=dt, split_opts=quimb_trunc_options
)
for _ in range(N):
quimb_mps_evo.step()
# NOTE: quimb's Tensor.overlap method computes <other|self> while tenpy does <self|other>. Thus, we
# need to conjugate the result below, where we have kept the LHS and RHS objects identical to tenpy.
print("Custom Quimb MPS ", quimb_mps_evo.pt.overlap(quimb_initial_state).conjugate())
### MPO ###
quimb_mpo_state = QuimbMPOState(MPO_identity(L))
quimb_mpo_evo = QuimbLayerwiseEvolver(
evolution_state=quimb_mpo_state, layers=quimb_layers, dt=dt, split_opts=quimb_trunc_options
)
quimb_mpo_evo.conjugate = True
for _ in range(N):
quimb_mpo_evo.step()
print("Custom Quimb MPO ", quimb_mpo_evo.pt.overlap(quimb_initial_state)) |
Pull Request Test Coverage Report for Build 13073186785Details
💛 - Coveralls |
This commit adds support for MPS states within the custom time evolver implementations. This is useful for testing purposes when comparing the algorithms against the backend specific default implementations (which only allow MPS) or (e.g.) exact statevector simulations of `QuantumCircuit` objects. This feature is not meant for end-user consumption and, thus, not advertised as such.
mrossinek
force-pushed
the
mps-evolution
branch
from
January 31, 2025 12:55
ec220d7
to
4029b00
Compare
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This commit adds support for MPS states within the custom time evolver implementations. This is useful for testing purposes when comparing the algorithms against the backend specific default implementations (which only allow MPS) or (e.g.) exact statevector simulations of
QuantumCircuit
objects.This feature is not meant for end-user consumption and, thus, not advertised as such.