How to use quimb.tensor.TNOptimizer
directly¶
Quimb provides a mechanism for optimizing tensor networks through its TNOptimizer
interface. The Quimb backend provided by this addon uses this under the hood. This how-to guide demonstrates how to work with this object directly, in case some users want more direct access to it.
Set up a model Hamiltonian¶
[1]:
from qiskit.transpiler import CouplingMap
from qiskit_addon_utils.problem_generators import generate_xyz_hamiltonian
# Generate some coupling map to use for this example
coupling_map = CouplingMap.from_heavy_hex(3, bidirectional=False)
# Choose a 10-qubit circle on this coupling map
reduced_coupling_map = coupling_map.reduce([0, 13, 1, 14, 10, 16, 4, 15, 3, 9])
# Get a qubit operator describing the Ising field model
hamiltonian = generate_xyz_hamiltonian(
reduced_coupling_map,
coupling_constants=(0.0, 0.0, 1.0),
ext_magnetic_field=(0.4, 0.0, 0.0),
)
/tmp/ipykernel_3439/2599302630.py:1: DeprecationWarning: Using Qiskit with Python 3.9 is deprecated as of the 2.1.0 release. Support for running Qiskit with Python 3.9 will be removed in the 2.3.0 release, which coincides with when Python 3.9 goes end of life.
from qiskit.transpiler import CouplingMap
Set up quimb simulator with default options¶
[2]:
import quimb.tensor as qtn
from qiskit_addon_aqc_tensor.simulation.quimb import (
QuimbSimulator,
qiskit_ansatz_to_quimb,
recover_parameters_from_quimb,
)
simulator = QuimbSimulator(qtn.Circuit)
Generate target circuit¶
[3]:
from qiskit.synthesis import SuzukiTrotter
from qiskit_addon_utils.problem_generators import generate_time_evolution_circuit
from qiskit_addon_aqc_tensor.simulation import (
compute_overlap,
tensornetwork_from_circuit,
)
evolution_time = 0.4
target_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=8),
time=evolution_time,
)
target_tns = tensornetwork_from_circuit(target_circuit, simulator)
Generate ansatz from a shallower circuit¶
[4]:
from qiskit_addon_aqc_tensor.ansatz_generation import (
AnsatzBlock,
generate_ansatz_from_circuit,
)
initial_shallow_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=2),
time=evolution_time,
)
ansatz, initial_parameters = generate_ansatz_from_circuit(initial_shallow_circuit)
ansatz = ansatz.decompose(AnsatzBlock)
ansatz.draw("mpl", fold=-1)
[4]:

Initialize objective function¶
[5]:
from qiskit_addon_aqc_tensor.objective import MaximizeStateFidelity
objective = MaximizeStateFidelity(target_tns, None, None)
Convert Qiskit ansatz and initial parameters to a Quimb parametrized circuit¶
[6]:
circ, conversion_context = qiskit_ansatz_to_quimb(ansatz, initial_parameters)
Perform optimization of Quimb circuit using automatic differentiation¶
[7]:
from qiskit_addon_aqc_tensor.simulation.quimb import tnoptimizer_objective_kwargs
tnopt = qtn.TNOptimizer(
circ,
**tnoptimizer_objective_kwargs(objective),
autodiff_backend="jax", # OPTIONS: jax, autograd, torch, etc.
)
circ_opt = tnopt.optimize(20)
+0.000020861517 [best: +0.000020861517] : : 22it [00:31, 1.41s/it]
Recover final parameters from the quimb circuit¶
[8]:
final_parameters = recover_parameters_from_quimb(circ_opt, conversion_context)
Check fidelity of final, compressed state w/ respect to target state¶
[9]:
compressed_circuit = ansatz.assign_parameters(final_parameters)
compressed_state = tensornetwork_from_circuit(compressed_circuit, simulator)
abs(compute_overlap(target_tns, compressed_state)) ** 2
[9]:
0.9999992039939346
Compare with fidelity of initial shallow state w/ respect to target state¶
[10]:
initial_shallow_state = tensornetwork_from_circuit(initial_shallow_circuit, simulator)
abs(compute_overlap(target_tns, initial_shallow_state)) ** 2
[10]:
0.9998389457702321