Lumaktaw sa pangunahing nilalaman

Mga multi-product formula para mabawasan ang Trotter error

Tinatayang paggamit ng QPU: Apat na minuto sa isang Heron r2 processor (TANDAAN: Tantiya lamang ito. Maaaring mag-iba ang inyong runtime.)

Pangkalahatang-ideya

Ipinapakita ng tutorial na ito kung paano gamitin ang isang Multi-Product Formula (MPF) upang makamit ang mas mababang Trotter error sa ating observable kumpara sa error na maidudulot ng pinakamalalim na Trotter circuit na ating isasagawa. Binabawasan ng mga MPF ang Trotter error ng Hamiltonian dynamics sa pamamagitan ng may-timbang na kombinasyon ng ilang circuit execution. Isaalang-alang ang gawain ng paghahanap ng mga inaasahang halaga ng observable para sa quantum state ρ(t)=eiHtρ(0)eiHt\rho(t)=e^{-i H t} \rho(0) e^{i H t} na may Hamiltonian na HH. Maaaring gumamit ng mga Product Formula (PF) upang tantiyahin ang time-evolution na eiHte^{-i H t} sa pamamagitan ng sumusunod:

  • Isulat ang Hamiltonian na HH bilang H=a=1dFa,H=\sum_{a=1}^d F_a, kung saan ang FaF_a ay mga Hermitian operator na ang bawat katumbas na unitary ay maaaring mahusay na maipatupad sa isang quantum device.
  • Tantiyahin ang mga terminong FaF_a na hindi nag-commute sa isa't isa.

Kaya naman, ang first-order PF (Lie-Trotter formula) ay:

S1(t):=a=1deiFat,S_1(t):=\prod_{a=1}^d e^{-i F_a t},

na may quadratic error term na S1(t)=eiHt+O(t2)S_1(t)=e^{-i H t}+\mathcal{O}\left(t^{2}\right). Maaari ring gumamit ng higher-order PF (Lie-Trotter-Suzuki formula), na mas mabilis na magkakalapit, at tinukoy nang rekursibo bilang:

S2(t):=a=1deiFat/2a=1deiFat/2S_2(t):=\prod_{a=1}^d e^{-i F_a t/2}\prod_{a=1}^d e^{-i F_a t/2}

S2χ(t):=S2χ2(sχt)2S2χ2((14sχ)t)S2χ2(sχt)2,S_{2 \chi}(t):= S_{2 \chi -2}(s_{\chi}t)^2 S_{2 \chi -2}((1-4s_{\chi})t)S_{2 \chi -2}(s_{\chi}t)^2,

kung saan ang χ\chi ay ang order ng symmetric PF at sp=(441/(2p1))1s_p = \left( 4 - 4^{1/(2p-1)} \right)^{-1}. Para sa matagalang time-evolution, maaaring hatiin ang time interval na tt sa kk na interval, na tinatawag na mga Trotter step, na may tagal na t/kt/k at tantiyahin ang time-evolution sa bawat interval gamit ang χ\chi order product formula na SχS_{\chi}. Kaya naman, ang PF ng order na χ\chi para sa time-evolution operator sa kk Trotter step ay:

Sχk(t)=[Sχ(tk)]k=eiHt+O(t(tk)χ) S_{\chi}^{k}(t) = \left[ S_{\chi} \left( \frac{t}{k} \right)\right]^k = e^{-i H t}+O\left(t \left( \frac{t}{k} \right)^{\chi} \right)

kung saan ang error term ay bumababa kasabay ng bilang ng mga Trotter step na kk at ng order na χ\chi ng PF.

Kapag may ibinigay na integer na k1k \geq 1 at isang product formula na Sχ(t)S_{\chi}(t), ang tinantyang time-evolved state na ρk(t)\rho_k(t) ay maaaring makuha mula sa ρ0\rho_0 sa pamamagitan ng pag-apply ng kk ulit ng product formula na Sχ(tk)S_{\chi}\left(\frac{t}{k}\right).

ρk(t)=Sχ(tk)kρ0Sχ(tk)k\rho_k(t)=S_{\chi}\left(\frac{t}{k}\right)^k \rho_0 S_{\chi}\left(\frac{t}{k}\right)^{-k}

Ang ρk(t)\rho_k(t) ay isang tantiya para sa ρ(t)\rho(t) na may Trotter approximation error na ||ρk(t)ρ(t)\rho_k(t)-\rho(t) ||. Kung isasaalang-alang natin ang isang linear na kombinasyon ng mga Trotter approximation ng ρ(t)\rho(t):

μ(t)=jlxjρjkj(tkj)+some remaining Trotter error,\mu(t) = \sum_{j}^{l} x_j \rho^{k_j}_{j}\left(\frac{t}{k_j}\right) + \text{some remaining Trotter error} \, ,

kung saan ang xjx_j ay ang ating mga weighting coefficient, ang ρjkj\rho^{k_j}_j ay ang density matrix na katumbas ng pure state na nakuha sa pamamagitan ng pag-evolve ng initial state gamit ang product formula, SχkjS^{k_j}_{\chi}, na nagsasangkot ng kjk_j Trotter step, at ang j1,...,lj \in {1, ..., l} ay nag-i-index ng bilang ng mga PF na bumubuo sa MPF. Lahat ng termino sa μ(t)\mu(t) ay gumagamit ng parehong product formula na Sχ(t)S_{\chi}(t) bilang basehan. Ang layunin ay mapabuti ang ||ρk(t)ρ(t)\rho_k(t)-\rho(t) \| sa pamamagitan ng paghahanap ng μ(t)\mu(t) na may mas mababang μ(t)ρ(t)\|\mu(t)-\rho(t)\|.

  • Hindi kailangang maging pisikal na estado ang μ(t)\mu(t) dahil hindi kailangang positibo ang xix_i. Ang layunin dito ay mabawasan ang error sa inaasahang halaga ng mga observable at hindi upang makahanap ng pisikal na kapalit para sa ρ(t)\rho(t).
  • Tinutukoy ng kjk_j ang parehong circuit depth at antas ng Trotter approximation. Ang mas maliit na halaga ng kjk_j ay nagbubunga ng mas maikling mga circuit, na nagdudulot ng mas kaunting circuit error ngunit magiging mas hindi tumpak na tantiya ng nais na estado.

Ang susi dito ay ang natitirang Trotter error na ibinibigay ng μ(t)\mu(t) ay mas maliit kaysa sa Trotter error na makukuha kung gagamitin lamang ang pinakamataas na halaga ng kjk_j.

Maaaring tingnan ang kapaki-pakinabang nito mula sa dalawang perspektibo:

  1. Para sa isang nakapirming badyet ng Trotter step na maisasagawa, makakakuha ng mga resulta na may mas maliit na Trotter error sa kabuuan.
  2. Kung may target na bilang ng Trotter step na masyadong malaki upang maisagawa, maaaring gamitin ang MPF upang makahanap ng isang koleksyon ng mas mababang-lalim na mga circuit na nagbubunga ng katulad na Trotter error.

Mga Kinakailangan

Bago simulan ang tutorial na ito, tiyakin na naka-install ang mga sumusunod:

  • Qiskit SDK v1.0 o mas bago, na may suporta sa visualization
  • Qiskit Runtime v0.22 o mas bago (pip install qiskit-ibm-runtime)
  • MPF Qiskit addons (pip install qiskit_addon_mpf)
  • Qiskit addons utils (pip install qiskit_addon_utils)
  • Quimb library (pip install quimb)
  • Qiskit Quimb library (pip install qiskit-quimb)
  • Numpy v0.21 para sa compatibility sa iba't ibang package (pip install numpy==0.21)

Bahagi I. Maliit na halimbawa

Suriin ang katatagan ng MPF

Walang malinaw na limitasyon sa pagpili ng bilang ng mga Trotter step na kjk_j na bumubuo sa MPF state na μ(t)\mu(t). Gayunpaman, dapat itong maingat na piliin upang maiwasan ang kawalang-tatag sa mga inaasahang halagang kinakalkula mula sa μ(t)\mu(t). Ang isang magandang pangkalahatang tuntunin ay itakda ang pinakamaliit na Trotter step na kmink_{\text{min}} upang t/kmin<1t/k_{\text{min}} \lt 1. Kung nais ninyong matuto pa tungkol dito at kung paano pipiliin ang inyong iba pang kjk_j na halaga, sumangguni sa gabay na How to choose the Trotter steps for an MPF.

Sa halimbawa sa ibaba, sinusuri natin ang katatagan ng MPF solution sa pamamagitan ng pagkalkula ng inaasahang halaga ng magnetisasyon para sa isang hanay ng mga oras gamit ang iba't ibang time-evolved state. Partikular, inihahambing natin ang mga inaasahang halagang kinakalkula mula sa bawat isa sa mga tinantyang time-evolution na isinagawa gamit ang katumbas na Trotter step at ang iba't ibang MPF model (static at dynamic na coefficient) kasama ang eksaktong halaga ng time-evolved observable. Una, tukuyin natin ang mga parameter para sa mga Trotter formula at ang mga oras ng evolution.

# Added by doQumentation — installs packages not in the Binder environment
%pip install -q qiskit-addon-mpf
import numpy as np

mpf_trotter_steps = [1, 2, 4]
order = 2
symmetric = False

trotter_times = np.arange(0.5, 1.55, 0.1)
exact_evolution_times = np.arange(trotter_times[0], 1.55, 0.05)

Para sa halimbawang ito, gagamitin natin ang Neel state bilang initial state na Neel=0101...01\vert \text{Neel} \rangle = \vert 0101...01 \rangle at ang Heisenberg model sa isang linya ng 10 site para sa Hamiltonian na namamahala sa time-evolution

H^Heis=Ji=1L1(XiX(i+1)+YiY(i+1)+ZiZ(i+1)),\hat{\mathcal{H}}_{Heis} = J \sum_{i=1}^{L-1} \left(X_i X_{(i+1)}+Y_i Y_{(i+1)}+ Z_i Z_{(i+1)} \right) \, ,

kung saan ang JJ ay ang coupling strength para sa mga pinakamalapit na gilid.

from qiskit.transpiler import CouplingMap
from rustworkx.visualization import graphviz_draw
from qiskit_addon_utils.problem_generators import generate_xyz_hamiltonian
import numpy as np

L = 10

# Generate some coupling map to use for this example
coupling_map = CouplingMap.from_line(L, bidirectional=False)
graphviz_draw(coupling_map.graph, method="circo")

# Get a qubit operator describing the Heisenberg field model
hamiltonian = generate_xyz_hamiltonian(
coupling_map,
coupling_constants=(1.0, 1.0, 1.0),
ext_magnetic_field=(0.0, 0.0, 0.0),
)

print(hamiltonian)
SparsePauliOp(['IIIIIIIXXI', 'IIIIIIIYYI', 'IIIIIIIZZI', 'IIIIIXXIII', 'IIIIIYYIII', 'IIIIIZZIII', 'IIIXXIIIII', 'IIIYYIIIII', 'IIIZZIIIII', 'IXXIIIIIII', 'IYYIIIIIII', 'IZZIIIIIII', 'IIIIIIIIXX', 'IIIIIIIIYY', 'IIIIIIIIZZ', 'IIIIIIXXII', 'IIIIIIYYII', 'IIIIIIZZII', 'IIIIXXIIII', 'IIIIYYIIII', 'IIIIZZIIII', 'IIXXIIIIII', 'IIYYIIIIII', 'IIZZIIIIII', 'XXIIIIIIII', 'YYIIIIIIII', 'ZZIIIIIIII'],
coeffs=[1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,
1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j,
1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j])

Ang observable na ating susukat ay ang magnetisasyon sa isang pares ng qubit sa gitna ng chain.

from qiskit.quantum_info import SparsePauliOp

observable = SparsePauliOp.from_sparse_list(
[("ZZ", (L // 2 - 1, L // 2), 1.0)], num_qubits=L
)
print(observable)
SparsePauliOp(['IIIIZZIIII'],
coeffs=[1.+0.j])

Nagtatakda tayo ng transpiler pass upang kolektahin ang mga XX at YY rotation sa circuit bilang isang XX+YY gate. Papayagan nito tayong samantalahin ang mga katangian ng spin conservation ng TeNPy sa panahon ng MPO computation, na lubos na nagpapabilis ng pagkalkula.

from qiskit.circuit.library import XXPlusYYGate
from qiskit.transpiler import PassManager
from qiskit.transpiler.passes.optimization.collect_and_collapse import (
CollectAndCollapse,
collect_using_filter_function,
collapse_to_operation,
)
from functools import partial

def filter_function(node):
return node.op.name in {"rxx", "ryy"}

collect_function = partial(
collect_using_filter_function,
filter_function=filter_function,
split_blocks=True,
min_block_size=1,
)

def collapse_to_xx_plus_yy(block):
param = 0.0
for node in block.data:
param += node.operation.params[0]
return XXPlusYYGate(param)

collapse_function = partial(
collapse_to_operation,
collapse_function=collapse_to_xx_plus_yy,
)

pm = PassManager()
pm.append(CollectAndCollapse(collect_function, collapse_function))

Pagkatapos ay lilikha tayo ng mga circuit na nagpapatupad ng mga tinantyang Trotter time-evolution.

from qiskit.synthesis import SuzukiTrotter
from qiskit_addon_utils.problem_generators import (
generate_time_evolution_circuit,
)
from qiskit import QuantumCircuit

# Initial Neel state preparation
initial_state_circ = QuantumCircuit(L)
initial_state_circ.x([i for i in range(L) if i % 2 != 0])

all_circs = []
for total_time in trotter_times:
mpf_trotter_circs = [
generate_time_evolution_circuit(
hamiltonian,
time=total_time,
synthesis=SuzukiTrotter(reps=num_steps, order=order),
)
for num_steps in mpf_trotter_steps
]

mpf_trotter_circs = pm.run(
mpf_trotter_circs
) # Collect XX and YY into XX + YY

mpf_circuits = [
initial_state_circ.compose(circuit) for circuit in mpf_trotter_circs
]
all_circs.append(mpf_circuits)
mpf_circuits[-1].draw("mpl", fold=-1)

Output of the previous code cell

Susunod, kinakalkula natin ang mga time-evolved na inaasahang halaga mula sa mga Trotter circuit.

from copy import deepcopy
from qiskit_aer import AerSimulator
from qiskit_ibm_runtime import EstimatorV2 as Estimator
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

aer_sim = AerSimulator()
estimator = Estimator(mode=aer_sim)

mpf_expvals_all_times, mpf_stds_all_times = [], []
for t, mpf_circuits in zip(trotter_times, all_circs):
mpf_expvals = []
circuits = [deepcopy(circuit) for circuit in mpf_circuits]
pm_sim = generate_preset_pass_manager(
backend=aer_sim, optimization_level=3
)
isa_circuits = pm_sim.run(circuits)
result = estimator.run(
[(circuit, observable) for circuit in isa_circuits], precision=0.005
).result()
mpf_expvals = [res.data.evs for res in result]
mpf_stds = [res.data.stds for res in result]
mpf_expvals_all_times.append(mpf_expvals)
mpf_stds_all_times.append(mpf_stds)

Kinakalkula rin natin ang mga eksaktong inaasahang halaga para sa paghahambing.

from scipy.linalg import expm
from qiskit.quantum_info import Statevector

exact_expvals = []
for t in exact_evolution_times:
# Exact expectation values
exp_H = expm(-1j * t * hamiltonian.to_matrix())
initial_state = Statevector(initial_state_circ).data
time_evolved_state = exp_H @ initial_state

exact_obs = (
time_evolved_state.conj()
@ observable.to_matrix()
@ time_evolved_state
).real
exact_expvals.append(exact_obs)

Mga static na MPF coefficient

Ang mga static MPF ay ang mga kung saan ang mga halaga ng xjx_j ay hindi nakasalalay sa oras ng evolution na tt. Isaalang-alang ang order na χ=1\chi = 1 PF na may kjk_j Trotter step, maaari itong isulat bilang:

S1kj(tkj)=eiHt+n=1Antn+1kjnS_1^{k_j}\left( \frac{t}{k_j} \right)=e^{-i H t}+ \sum_{n=1}^{\infty} A_n \frac{t^{n+1}}{k_j^n}

kung saan ang AnA_n ay mga matrix na nakasalalay sa mga commutator ng mga FaF_a na termino sa decomposition ng Hamiltonian. Mahalaga pong tandaan na ang AnA_n mismo ay independyente sa oras at sa bilang ng mga Trotter step na kjk_j. Kaya naman, posibleng kanselahin ang mga lower-order error term na nag-aambag sa μ(t)\mu(t) sa pamamagitan ng maingat na pagpili ng mga timbang na xjx_j ng linear na kombinasyon. Upang kanselahin ang Trotter error para sa unang l1l-1 na termino (ang mga ito ay magbibigay ng pinakamalaking kontribusyon dahil katumbas ang mga ito ng mas mababang bilang ng Trotter step) sa expression para sa μ(t)\mu(t), ang mga coefficient na xjx_j ay dapat matugunan ang mga sumusunod na equation:

j=1lxj=1\sum_{j=1}^l x_j = 1 j=1l1xjkjn=0\sum_{j=1}^{l-1} \frac{x_j}{k_j^{n}} = 0

na may n=0,...l2n=0, ... l-2. Tinitiyak ng unang equation na walang bias sa constructed state na μ(t)\mu(t), habang tinitiyak ng pangalawang equation ang pagkansela ng mga Trotter error. Para sa higher-order PF, ang pangalawang equation ay nagiging j=1l1xjkjη=0\sum_{j=1}^{l-1} \frac{x_j}{k_j^{\eta}} = 0 kung saan ang η=χ+2n\eta = \chi + 2n para sa mga symmetric PF at η=χ+n\eta = \chi + n kung hindi, na may n=0,...,l2n=0, ..., l-2. Ang resulta na error (Refs. [1],[2]) ay

ϵ=O(tl+1k1l). \epsilon = \mathcal{O} \left( \frac{t^{l+1}}{k_1^l} \right).

Ang pagtatukoy ng mga static MPF coefficient para sa isang ibinigay na hanay ng mga halaga ng kjk_j ay katumbas ng paglutas ng linear system ng mga equation na tinukoy ng dalawang equation sa itaas para sa mga variable na xjx_j: Ax=bAx=b. Kung saan ang xx ang ating mga coefficient ng interes, ang AA ay isang matrix na nakasalalay sa kjk_j at sa uri ng PF na ating ginagamit (SS), at ang bb ay isang vector ng mga constraint. Partikular:

A0,j=1A_{0,j} = 1 Ai>0,j=kj(χ+s(i1))A_{i>0,j} = k_{j}^{-(\chi + s(i-1))} b0=1b_0 = 1 bi>0=0b_{i>0} = 0

kung saan ang χ\chi ay ang order, ang ss ay 22 kung ang symmetric ay True at 11 kung hindi, ang kjk_{j} ay ang trotter_steps, at ang xx ay ang mga variable na lulutas. Ang mga indeks na ii at jj ay nagsisimula sa 00. Maaari rin nating visualisahin ito sa matrix form:

A=[A0,0A0,1A0,2...A1,0A1,1A1,2...A2,0A2,1A2,2...............]=[111...k0(χ+s(11))k1(χ+s(11))k2(χ+s(11))...k0(χ+s(21))k1(χ+s(21))k2(χ+s(21))...............]A = \begin{bmatrix} A_{0,0} & A_{0,1} & A_{0,2} & ... \\ A_{1,0} & A_{1,1} & A_{1,2} & ... \\ A_{2,0} & A_{2,1} & A_{2,2} & ... \\ ... & ... & ... & ... \end{bmatrix} = \begin{bmatrix} 1 & 1 & 1 & ... \\ k_{0}^{-(\chi + s(1-1))} & k_{1}^{-(\chi + s(1-1))} & k_{2}^{-(\chi + s(1-1))} & ... \\ k_{0}^{-(\chi + s(2-1))} & k_{1}^{-(\chi + s(2-1))} & k_{2}^{-(\chi + s(2-1))} & ... \\ ... & ... & ... & ... \end{bmatrix}

at

b=[b0b1b2...]=[100...]b = \begin{bmatrix} b_{0} \\ b_{1} \\ b_{2} \\ ... \end{bmatrix} = \begin{bmatrix} 1 \\ 0 \\ 0 \\ ... \end{bmatrix}

Para sa karagdagang detalye, sumangguni sa dokumentasyon ng Linear System of Equations (LSE).

Maaari tayong makahanap ng solusyon para sa xx nang analitiko bilang x=A1bx = A^{-1}b; tingnan halimbawa ang Refs. [1] o [2]. Gayunpaman, ang eksaktong solusyong ito ay maaaring "ill-conditioned", na nagbubunga ng napakalaking L1-norm ng ating mga coefficient na xx, na maaaring magdulot ng masamang pagganap ng MPF. Sa halip, maaari ring makakuha ng tinantyang solusyon na nagpapaliit ng L1-norm ng xx upang subukang i-optimize ang gawi ng MPF.

I-set up ang LSE

Ngayon na napili na natin ang ating mga halaga ng kjk_j, kailangan muna nating itayo ang LSE, Ax=bAx=b gaya ng ipinaliwanag sa itaas. Ang matrix na AA ay nakasalalay hindi lamang sa kjk_j kundi pati na rin sa ating pagpili ng PF, partikular ang order nito. Bukod pa rito, maaari rin ninyong isaalang-alang kung ang PF ay symmetric o hindi (tingnan ang [1]) sa pamamagitan ng pagtatakda ng symmetric=True/False. Gayunpaman, hindi ito kinakailangan, gaya ng ipinapakita ng Ref. [2].

from qiskit_addon_mpf.static import setup_static_lse

lse = setup_static_lse(mpf_trotter_steps, order=order, symmetric=symmetric)

Suriin natin ang mga halagang pinili sa itaas upang itayo ang matrix na AA at ang vector na bb. Sa j=0,1,2j=0,1, 2 Trotter step na kj=[1,2,4]k_j = [1, 2, 4], order na χ=2\chi = 2 at pagpili ng non-symmetric Trotter step (s=1s=1), makikita natin na ang mga matrix element ng AA sa ibaba ng unang row ay tinutukoy ng expression na Ai>0,j=kj(2+1(i1))A_{i>0,j} = k_{j}^{-(2 + 1(i-1))}, partikular:

A0,0=A0,1=A0,2=1A_{0,0} = A_{0,1} = A_{0,2} = 1 A1,j=kj1A1,0=112,  ,A1,1=122,  ,A1,2=142 A_{1,j} = k_{j}^{-1} \rightarrow A_{1,0} = \frac{1}{1^2}, \;, A_{1,1} = \frac{1}{2^2}, \;, A_{1,2} = \frac{1}{4^2} A2,j=kj2A2,0=113,  ,A2,1=123,  ,A2,2=143 A_{2,j} = k_{j}^{-2} \rightarrow A_{2,0} = \frac{1}{1^3}, \;, A_{2,1} = \frac{1}{2^3}, \;, A_{2,2} = \frac{1}{4^3}

o sa matrix form:

A=[11111221421123143]A = \begin{bmatrix} 1 & 1 & 1\\ 1 & \frac{1}{2^2} & \frac{1}{4^2} \\ 1 & \frac{1}{2^3} & \frac{1}{4^3} \\ \end{bmatrix}

Maaari itong makita sa pamamagitan ng pagsusuri ng lse object:

lse.A
array([[1.      , 1.      , 1.      ],
[1. , 0.25 , 0.0625 ],
[1. , 0.125 , 0.015625]])

Habang ang vector ng mga constraint na bb ay may mga sumusunod na elemento: b0=1b_{0} = 1 b1=b2=0b_1 = b_2 = 0

Kaya naman,

b=[100]b = \begin{bmatrix} 1 \\ 0 \\ 0 \end{bmatrix}

At katulad nito sa lse:

lse.b
array([1., 0., 0.])

Ang lse object ay may mga pamamaraan para sa paghahanap ng mga static na coefficient na xjx_j na natutugunan ang sistema ng mga equation.

mpf_coeffs = lse.solve()
print(
f"The static coefficients associated with the ansatze are: {mpf_coeffs}"
)
The static coefficients associated with the ansatze are: [ 0.04761905 -0.57142857  1.52380952]
I-optimize ang xx gamit ang isang eksaktong modelo

Bilang alternatibo sa pagkalkula ng x=A1bx=A^{-1}b, maaari rin ninyong gamitin ang setup_exact_model upang itayo ang isang cvxpy.Problem instance na gumagamit ng LSE bilang mga constraint at ang optimal na solusyon nito ay magbubunga ng xx.

Sa susunod na seksyon, magiging malinaw kung bakit umiiral ang interface na ito.

from qiskit_addon_mpf.costs import setup_exact_problem

model_exact, coeffs_exact = setup_exact_problem(lse)
model_exact.solve()
print(coeffs_exact.value)
[ 0.04761905 -0.57142857  1.52380952]

Bilang tagapagpahiwatig kung ang isang MPF na itinayo gamit ang mga coefficient na ito ay magbibigay ng magandang resulta, maaari nating gamitin ang L1-norm (tingnan din ang Ref. [1]).

print(
"L1 norm of the exact coefficients:",
np.linalg.norm(coeffs_exact.value, ord=1),
) # ord specifies the norm. ord=1 is for L1
L1 norm of the exact coefficients: 2.1428571428556378
I-optimize ang xx gamit ang isang tinantyang modelo

Maaaring mangyari na ang L1 norm para sa piniling hanay ng mga halaga ng kjk_j ay masyadong mataas. Kung ganoon ang kaso at hindi kayo makapipili ng ibang hanay ng mga halaga ng kjk_j, maaari ninyong gamitin ang tinantyang solusyon sa LSE sa halip na eksaktong solusyon.

Upang gawin ito, gamitin lamang ang setup_approximate_model upang itayo ang ibang cvxpy.Problem instance, na naglilimita sa L1-norm sa isang piniling threshold habang pinipigilan ang pagkakaiba ng AxAx at bb.

from qiskit_addon_mpf.costs import setup_sum_of_squares_problem

model_approx, coeffs_approx = setup_sum_of_squares_problem(
lse, max_l1_norm=1.5
)
model_approx.solve()
print(coeffs_approx.value)
print(
"L1 norm of the approximate coefficients:",
np.linalg.norm(coeffs_approx.value, ord=1),
)
[-1.10294118e-03 -2.48897059e-01  1.25000000e+00]
L1 norm of the approximate coefficients: 1.5

Tandaan po na mayroon kayong ganap na kalayaan kung paano lutasin ang optimization problem na ito, na nangangahulugang maaari ninyong baguhin ang optimization solver, ang mga convergence threshold nito, at iba pa. Tingnan ang kaukulang gabay sa How to use the approximate model.

Dynamic MPF coefficients

Sa nakaraang seksyon, nagpakilala tayo ng static MPF na nagpapabuti sa karaniwang Trotter approximation. Gayunpaman, hindi kinakailangan na mino-minimize ng static na bersyon na ito ang error sa approximation. Sa konkretong paraan, ang static MPF na tinatawag na μS(t)\mu^S(t), ay hindi ang pinakamainam na projection ng ρ(t)\rho(t) sa subspace na sinasaklaw ng mga product-formula state na {ρki(t)}i=1r\{\rho_{k_i}(t)\}_{i=1}^r.

Upang matugunan ito, isinasaalang-alang natin ang isang dynamic MPF (ipinakikilala sa Ref. [2] at ipinakita sa eksperimento sa Ref. [3]) na talagang mino-minimize ang approximation error sa Frobenius norm. Pormal na sinasabi, nakatuon tayo sa pag-minimize ng

ρ(t)μD(t)F2  =  Tr[(ρ(t)μD(t))2],\|\rho(t) - \mu^D(t)\|_F^2 \;=\; \mathrm{Tr}\bigl[ \left( \rho(t) - \mu^D(t)\right)^2 \bigr],

kaugnay ng ilang mga coefficient na xi(t)x_i(t) sa bawat oras na tt. Ang pinakamainam na projector sa Frobenius norm ay μD(t)  =  i=1rxi(t)ρki(t)\mu^D(t) \;=\; \sum_{i=1}^r x_i(t)\,\rho_{k_i}(t), at tinatawag nating μD(t)\mu^D(t) ang dynamic MPF. Sa pamamagitan ng paglalagay ng mga kahulugan sa itaas:

ρ(t)μD(t)F2  =  =Tr[(ρ(t)μD(t))2]  =  =Tr[(ρ(t)i=1rxi(t)ρki(t))(ρ(t)j=1rxj(t)ρkj(t))]  =  =1  +  i,j=1rMi,j(t)xi(t)xj(t)    2i=1rLiexact(t)xi(t),\|\rho(t) - \mu^D(t)\|_F^2 \;=\; \\ = \mathrm{Tr}\bigl[ \left( \rho(t) - \mu^D(t)\right)^2 \bigr] \;=\; \\ = \mathrm{Tr}\bigl[ \left( \rho(t) - \sum_{i=1}^r x_i(t)\,\rho_{k_i}(t) \right) \left( \rho(t) - \sum_{j=1}^r x_j(t)\,\rho_{k_j}(t) \right) \bigr] \;=\; \\ = 1 \;+\; \sum_{i,j=1}^r M_{i,j}(t)\,x_i(t)\,x_j(t) \;-\; 2 \sum_{i=1}^r L_i^{\mathrm{exact}}(t)\,x_i(t),

kung saan ang Mi,j(t)M_{i,j}(t) ay ang Gram matrix, na tinukoy ng

Mi,j(t)  =  Tr[ρki(t)ρkj(t)]  =  ψin ⁣S(t/ki)kiS(t/kj)kj ⁣ψin2.M_{i,j}(t) \;=\; \mathrm{Tr}\bigl[\rho_{k_i}(t)\,\rho_{k_j}(t)\bigr] \;=\; \bigl|\langle \psi_{\mathrm{in}} \!\mid S\bigl(t/k_i\bigr)^{-k_i}\,S\bigl(t/k_j\bigr)^{k_j} \!\mid \psi_{\mathrm{in}} \rangle \bigr|^2.

at

Liexact(t)=Tr[ρ(t)ρki(t)]L_i^{\mathrm{exact}}(t) = \mathrm{Tr}[\rho(t)\,\rho_{k_i}(t)]

ay kumakatawan sa overlap sa pagitan ng eksaktong estado na ρ(t)\rho(t) at ng bawat product-formula approximation na ρki(t)\rho_{k_i}(t). Sa mga praktikal na sitwasyon, ang mga overlap na ito ay maaaring masukat lamang nang may katumpakan dahil sa ingay o limitadong access sa ρ(t)\rho(t).

Dito, ang ψin\lvert\psi_{\mathrm{in}}\rangle ay ang paunang estado, at ang S()S(\cdot) ay ang operasyon na inilalapat sa product formula. Sa pamamagitan ng pagpili ng mga coefficient na xi(t)x_i(t) na nagmi-minimize sa expression na ito (at pangangasiwa ng approximate overlap data kapag hindi ganap na kilala ang ρ(t)\rho(t)), nakakakuha tayo ng "pinakamahusay" (sa kahulugan ng Frobenius norm) na dynamic approximation ng ρ(t)\rho(t) sa loob ng MPF subspace. Ang mga dami na Li(t)L_i(t) at Mi,j(t)M_{i,j}(t) ay maaaring kalkulahin nang mahusay gamit ang mga pamamaraan ng tensor network [3]. Nagbibigay ang MPF Qiskit addon ng ilang "backend" para isagawa ang kalkulasyon. Ipinapakita ng halimbawa sa ibaba ang pinaka-flexible na paraan para gawin ito, at ang TeNPy layer-based backend docs ay nagpapaliwanag din nang may detalye. Upang magamit ang pamamaraang ito, magsimula sa circuit na nagpapatupad ng nais na time-evolution at lumikha ng mga modelo na kumakatawan sa mga operasyong ito mula sa mga layer ng kaukulang circuit. Sa huli, lumilikha ng isang Evolver na object na maaaring gamitin para makabuo ng mga time-evolved na dami na Mi,j(t)M_{i,j}(t) at Li(t)L_i(t). Nagsisimula tayo sa paglikha ng Evolver na object na katumbas ng approximate time-evolution (ApproxEvolverFactory) na ipinapatupad ng mga circuit. Sa partikular, bigyang-pansin ang variable na order upang matiyak na magkatugma ang mga ito. Pansinin na sa pagbuo ng mga circuit na katumbas ng approximate time-evolution, gumagamit tayo ng placeholder na mga halaga para sa time = 1.0 at sa bilang ng mga Trotter step (reps=1). Ang tamang mga approximating circuit ay ginagawa ng dynamic problem solver sa setup_dynamic_lse.

from qiskit_addon_utils.slicing import slice_by_depth
from qiskit_addon_mpf.backends.tenpy_layers import LayerModel
from qiskit_addon_mpf.backends.tenpy_layers import LayerwiseEvolver
from functools import partial

# Create approximate time-evolution circuits
single_2nd_order_circ = generate_time_evolution_circuit(
hamiltonian, time=1.0, synthesis=SuzukiTrotter(reps=1, order=order)
)
single_2nd_order_circ = pm.run(single_2nd_order_circ) # collect XX and YY

# Find layers in the circuit
layers = slice_by_depth(single_2nd_order_circ, max_slice_depth=1)

# Create tensor network models
models = [
LayerModel.from_quantum_circuit(layer, conserve="Sz") for layer in layers
]

# Create the time-evolution object
approx_factory = partial(
LayerwiseEvolver,
layers=models,
options={
"preserve_norm": False,
"trunc_params": {
"chi_max": 64,
"svd_min": 1e-8,
"trunc_cut": None,
},
"max_delta_t": 2,
},
)
babala

Ang mga opsyon ng LayerwiseEvolver na nagtatakda ng mga detalye ng tensor network simulation ay dapat piliin nang maingat upang maiwasan ang pagtatayo ng isang ill-defined na optimization problem.

Pagkatapos ay itinatayo natin ang exact evolver (halimbawa, ExactEvolverFactory), na nagbabalik ng isang Evolver na object na nagkalkula ng tunay o "sanggunian" na time-evolution. Sa realidad, aapproximate natin ang eksaktong evolution sa pamamagitan ng paggamit ng higher-order Suzuki–Trotter formula o iba pang maaasahang pamamaraan na may maliit na time step. Sa ibaba, ina-approximate natin ang eksaktong time-evolved state gamit ang fourth-order Suzuki-Trotter formula na may maliit na time step na dt=0.1, na nangangahulugang ang bilang ng mga Trotter step na ginagamit sa oras na tt ay k=t/dtk=t/dt. Tinutukoy din natin ang ilang TeNPy-specific na truncation options para limitahan ang maximum bond dimension ng pinagbabatayang tensor network, pati na rin ang minimum singular values ng mga hiwalay na bond ng tensor network. Ang mga parameter na ito ay maaaring makaapekto sa katumpakan ng expectation value na kinakalkula gamit ang dynamic MPF coefficients, kaya't mahalaga na suriin ang hanay ng mga halaga upang mahanap ang pinakamainam na balanse sa pagitan ng oras ng pagkalkula at katumpakan. Tandaan na ang pagkalkula ng mga MPF coefficient ay hindi nakasalalay sa expectation value ng PF na nakuha mula sa pagpapatupad sa hardware, at samakatuwid ay maaari itong i-tune sa post-processing.

single_4th_order_circ = generate_time_evolution_circuit(
hamiltonian, time=1.0, synthesis=SuzukiTrotter(reps=1, order=4)
)
single_4th_order_circ = pm.run(single_4th_order_circ)
exact_model_layers = [
LayerModel.from_quantum_circuit(layer, conserve="Sz")
for layer in slice_by_depth(single_4th_order_circ, max_slice_depth=1)
]

exact_factory = partial(
LayerwiseEvolver,
layers=exact_model_layers,
dt=0.1,
options={
"preserve_norm": False,
"trunc_params": {
"chi_max": 64,
"svd_min": 1e-8,
"trunc_cut": None,
},
"max_delta_t": 2,
},
)

Susunod, likhain ang paunang estado ng iyong sistema sa format na katugma sa TeNPy (halimbawa, isang MPS_neel_state=0101...01\vert 0101...01 \rangle). Itinatayo nito ang many-body wavefunction na ie-evolve mo sa oras na ψin\lvert\psi_{\mathrm{in}}\rangle bilang isang tensor.

from qiskit_addon_mpf.backends.tenpy_tebd import MPOState
from qiskit_addon_mpf.backends.tenpy_tebd import MPS_neel_state

def identity_factory():
return MPOState.initialize_from_lattice(models[0].lat, conserve=True)

mps_initial_state = MPS_neel_state(models[0].lat)

Para sa bawat time step na tt itinatayo natin ang dynamic linear system of equations gamit ang pamamaraan na setup_dynamic_lse. Ang kaukulang object ay naglalaman ng impormasyon tungkol sa dynamic MPF problem: ang lse.A ay nagbibigay ng Gram matrix na MM habang ang lse.b ay nagbibigay ng overlap na LL. Pagkatapos ay maaari nating solusyunan ang LSE (kapag hindi ito ill-defined) upang mahanap ang mga dynamic coefficient gamit ang setup_frobenius_problem. Mahalaga na mapansin ang pagkakaiba sa mga static coefficient, na nakasalalay lamang sa mga detalye ng product formula na ginamit at independiyente sa mga detalye ng time-evolution (Hamiltonian at paunang estado).

from qiskit_addon_mpf.dynamic import setup_dynamic_lse
from qiskit_addon_mpf.costs import setup_frobenius_problem

mpf_dynamic_coeffs_list = []
for t in trotter_times:
print(f"Computing dynamic coefficients for time={t}")
lse = setup_dynamic_lse(
mpf_trotter_steps,
t,
identity_factory,
exact_factory,
approx_factory,
mps_initial_state,
)
problem, coeffs = setup_frobenius_problem(lse)
try:
problem.solve()
mpf_dynamic_coeffs_list.append(coeffs.value)
except Exception as error:
mpf_dynamic_coeffs_list.append(np.zeros(len(mpf_trotter_steps)))
print(error, "Calculation Failed for time", t)
print("")
Computing dynamic coefficients for time=0.5

Computing dynamic coefficients for time=0.6

Computing dynamic coefficients for time=0.7

Computing dynamic coefficients for time=0.7999999999999999

Computing dynamic coefficients for time=0.8999999999999999

Computing dynamic coefficients for time=0.9999999999999999

Computing dynamic coefficients for time=1.0999999999999999

Computing dynamic coefficients for time=1.1999999999999997

Computing dynamic coefficients for time=1.2999999999999998

Computing dynamic coefficients for time=1.4

Computing dynamic coefficients for time=1.4999999999999998

Sa wakas, i-plot ang mga expectation value na ito sa buong oras ng evolution.

import matplotlib.pyplot as plt

sym = {1: "^", 2: "s", 4: "p"}
# Get expectation values at all times for each Trotter step
for k, step in enumerate(mpf_trotter_steps):
trotter_curve, trotter_curve_error = [], []
for trotter_expvals, trotter_stds in zip(
mpf_expvals_all_times, mpf_stds_all_times
):
trotter_curve.append(trotter_expvals[k])
trotter_curve_error.append(trotter_stds[k])

plt.errorbar(
trotter_times,
trotter_curve,
yerr=trotter_curve_error,
alpha=0.5,
markersize=4,
marker=sym[step],
color="grey",
label=f"{mpf_trotter_steps[k]} Trotter steps",
) # , , )

# Get expectation values at all times for the static MPF with exact coeffs
exact_mpf_curve, exact_mpf_curve_error = [], []
for trotter_expvals, trotter_stds in zip(
mpf_expvals_all_times, mpf_stds_all_times
):
mpf_std = np.sqrt(
sum(
[
(coeff**2) * (std**2)
for coeff, std in zip(coeffs_exact.value, trotter_stds)
]
)
)
exact_mpf_curve_error.append(mpf_std)
exact_mpf_curve.append(trotter_expvals @ coeffs_exact.value)

plt.errorbar(
trotter_times,
exact_mpf_curve,
yerr=exact_mpf_curve_error,
markersize=4,
marker="o",
label="Static MPF - Exact",
color="purple",
)

# Get expectation values at all times for the static MPF with approximate
approx_mpf_curve, approx_mpf_curve_error = [], []
for trotter_expvals, trotter_stds in zip(
mpf_expvals_all_times, mpf_stds_all_times
):
mpf_std = np.sqrt(
sum(
[
(coeff**2) * (std**2)
for coeff, std in zip(coeffs_approx.value, trotter_stds)
]
)
)
approx_mpf_curve_error.append(mpf_std)
approx_mpf_curve.append(trotter_expvals @ coeffs_approx.value)

plt.errorbar(
trotter_times,
approx_mpf_curve,
yerr=approx_mpf_curve_error,
markersize=4,
marker="o",
color="orange",
label="Static MPF - Approximate",
)

# # Get expectation values at all times for the dynamic MPF
dynamic_mpf_curve, dynamic_mpf_curve_error = [], []
for trotter_expvals, trotter_stds, dynamic_coeffs in zip(
mpf_expvals_all_times, mpf_stds_all_times, mpf_dynamic_coeffs_list
):
mpf_std = np.sqrt(
sum(
[
(coeff**2) * (std**2)
for coeff, std in zip(dynamic_coeffs, trotter_stds)
]
)
)
dynamic_mpf_curve_error.append(mpf_std)
dynamic_mpf_curve.append(trotter_expvals @ dynamic_coeffs)

plt.errorbar(
trotter_times,
dynamic_mpf_curve,
yerr=dynamic_mpf_curve_error,
markersize=4,
marker="o",
color="pink",
label="Dynamic MPF",
)

plt.plot(
exact_evolution_times,
exact_expvals,
lw=3,
color="red",
label="Exact time-evolution",
)

plt.title(
f"Expectation values for (ZZ,{(L//2-1, L//2)}) as a function of time"
)
plt.xlabel("Time")
plt.ylabel("Expectation Value")
plt.legend()
plt.grid()

Output of the previous code cell

Sa mga kaso tulad ng halimbawa sa itaas, kung saan ang k=1k=1 PF ay hindi gumaganap nang maayos sa lahat ng oras, ang kalidad ng mga resulta ng dynamic MPF ay lubos din na naaapektuhan. Sa mga ganitong sitwasyon, kapaki-pakinabang na suriin ang posibilidad ng paggamit ng mga indibidwal na PF na may mas mataas na bilang ng mga Trotter step upang mapabuti ang pangkalahatang kalidad ng mga resulta. Sa mga simulation na ito, nakikita natin ang ugnayan ng iba't ibang uri ng mga error: error mula sa limitadong sampling, at Trotter error mula sa mga product formula. Tinutulungan ng MPF na bawasan ang Trotter error dahil sa mga product formula ngunit nagdudulot ng mas mataas na sampling error kumpara sa mga product formula. Maaari itong maging kapaki-pakinabang, dahil ang mga product formula ay maaaring bawasan ang sampling error sa pamamagitan ng mas maraming sampling, ngunit ang systematic error dahil sa Trotter approximation ay nananatiling hindi nababago.

Isa pang kawili-wiling pag-uugali na maaari nating masundan mula sa plot ay ang expectation value para sa PF para sa k=1k=1 ay nagsisimulang kumilos nang pabago-bago (bukod sa hindi maging magandang approximation para sa eksaktong isa) sa mga oras kung saan ang t/k>1t/k > 1 , tulad ng ipinaliwanag sa gabay kung paano pumili ng bilang ng mga Trotter step.

Step 1: Map classical inputs to a quantum problem

Isaalang-alang natin ngayon ang isang solong oras na t=1.0t=1.0 at kalkulahin ang expectation value ng magnetization gamit ang iba't ibang pamamaraan na gumagamit ng isang QPU. Ang partikular na pagpili ng tt ay ginawa upang mapakinabangan ang pagkakaiba sa pagitan ng iba't ibang pamamaraan at mapansin ang kaukulang bisa ng bawat isa. Upang matukoy ang window ng oras kung saan ginagarantiyahan ng dynamic MPF na makabuo ng mga observable na may mas mababang error kaysa sa alinman sa mga indibidwal na Trotter formula sa loob ng multi-product, maaari nating ipatupad ang "MPF test" — tingnan ang equation (17) at kaugnay na teksto sa [3].

Set up the Trotter circuits

Sa puntong ito, nahanap na natin ang ating mga expansion coefficient na xx, at ang natitira na lang ay ang pagbuo ng mga Trotterized quantum circuit. Muli, ang module na qiskit_addon_utils.problem_generators ay nakatutulong na may kapaki-pakinabang na function para dito:

from qiskit.synthesis import SuzukiTrotter
from qiskit_addon_utils.problem_generators import (
generate_time_evolution_circuit,
)
from qiskit import QuantumCircuit

total_time = 1.0
mpf_circuits = []
for k in mpf_trotter_steps:
# Initial Neel state preparation
circuit = QuantumCircuit(L)
circuit.x([i for i in range(L) if i % 2 != 0])

trotter_circ = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(order=order, reps=k),
time=total_time,
)

circuit.compose(trotter_circ, inplace=True)

mpf_circuits.append(pm.run(circuit))
mpf_circuits[-1].draw("mpl", fold=-1, scale=0.4)

Output of the previous code cell

Step 2: Optimize problem for quantum hardware execution

Bumalik tayo sa pagkalkula ng expectation value para sa isang solong time point. Pipili tayo ng backend para isagawa ang eksperimento sa hardware.

from qiskit_ibm_runtime import QiskitRuntimeService

service = QiskitRuntimeService()
backend = service.least_busy(min_num_qubits=127)
print(backend)

qubits = list(range(backend.num_qubits))

Pagkatapos ay tinatanggal natin ang mga outlier qubit mula sa coupling map upang matiyak na hindi isasama ng transpiler sa layout stage ang mga ito. Sa ibaba, ginagamit natin ang iniulat na mga katangian ng backend na nakaimbak sa target na object at tinatanggal ang mga qubit na may measurement error o two-qubit gate na lampas sa isang tiyak na threshold (max_meas_err, max_twoq_err) o may T2T_2 time (na nagtatakda ng pagkawala ng coherence) na mas mababa sa isang tiyak na threshold (min_t2).

import copy
from qiskit.transpiler import Target, CouplingMap

target = backend.target
instruction_2q = "cz"

cmap = target.build_coupling_map(filter_idle_qubits=True)
cmap_list = list(cmap.get_edges())

max_meas_err = 0.012
min_t2 = 40
max_twoq_err = 0.005

# Remove qubits with bad measurement or t2
cust_cmap_list = copy.deepcopy(cmap_list)
for q in range(target.num_qubits):
meas_err = target["measure"][(q,)].error
if target.qubit_properties[q].t2 is not None:
t2 = target.qubit_properties[q].t2 * 1e6
else:
t2 = 0
if meas_err > max_meas_err or t2 < min_t2:
# print(q)
for q_pair in cmap_list:
if q in q_pair:
try:
cust_cmap_list.remove(q_pair)
except ValueError:
continue

# Remove qubits with bad 2q gate or t2
for q in cmap_list:
twoq_gate_err = target[instruction_2q][q].error
if twoq_gate_err > max_twoq_err:
# print(q)
for q_pair in cmap_list:
if q == q_pair:
try:
cust_cmap_list.remove(q_pair)
except ValueError:
continue

cust_cmap = CouplingMap(cust_cmap_list)

cust_target = Target.from_configuration(
basis_gates=backend.configuration().basis_gates
+ ["measure"], # or whatever new set of gates
coupling_map=cust_cmap,
)

sorted_components = sorted(
[list(comp.physical_qubits) for comp in cust_cmap.connected_components()],
reverse=True,
)
print("size of largest component", len(sorted_components[0]))
size of largest component 10

Nais nating itakda ang max_meas_err, min_t2, at max_twoq_err upang mahanap ang sapat na malaking subset ng mga qubit na sumusuporta sa circuit na tatakbo. Sa ating kaso, sapat na ang mahanap ang isang 10-qubit na 1D chain.

cust_cmap.draw()

Output of the previous code cell

Pagkatapos ay maaari nating i-map ang circuit at observable sa mga pisikal na qubit ng device.

from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

transpiler = generate_preset_pass_manager(
optimization_level=3, target=cust_target
)

transpiled_circuits = [transpiler.run(circ) for circ in mpf_circuits]

qubits_layouts = [
[
idx
for idx, qb in circuit.layout.initial_layout.get_physical_bits().items()
if qb._register.name != "ancilla"
]
for circuit in transpiled_circuits
]

transpiled_circuits = []
for circuit, layout in zip(mpf_circuits, qubits_layouts):
transpiler = generate_preset_pass_manager(
optimization_level=3, backend=backend, initial_layout=layout
)
transpiled_circuit = transpiler.run(circuit)
transpiled_circuits.append(transpiled_circuit)

# transform the observable defined on virtual qubits to
# an observable defined on all physical qubits
isa_observables = [
observable.apply_layout(circ.layout) for circ in transpiled_circuits
]
print(transpiled_circuits[-1].depth(lambda x: x.operation.num_qubits == 2))
print(transpiled_circuits[-1].count_ops())
transpiled_circuits[-1].draw("mpl", idle_wires=False, fold=False)
51
OrderedDict([('sx', 310), ('rz', 232), ('cz', 132), ('x', 19)])

Output of the previous code cell

Step 3: Execute using Qiskit primitives

Sa pamamagitan ng Estimator primitive, makukuha natin ang pagtatantya ng expectation value mula sa QPU. Isinasagawa natin ang mga na-optimize na AQC circuit na may karagdagang mga teknik ng error mitigation at suppression.

from qiskit_ibm_runtime import EstimatorV2 as Estimator

estimator = Estimator(mode=backend)
estimator.options.default_shots = 30000

# Set simple error suppression/mitigation options
estimator.options.dynamical_decoupling.enable = True
estimator.options.twirling.enable_gates = True
estimator.options.twirling.enable_measure = True
estimator.options.twirling.num_randomizations = "auto"
estimator.options.twirling.strategy = "active-accum"
estimator.options.resilience.measure_mitigation = True
estimator.options.experimental.execution_path = "gen3-turbo"

estimator.options.resilience.zne_mitigation = True
estimator.options.resilience.zne.noise_factors = (1, 3, 5)
estimator.options.resilience.zne.extrapolator = ("exponential", "linear")

estimator.options.environment.job_tags = ["mpf small"]

job = estimator.run(
[
(circ, observable)
for circ, observable in zip(transpiled_circuits, isa_observables)
]
)

Hakbang 4: I-post-process at ibalik ang resulta sa nais na klasikal na format

Ang tanging hakbang sa post-processing ay ang pagsasama ng expected value na nakuha mula sa Qiskit Runtime primitives sa iba't ibang Trotter steps gamit ang kani-kanilang MPF coefficients. Para sa isang observable na AA mayroon tayong:

Ampf=Tr[Aμ(t)]=jxjTr[Aρkj]=jxjAj \langle A \rangle_{\text{mpf}} = \text{Tr} [A \mu(t)] = \sum_{j} x_j \text{Tr} [A \rho_{k_j}] = \sum_{j} x_j \langle A \rangle_j

Una, kinukuha natin ang mga indibidwal na expected value na nakuha para sa bawat isa sa mga Trotter circuit:

result_exp = job.result()
evs_exp = [res.data.evs for res in result_exp]
evs_std = [res.data.stds for res in result_exp]

print(evs_exp)
[array(-0.06361607), array(-0.23820448), array(-0.50271805)]

Susunod, pinagsasama-sama lamang natin ang mga ito kasama ang ating mga MPF coefficient upang makuha ang kabuuang expected value ng MPF. Sa ibaba, ginagawa natin ito para sa bawat isa sa mga iba't ibang paraan kung saan kinomputin natin ang xx.

exact_mpf_std = np.sqrt(
sum(
[
(coeff**2) * (std**2)
for coeff, std in zip(coeffs_exact.value, evs_std)
]
)
)
print(
"Exact static MPF expectation value: ",
evs_exp @ coeffs_exact.value,
"+-",
exact_mpf_std,
)
approx_mpf_std = np.sqrt(
sum(
[
(coeff**2) * (std**2)
for coeff, std in zip(coeffs_approx.value, evs_std)
]
)
)
print(
"Approximate static MPF expectation value: ",
evs_exp @ coeffs_approx.value,
"+-",
approx_mpf_std,
)
dynamic_mpf_std = np.sqrt(
sum(
[
(coeff**2) * (std**2)
for coeff, std in zip(mpf_dynamic_coeffs_list[7], evs_std)
]
)
)
print(
"Dynamic MPF expectation value: ",
evs_exp @ mpf_dynamic_coeffs_list[7],
"+-",
dynamic_mpf_std,
)
Exact static MPF expectation value:  -0.6329590442738475 +- 0.012798249760406036
Approximate static MPF expectation value: -0.5690390035339492 +- 0.010459559917168473
Dynamic MPF expectation value: -0.4655579758795695 +- 0.007639139186720507

Sa wakas, para sa maliit na problemang ito maaari nating makuha ang eksaktong reference value gamit ang scipy.linalg.expm tulad ng sumusunod:

from scipy.linalg import expm
from qiskit.quantum_info import Statevector

exp_H = expm(-1j * total_time * hamiltonian.to_matrix())

initial_state_circuit = QuantumCircuit(L)
initial_state_circuit.x([i for i in range(L) if i % 2 != 0])
initial_state = Statevector(initial_state_circuit).data

time_evolved_state = exp_H @ initial_state

exact_obs = (
time_evolved_state.conj() @ observable.to_matrix() @ time_evolved_state
)
print("Exact expectation value ", exact_obs.real)
Exact expectation value  -0.39909900734489434
sym = {1: "^", 2: "s", 4: "p"}
# Get expectation values at all times for each Trotter step
for k, step in enumerate(mpf_trotter_steps):
plt.errorbar(
k,
evs_exp[k],
yerr=evs_std[k],
alpha=0.5,
markersize=4,
marker=sym[step],
color="grey",
label=f"{mpf_trotter_steps[k]} Trotter steps",
) # , , )

plt.errorbar(
3,
evs_exp @ coeffs_exact.value,
yerr=exact_mpf_std,
markersize=4,
marker="o",
color="purple",
label="Static MPF",
)

plt.errorbar(
4,
evs_exp @ coeffs_approx.value,
yerr=approx_mpf_std,
markersize=4,
marker="o",
color="orange",
label="Approximate static MPF",
)

plt.errorbar(
5,
evs_exp @ mpf_dynamic_coeffs_list[7],
yerr=dynamic_mpf_std,
markersize=4,
marker="o",
color="pink",
label="Dynamic MPF",
)

plt.axhline(
y=exact_obs.real,
linestyle="--",
color="red",
label="Exact time-evolution",
)

plt.title(
f"Expectation values for (ZZ,{(L//2-1, L//2)}) at time {total_time} for the different methods "
)
plt.xlabel("Method")
plt.ylabel("Expectation Value")
plt.legend(loc="upper center", bbox_to_anchor=(0.5, -0.2), ncol=2)
plt.grid(alpha=0.1)
plt.tight_layout()
plt.show()

Output of the previous code cell

Sa halimbawa sa itaas, ang dynamic MPF na pamamaraan ang pinakamahusay sa mga tuntunin ng expected value, na nagpapabuti kaysa sa makukuha nating resulta sa pamamagitan ng paggamit ng pinakamataas na bilang ng Trotter steps nang mag-isa. Kahit na ang iba't ibang MPF technique ay hindi palaging nagkakamit ng pinahusay na expected value kumpara sa pinakamataas na bilang ng Trotter steps (tulad ng exact at ang approximate na modelo sa plot sa itaas), ang standard deviation ng mga halagang ito ay mahusay na nakukuha ang tumaas na variance na natamo kapag ginagamit ang MPF technique. Itinatampok nito ang kawalan ng katiyakan sa paligid ng nakuhang expected value, na palaging kinabibilangan ng expected value na inaasahan natin mula sa eksaktong time-evolution ng sistema. Sa kabilang banda, ang mga expected value na kinakalkula gamit ang mas mababang bilang ng Trotter steps ay nabigo sa pagkuha ng eksaktong expected value sa loob ng kanilang kawalan ng katiyakan, kaya't tiwala nitong ibinabalik ang maling resulta.

def relative_error(ev, exact_ev):
return abs(ev - exact_ev)

relative_error_k = [relative_error(ev, exact_obs.real) for ev in evs_exp]
relative_error_mpf = relative_error(evs_exp @ mpf_coeffs, exact_obs.real)
relative_error_approx_mpf = relative_error(
evs_exp @ coeffs_approx.value, exact_obs.real
)
relative_error_dynamic_mpf = relative_error(
evs_exp @ mpf_dynamic_coeffs_list[7], exact_obs.real
)

print("relative error for each trotter steps", relative_error_k)
print("relative error with MPF exact coeffs", relative_error_mpf)
print("relative error with MPF approx coeffs", relative_error_approx_mpf)
print("relative error with MPF dynamic coeffs", relative_error_dynamic_mpf)
relative error for each trotter steps [0.33548293650112293, 0.16089452939226306, 0.10361904247828346]
relative error with MPF exact coeffs 0.2338600369291003
relative error with MPF approx coeffs 0.16993999618905486
relative error with MPF dynamic coeffs 0.06645896853467514

Bahagi II: palawakin ito

Palawakin natin ang problema nang higit pa sa kung ano ang posibleng ma-simulate nang eksakto. Sa seksyong ito, magtutuon tayo sa pagpapareplika ng ilang mga resulta na ipinapakita sa Ref. [3].

Hakbang 1: I-map ang mga klasikal na input sa isang quantum na problema

Hamiltonian

Para sa malawak na halimbawa, ginagamit natin ang XXZ model sa isang linya ng 50 sites:

H^XXZ=i=1L1Ji,(i+1)(XiX(i+1)+YiY(i+1)+2ZiZ(i+1)),\hat{\mathcal{H}}_{XXZ} = \sum_{i=1}^{L-1} J_{i,(i+1)}\left(X_i X_{(i+1)}+Y_i Y_{(i+1)}+ 2\cdot Z_i Z_{(i+1)} \right) \, ,

kung saan ang Ji,(i+1)J_{i,(i+1)} ay isang random coefficient na naaayon sa edge (i,i+1)(i, i+1). Ito ang Hamiltonian na isinasaalang-alang sa demonstrasyon na inilalahad sa Ref. [3].

L = 50
# Generate some coupling map to use for this example
coupling_map = CouplingMap.from_line(L, bidirectional=False)
graphviz_draw(coupling_map.graph, method="circo")

Output of the previous code cell

import numpy as np
from qiskit.quantum_info import SparsePauliOp, Pauli

# Generate random coefficients for our XXZ Hamiltonian
np.random.seed(0)
even_edges = list(coupling_map.get_edges())[::2]
odd_edges = list(coupling_map.get_edges())[1::2]

Js = np.random.uniform(0.5, 1.5, size=L)
hamiltonian = SparsePauliOp(Pauli("I" * L))
for i, edge in enumerate(even_edges + odd_edges):
hamiltonian += SparsePauliOp.from_sparse_list(
[
("XX", (edge), 2 * Js[i]),
("YY", (edge), 2 * Js[i]),
("ZZ", (edge), 4 * Js[i]),
],
num_qubits=L,
)

print(hamiltonian)
SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXX', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYY', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZ', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'XXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'YYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'ZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXI', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYI', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZI', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],
coeffs=[1. +0.j, 2.09762701+0.j, 2.09762701+0.j, 4.19525402+0.j,
2.43037873+0.j, 2.43037873+0.j, 4.86075747+0.j, 2.20552675+0.j,
2.20552675+0.j, 4.4110535 +0.j, 2.08976637+0.j, 2.08976637+0.j,
4.17953273+0.j, 1.8473096 +0.j, 1.8473096 +0.j, 3.6946192 +0.j,
2.29178823+0.j, 2.29178823+0.j, 4.58357645+0.j, 1.87517442+0.j,
1.87517442+0.j, 3.75034885+0.j, 2.783546 +0.j, 2.783546 +0.j,
5.567092 +0.j, 2.92732552+0.j, 2.92732552+0.j, 5.85465104+0.j,
1.76688304+0.j, 1.76688304+0.j, 3.53376608+0.j, 2.58345008+0.j,
2.58345008+0.j, 5.16690015+0.j, 2.05778984+0.j, 2.05778984+0.j,
4.11557968+0.j, 2.13608912+0.j, 2.13608912+0.j, 4.27217824+0.j,
2.85119328+0.j, 2.85119328+0.j, 5.70238655+0.j, 1.14207212+0.j,
1.14207212+0.j, 2.28414423+0.j, 1.1742586 +0.j, 1.1742586 +0.j,
2.3485172 +0.j, 1.04043679+0.j, 1.04043679+0.j, 2.08087359+0.j,
2.66523969+0.j, 2.66523969+0.j, 5.33047938+0.j, 2.5563135 +0.j,
2.5563135 +0.j, 5.112627 +0.j, 2.7400243 +0.j, 2.7400243 +0.j,
5.48004859+0.j, 2.95723668+0.j, 2.95723668+0.j, 5.91447337+0.j,
2.59831713+0.j, 2.59831713+0.j, 5.19663426+0.j, 1.92295872+0.j,
1.92295872+0.j, 3.84591745+0.j, 2.56105835+0.j, 2.56105835+0.j,
5.12211671+0.j, 1.23654885+0.j, 1.23654885+0.j, 2.4730977 +0.j,
2.27984204+0.j, 2.27984204+0.j, 4.55968409+0.j, 1.28670657+0.j,
1.28670657+0.j, 2.57341315+0.j, 2.88933783+0.j, 2.88933783+0.j,
5.77867567+0.j, 2.04369664+0.j, 2.04369664+0.j, 4.08739329+0.j,
1.82932388+0.j, 1.82932388+0.j, 3.65864776+0.j, 1.52911122+0.j,
1.52911122+0.j, 3.05822245+0.j, 2.54846738+0.j, 2.54846738+0.j,
5.09693476+0.j, 1.91230066+0.j, 1.91230066+0.j, 3.82460133+0.j,
2.1368679 +0.j, 2.1368679 +0.j, 4.2737358 +0.j, 1.0375796 +0.j,
1.0375796 +0.j, 2.0751592 +0.j, 2.23527099+0.j, 2.23527099+0.j,
4.47054199+0.j, 2.22419145+0.j, 2.22419145+0.j, 4.44838289+0.j,
2.23386799+0.j, 2.23386799+0.j, 4.46773599+0.j, 2.88749616+0.j,
2.88749616+0.j, 5.77499231+0.j, 2.3636406 +0.j, 2.3636406 +0.j,
4.7272812 +0.j, 1.7190158 +0.j, 1.7190158 +0.j, 3.4380316 +0.j,
1.87406391+0.j, 1.87406391+0.j, 3.74812782+0.j, 2.39526239+0.j,
2.39526239+0.j, 4.79052478+0.j, 1.12045094+0.j, 1.12045094+0.j,
2.24090189+0.j, 2.33353343+0.j, 2.33353343+0.j, 4.66706686+0.j,
2.34127574+0.j, 2.34127574+0.j, 4.68255148+0.j, 1.42076512+0.j,
1.42076512+0.j, 2.84153024+0.j, 1.2578526 +0.j, 1.2578526 +0.j,
2.51570519+0.j, 1.6308567 +0.j, 1.6308567 +0.j, 3.2617134 +0.j])

Para sa isang observable, pinipili natin ang Z24Z25Z_{24}Z_{25}, tulad ng makikita sa lower panel ng Fig. 5 ng Ref. [3].

observable = SparsePauliOp.from_sparse_list(
[("ZZ", (L // 2 - 1, L // 2), 1.0)], num_qubits=L
)
print(observable)
SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIII'],
coeffs=[1.+0.j])

Pumili ng mga Trotter step

Ang eksperimentong ipinapakita sa Fig. 4 ng Ref. [3] ay gumagamit ng kj=[2,3,4]k_j = [2, 3, 4] na symmetric Trotter steps ng order na 22. Nakatutok tayo sa mga resulta para sa oras na t=3t=3, kung saan ang MPF at ang isang PF na may mas mataas na bilang ng Trotter steps (6 sa kasong ito) ay may parehong Trotter error. Gayunpaman, ang MPF expected value ay kinakalkula mula sa mga circuit na naaayon sa mas mababang bilang ng Trotter steps at sa gayon ay mas mababaw. Sa praktika, kahit na ang MPF at ang mas malalim na Trotter steps circuit ay may parehong Trotter error, inaasahan nating ang experimental expected value na kinakalkula mula sa mga MPF circuit ay magiging mas malapit sa teoriya, dahil nangangailangan ito ng pagpapatakbo ng mas mababaw na mga circuit na mas hindi nalantad sa hardware noise kumpara sa circuit na naaayon sa mas mataas na Trotter step PF.

total_time = 3
mpf_trotter_steps = [2, 3, 4]
order = 2
symmetric = True

I-set up ang LSE

Dito tinitignan natin ang mga static MPF coefficient para sa problemang ito.

lse = setup_static_lse(mpf_trotter_steps, order=order, symmetric=symmetric)
mpf_coeffs = lse.solve()
print(
f"The static coefficients associated with the ansatze are: {mpf_coeffs}"
)
print("L1 norm:", np.linalg.norm(mpf_coeffs, ord=1))
The static coefficients associated with the ansatze are: [ 0.26666667 -2.31428571  3.04761905]
L1 norm: 5.628571428571431
model_approx, coeffs_approx = setup_sum_of_squares_problem(
lse, max_l1_norm=2.0
)
model_approx.solve()
print(coeffs_approx.value)
print(
"L1 norm of the approximate coefficients:",
np.linalg.norm(coeffs_approx.value, ord=1),
)
[-0.24255546 -0.25744454  1.5       ]
L1 norm of the approximate coefficients: 2.0

Mga dynamic coefficient

# Create approximate time-evolution circuits
single_2nd_order_circ = generate_time_evolution_circuit(
hamiltonian, time=1.0, synthesis=SuzukiTrotter(reps=1, order=order)
)
single_2nd_order_circ = pm.run(single_2nd_order_circ) # collect XX and YY

# Find layers in the circuit
layers = slice_by_depth(single_2nd_order_circ, max_slice_depth=1)

# Create tensor network models
models = [
LayerModel.from_quantum_circuit(layer, conserve="Sz") for layer in layers
]

# Create the time-evolution object
approx_factory = partial(
LayerwiseEvolver,
layers=models,
options={
"preserve_norm": False,
"trunc_params": {
"chi_max": 64,
"svd_min": 1e-8,
"trunc_cut": None,
},
"max_delta_t": 4,
},
)

# Create exact time-evolution circuits
single_4th_order_circ = generate_time_evolution_circuit(
hamiltonian, time=1.0, synthesis=SuzukiTrotter(reps=1, order=4)
)
single_4th_order_circ = pm.run(single_4th_order_circ)
exact_model_layers = [
LayerModel.from_quantum_circuit(layer, conserve="Sz")
for layer in slice_by_depth(single_4th_order_circ, max_slice_depth=1)
]

# Create the time-evolution object
exact_factory = partial(
LayerwiseEvolver,
layers=exact_model_layers,
dt=0.1,
options={
"preserve_norm": False,
"trunc_params": {
"chi_max": 64,
"svd_min": 1e-8,
"trunc_cut": None,
},
"max_delta_t": 3,
},
)

def identity_factory():
return MPOState.initialize_from_lattice(models[0].lat, conserve=True)

mps_initial_state = MPS_neel_state(models[0].lat)

lse = setup_dynamic_lse(
mpf_trotter_steps,
total_time,
identity_factory,
exact_factory,
approx_factory,
mps_initial_state,
)
problem, coeffs = setup_frobenius_problem(lse)
try:
problem.solve()
mpf_dynamic_coeffs = coeffs.value
except Exception as error:
print(error, "Calculation Failed for time", total_time)
print("")

Itayo ang bawat Trotter circuit sa aming MPF decomposition

from qiskit.synthesis import SuzukiTrotter
from qiskit_addon_utils.problem_generators import (
generate_time_evolution_circuit,
)
from qiskit import QuantumCircuit

mpf_circuits = []
for k in mpf_trotter_steps:
# Initial state preparation |1010..>
circuit = QuantumCircuit(L)
circuit.x([i for i in range(L) if i % 2])

trotter_circ = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=k, order=order),
time=total_time,
)

circuit.compose(trotter_circ, qubits=range(L), inplace=True)

mpf_circuits.append(circuit)

Itayo ang Trotter circuit na may katulad na Trotter error sa MPF

k = 6

# Initial state preparation |1010..>
comp_circuit = QuantumCircuit(L)
comp_circuit.x([i for i in range(L) if i % 2])

trotter_circ = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=k, order=order),
time=total_time,
)

comp_circuit.compose(trotter_circ, qubits=range(L), inplace=True)

mpf_circuits.append(comp_circuit)

Hakbang 2: I-optimize ang problema para sa pagpapatakbo sa quantum hardware

import copy
from qiskit.transpiler import Target, CouplingMap

target = backend.target
instruction_2q = "cz"

cmap = target.build_coupling_map(filter_idle_qubits=True)
cmap_list = list(cmap.get_edges())

max_meas_err = 0.055
min_t2 = 30
max_twoq_err = 0.01

# Remove qubits with bad measurement or t2
cust_cmap_list = copy.deepcopy(cmap_list)
for q in range(target.num_qubits):
meas_err = target["measure"][(q,)].error
if target.qubit_properties[q].t2 is not None:
t2 = target.qubit_properties[q].t2 * 1e6
else:
t2 = 0
if meas_err > max_meas_err or t2 < min_t2:
# print(q)
for q_pair in cmap_list:
if q in q_pair:
try:
cust_cmap_list.remove(q_pair)
except ValueError:
continue

# Remove qubits with bad 2q gate or t2
for q in cmap_list:
twoq_gate_err = target[instruction_2q][q].error
if twoq_gate_err > max_twoq_err:
# print(q)
for q_pair in cmap_list:
if q == q_pair:
try:
cust_cmap_list.remove(q_pair)
except ValueError:
continue

cust_cmap = CouplingMap(cust_cmap_list)

cust_target = Target.from_configuration(
basis_gates=backend.configuration().basis_gates
+ ["measure"], # or whatever new set of gates
coupling_map=cust_cmap,
)

sorted_components = sorted(
[list(comp.physical_qubits) for comp in cust_cmap.connected_components()],
reverse=True,
)
print("size of largest component", len(sorted_components[0]))
size of largest component 73
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

transpiler = generate_preset_pass_manager(
optimization_level=3, target=cust_target
)

transpiled_circuits = [transpiler.run(circ) for circ in mpf_circuits]

qubits_layouts = [
[
idx
for idx, qb in circuit.layout.initial_layout.get_physical_bits().items()
if qb._register.name != "ancilla"
]
for circuit in transpiled_circuits
]

transpiled_circuits = []
for circuit, layout in zip(mpf_circuits, qubits_layouts):
transpiler = generate_preset_pass_manager(
optimization_level=3, backend=backend, initial_layout=layout
)
transpiled_circuit = transpiler.run(circuit)
transpiled_circuits.append(transpiled_circuit)

# transform the observable defined on virtual qubits to
# an observable defined on all physical qubits
isa_observables = [
observable.apply_layout(circ.layout) for circ in transpiled_circuits
]

Hakbang 3: Isagawa gamit ang mga Qiskit primitive

from qiskit_ibm_runtime import EstimatorV2 as Estimator

estimator = Estimator(mode=backend)
estimator.options.default_shots = 30000

# Set simple error suppression/mitigation options
estimator.options.dynamical_decoupling.enable = True
estimator.options.twirling.enable_gates = True
estimator.options.twirling.enable_measure = True
estimator.options.twirling.num_randomizations = "auto"
estimator.options.twirling.strategy = "active-accum"
estimator.options.resilience.measure_mitigation = True
estimator.options.experimental.execution_path = "gen3-turbo"

estimator.options.resilience.zne_mitigation = True
estimator.options.resilience.zne.noise_factors = (1, 1.2, 1.4)
estimator.options.resilience.zne.extrapolator = "linear"

estimator.options.environment.job_tags = ["mpf large"]

job_50 = estimator.run(
[
(circ, observable)
for circ, observable in zip(transpiled_circuits, isa_observables)
]
)

Hakbang 4: Post-process at ibalik ang resulta sa nais na klasikal na format

result = job_50.result()
evs = [res.data.evs for res in result]
std = [res.data.stds for res in result]

print(evs)
print(std)
[array(-0.08034071), array(-0.00605026), array(-0.15345759), array(-0.18127293)]
[array(0.04482517), array(0.03438413), array(0.21540776), array(0.21520829)]
exact_mpf_std = np.sqrt(
sum([(coeff**2) * (std**2) for coeff, std in zip(mpf_coeffs, std[:3])])
)
print(
"Exact static MPF expectation value: ",
evs[:3] @ mpf_coeffs,
"+-",
exact_mpf_std,
)
approx_mpf_std = np.sqrt(
sum(
[
(coeff**2) * (std**2)
for coeff, std in zip(coeffs_approx.value, std[:3])
]
)
)
print(
"Approximate static MPF expectation value: ",
evs[:3] @ coeffs_approx.value,
"+-",
approx_mpf_std,
)
dynamic_mpf_std = np.sqrt(
sum(
[
(coeff**2) * (std**2)
for coeff, std in zip(mpf_dynamic_coeffs, std[:3])
]
)
)
print(
"Dynamic MPF expectation value: ",
evs[:3] @ mpf_dynamic_coeffs,
"+-",
dynamic_mpf_std,
)
Exact static MPF expectation value:  -0.47510243192011536 +- 0.6613940032465087
Approximate static MPF expectation value: -0.20914170384216998 +- 0.32341567460419135
Dynamic MPF expectation value: -0.07994951978722761 +- 0.07423091963310202
sym = {2: "^", 3: "s", 4: "p"}
# Get expectation values at all times for each Trotter step
for k, step in enumerate(mpf_trotter_steps):
plt.errorbar(
k,
evs[k],
yerr=std[k],
alpha=0.5,
markersize=4,
marker=sym[step],
color="grey",
label=f"{mpf_trotter_steps[k]} Trotter steps",
)

plt.errorbar(
3,
evs[-1],
yerr=std[-1],
alpha=0.5,
markersize=8,
marker="x",
color="blue",
label="6 Trotter steps",
)

plt.errorbar(
4,
evs[:3] @ mpf_coeffs,
yerr=exact_mpf_std,
markersize=4,
marker="o",
color="purple",
label="Static MPF",
)

plt.errorbar(
5,
evs[:3] @ coeffs_approx.value,
yerr=approx_mpf_std,
markersize=4,
marker="o",
color="orange",
label="Approximate static MPF",
)

plt.errorbar(
6,
evs[:3] @ mpf_dynamic_coeffs,
yerr=dynamic_mpf_std,
markersize=4,
marker="o",
color="pink",
label="Dynamic MPF",
)

exact_obs = -0.24384471447172074 # Calculated via Tensor Network calculation
plt.axhline(
y=exact_obs, linestyle="--", color="red", label="Exact time-evolution"
)

plt.title(
f"Expectation values for (ZZ,{(L//2-1, L//2)}) at time {total_time} for the different methods "
)
plt.xlabel("Method")
plt.ylabel("Expectation Value")
plt.legend(loc="upper center", bbox_to_anchor=(0.5, -0.2), ncol=2)
plt.grid(alpha=0.1)
plt.tight_layout()
plt.show()

Output of the previous code cell

Kapag nagpapatakbo ng mga circuit sa hardware, maaaring makatagpo tayo ng mga karagdagang hamon sa pagkuha ng tumpak na mga expectation value dahil sa presensya ng hardware noise. Hindi ito isinasaalang-alang sa MPF formalism at maaaring makapagpahina sa solusyon ng MPF. Halimbawa, ito ang maaaring dahilan kung bakit nabigo ang mga dynamic coefficient na magbigay ng mas tumpak na pagtatantya ng expectation value kumpara sa approximate static coefficient sa plot. Ibig sabihin, ang approximate evolver, na simumuyla ng approximate circuit, ay hindi tumpak na sumasalamin sa mga resulta na nakuha sa pamamagitan ng pagpapatakbo ng mga approximate circuit sa presensya ng hardware noise. Sa mga kadahilanang ito, inirerekomenda na pagsamahin ang iba't ibang teknik ng error mitigation upang makakuha ng mga resulta na kasing-lapit ng posible sa mga ideal na halaga para sa bawat isa sa mga product formula. Ipapakita nito ang pare-parehong mga benepisyo mula sa diskarteng MPF.

Sa kabuuan, ang mga approximate static coefficient ay nagbibigay pa rin ng mas tumpak na solusyon kaysa sa product formula na may mas mataas na bilang ng Trotter step na may parehong dami ng Trotter error sa noiseless na kapaligiran.

Mahalaga rin na tandaan na sa halimbawa na nagpapareplika ng eksperimento sa Ref. [3], ang time point na t=3t=3 ay lampas sa limitasyon kung saan inaasahang gumagana nang maayos ang PF na may k=2k=2, na siyang t/k>1t/k>1 ayon sa tinalakay sa gabay na ito.

Mga Sanggunian

[1] Vazquez, A. C., Egger, D. J., Ochsner, D., & Woerner, S. (2023). Well-conditioned multi-product formulas for hardware-friendly Hamiltonian simulation. Quantum, 7, 1067.

[2] Zhuk, S., Robertson, N. F., & Bravyi, S. (2024). Trotter error bounds and dynamic multi-product formulas for Hamiltonian simulation. Physical Review Research, 6(3), 033309.