メインコンテンツまでスキップ

Circuit factories: 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})

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 tt. 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)
# output:
<quri_parts.circuit.circuit_parametric.ImmutableBoundParametricQuantumCircuit object at 0x11a295f50>

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 tt. We provide 2 variants of the ControlledTimeEvolutionCircuitFactory:

  • ControlledTrotterTimeEvolutionCircuitFactory: generates a time evolution circuit based on Trotterization
  • ControlledExactUnitaryTimeEvolutionCircuitFactory: generates a time evolution circuit based on the exact unitary matrix generated by the QubitHamiltonianInput.
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 TimeEvolutionCircuitFactorys.

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
)
)

We can create time evolution circuit factories that automatically generate device-specific transpiled time evolution circuit. To do so, pass in the transpiler as a keyword argument of the circuit factories.

nisq_trotter_factory = TrotterTimeEvolutionCircuitFactory(
hamiltonian_input, n_trotter=10, transpiler=nisq_vm.transpile
)

star_trotter_factory = TrotterTimeEvolutionCircuitFactory(
hamiltonian_input, n_trotter=10, transpiler=star_vm.transpile
)
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_trotter_factory(evolution_time)))
print("Gates that compose the time evolution circuit on STAR device:")
print(get_gate_names(star_trotter_factory(evolution_time)))
# output:
Gates that compose the time evolution circuit on superconducting NISQ device:
{'CNOT', 'X', 'SqrtX', 'RZ'}
Gates that compose the time evolution circuit on STAR device:
{'CNOT', 'H', '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(nisq_trotter_factory(evolution_time)))
print("\n")
print("Evaluation result on STAR device:")
pprint.pprint(star_vm.analyze(star_trotter_factory(evolution_time)))
# output:
Evaluation result on NISQ device:
AnalyzeResult(lowering_level=<LoweringLevel.ArchLogicalCircuit: 1>,
qubit_count=np.int64(4),
gate_count=3540,
depth=2386,
latency=TimeValue(value=1317960.0, unit=<TimeUnit.NANOSECOND>),
fidelity=2.7799009245661343e-10)


Evaluation result on STAR device:
AnalyzeResult(lowering_level=<LoweringLevel.ArchLogicalCircuit: 1>,
qubit_count=np.int64(4),
gate_count=1460,
depth=770,
latency=TimeValue(value=16380000.0, unit=<TimeUnit.NANOSECOND>),
fidelity=np.float64(0.9958925186936065))

We can see that the fidelity of the time evolution citcuit improves from 101010^{-10} to 99.5% at the cost of about 12 times execution time on the error corrected STAR architecture.