Circuit Factory: Time evolution
In this tutorial, you will learn how to use QURI Algo to obtain time evolution circuits and their controlled conterpart. This includes:
- Understanding the
HamiltonianInput
object and how to create one - Understanding the
(Controlled)TimeEvolutionCircuitFactory
object - Evaluate the (controlled) time evolution circuit with QURI VM
Hamiltonian
In QURI Algo, we provide the HamiltionianInput
interface representing any problen that involves a Hamiltonian. Here, we first set up a molecular Hamiltonian as the example of this tutorial.
import numpy as np
from pyscf import gto, scf
from quri_parts.pyscf.mol import get_spin_mo_integrals_from_mole
from quri_parts.openfermion.mol import get_qubit_mapped_hamiltonian
from quri_parts.core.operator import Operator, PAULI_IDENTITY
mole = gto.M(atom="H 0 0 0; H 0 0 1")
mf = scf.RHF(mole).run()
hamiltonian, mapping = get_qubit_mapped_hamiltonian(
*get_spin_mo_integrals_from_mole(mole, mf.mo_coeff)
)
eff_hamiltonian = hamiltonian - Operator({PAULI_IDENTITY: hamiltonian.constant})
converged SCF energy = -1.06610864931794
The Hamiltonian generated above is a qubit Hamiltonian represented by a QURI Parts Operator
. We create a QubitHamiltonianInput
object, which is a variant of HamiltonianInput
that represents a Hamiltonian in its qubit mapped form.
from quri_algo.problem import QubitHamiltonianInput
hamiltonian_input = QubitHamiltonianInput(mapping.n_qubits, eff_hamiltonian)
Time evolution circuit
Having constructed the Hamiltonian, we can construct a time evolution circuit that performs the time evolution with the Hamiltonian. In QURI Algo, we provide TimeEvolutionCircuitFactory
, which allows you to create a time evolution circuit at time step . We provide a variant of the TimeEvolutionCircuitFactory
, theTrotterTimeEvolutionCircuitFactory
, that generates a time evolution circuit based on Trotterization. To create one, you need to pass in the hamiltonian_input
we created above as well as the Trotter steps.
from quri_algo.circuit.time_evolution.trotter_time_evo import TrotterTimeEvolutionCircuitFactory
trotter_10_circuit_factory = TrotterTimeEvolutionCircuitFactory(hamiltonian_input, n_trotter=10)
With the factory, you can generate a time evolution circuit by passing an evolution time to it.
# Evolution circuit at t = 5.0
circuit = trotter_10_circuit_factory(5.0)
print(circuit)
<quri_parts.rust.circuit.circuit_parametric.ImmutableBoundParametricQuantumCircuit object at 0x7f34be26fd80>
Controlled time evolution circuits
In phase-estimation-like algorithms, controlled time evolution circuits are used ubiquitously. To facilitate them, we also provide ControlledTimeEvolutionCircuitFactory
for generating controlled time evolution circuits at time step . We provide 2 variants of the ControlledTimeEvolutionCircuitFactory
:
ControlledTrotterTimeEvolutionCircuitFactory
: generates a time evolution circuit based on TrotterizationControlledExactUnitaryTimeEvolutionCircuitFactory
: generates a time evolution circuit based on the exact unitary matrix generated by theQubitHamiltonianInput
.
from quri_algo.circuit.time_evolution.trotter_time_evo import TrotterControlledTimeEvolutionCircuitFactory
from quri_algo.circuit.time_evolution.exact_unitary import ExactUnitaryControlledTimeEvolutionCircuitFactory
trotter_controlled_factory = TrotterControlledTimeEvolutionCircuitFactory(hamiltonian_input, n_trotter=10)
exact_controlled_factory = ExactUnitaryControlledTimeEvolutionCircuitFactory(hamiltonian_input)
They can be used in the same way as TimeEvolutionCircuitFactory
s.
evolution_time = 10
trotter_controlled_circuit = trotter_controlled_factory(evolution_time)
exact_controlled_circuit = exact_controlled_factory(evolution_time)
Evolution circuit on error corrected devices
On an error corrected device, we need to transpile the circuit according to the native gates of the device or architecture.
from quri_parts.backend.devices import nisq_spcond_lattice, star_device
from quri_parts.circuit.topology import SquareLattice
from quri_parts.backend.units import TimeValue, TimeUnit
from quri_vm import VM
nisq_vm = VM.from_device_prop(
nisq_spcond_lattice.generate_device_property(
lattice=SquareLattice(4, 4),
native_gates=("RZ", "SqrtX", "X", "CNOT"),
gate_error_1q=1e-3,
gate_error_2q=1e-2,
gate_error_meas=1e-2,
gate_time_1q=TimeValue(60, TimeUnit.NANOSECOND),
gate_time_2q=TimeValue(660, TimeUnit.NANOSECOND),
gate_time_meas=TimeValue(1.4, TimeUnit.MICROSECOND),
)
)
star_vm = VM.from_device_prop(
star_device.generate_device_property(
qubit_count=16,
code_distance=9,
qec_cycle=TimeValue(1, TimeUnit.MICROSECOND),
physical_error_rate=1e-4
)
)
Each VM has its own transpiler, which ensures that any time a logical circuit is simulated, it first is transpiled to a gate-set that is supported by the device in question. Below we first define the logical circuit factory that we will use for time-evolution.
trotter_factory = TrotterTimeEvolutionCircuitFactory(
hamiltonian_input, n_trotter=10
)
from quri_parts.circuit import NonParametricQuantumCircuit
def get_gate_names(circuit: NonParametricQuantumCircuit) -> set[str]:
return set([g.name for g in circuit.gates])
print("Gates that compose the time evolution circuit on superconducting NISQ device:")
print(get_gate_names(nisq_vm.transpile(trotter_factory(evolution_time))))
print("Gates that compose the time evolution circuit on STAR device:")
print(get_gate_names(star_vm.transpile(trotter_factory(evolution_time))))
Gates that compose the time evolution circuit on superconducting NISQ device:
{'X', 'CNOT', 'RZ', 'SqrtX'}
Gates that compose the time evolution circuit on STAR device:
{'H', 'CNOT', 'RZ', 'S'}
We can also perform an analysis of the circuit based on each architecture:
import pprint
print("Evaluation result on NISQ device:")
pprint.pprint(nisq_vm.analyze(trotter_factory(evolution_time)))
print("\n")
print("Evaluation result on STAR device:")
pprint.pprint(star_vm.analyze(trotter_factory(evolution_time)))
Evaluation result on NISQ device:
AnalyzeResult(lowering_level=<LoweringLevel.ArchLogicalCircuit: 1>,
qubit_count=4,
gate_count=1860,
depth=853,
latency=TimeValue(value=239160.0, unit=<TimeUnit.NANOSECOND>),
fidelity=0.005982771196137667)
Evaluation result on STAR device:
AnalyzeResult(lowering_level=<LoweringLevel.ArchLogicalCircuit: 1>,
qubit_count=4,
gate_count=1460,
depth=770,
latency=TimeValue(value=16380000.0, unit=<TimeUnit.NANOSECOND>),
fidelity=0.9958925186936065)
We can see that the fidelity of the time evolution citcuit improves from to 99.5% at the cost of about 68 times execution time on the error corrected STAR architecture.