Skip to main content
IBM Quantum Platform

Utilizar diferentes normas Lp para o truncamento do termo de Pauli

Observação: Antes de ler este guia, você deve ler o guia sobre termos de Pauli truncados, que descreve o truncamento de termos de Pauli de baixo peso incorporado ao método de retropropagação com base em um valor de “ TruncationErrorBudget ” especificado.

Neste guia, você aprenderá sobre o argumento de palavra-chave backpropagate p_norm, que pode ser usado para alterar a norma Lp utilizada para estimar o erro decorrente dos termos de Pauli truncados.


Construa um circuito de exemplo

Este guia utiliza o mesmo circuito de exemplo apresentado no guia sobre os termos truncados de Pauli :

import rustworkx.generators
from qiskit.synthesis import LieTrotter
from qiskit_addon_utils.problem_generators import (
    PauliOrderStrategy,
    generate_time_evolution_circuit,
    generate_xyz_hamiltonian,
)
from qiskit_addon_utils.slicing import combine_slices, slice_by_gate_types

# Generate a linear chain of 10 qubits
num_qubits = 10
linear_chain = rustworkx.generators.path_graph(num_qubits)

# Use an arbitrary XY model
hamiltonian = generate_xyz_hamiltonian(
    linear_chain,
    coupling_constants=(0.05, 0.02, 0.0),
    ext_magnetic_field=(0.02, 0.08, 0.0),
    pauli_order_strategy=PauliOrderStrategy.InteractionThenColor,
)
# Evolve for some time
circuit = generate_time_evolution_circuit(
    hamiltonian, synthesis=LieTrotter(reps=3), time=2.0
)
# slice the circuit by gate type
slices = slice_by_gate_types(circuit)

# For visualization purposes only, recombine the slices with barriers between them and draw the resulting circuit
combine_slices(slices, include_barriers=True).draw("mpl", fold=50, scale=0.6)

Output:

Output of the previous code cell

Defina uma observável para a magnetização total:

from qiskit.quantum_info import SparsePauliOp

obs = SparsePauliOp.from_sparse_list(
    [("Z", [i], 1.0) for i in range(num_qubits)], num_qubits=num_qubits
)

A título de referência, calcule o valor exato da esperança:

from qiskit.primitives import StatevectorEstimator

estimator = StatevectorEstimator()
job = estimator.run([(circuit, obs)])
res = job.result()
exact_exp = res[0].data.evs
print(exact_exp)

Output:

9.318197859862146

Utilize a norma “ L1 ”

Por padrão, e como você já viu no guia sobre termos truncados de Pauli, p_norm=1, o que significa que o erro é estimado da seguinte forma:

∣⟨ψ∣Δ∣ψ⟩∣≤∑P∈T∣cP∣|\langle\psi|\Delta|\psi\rangle| \leq \sum_{P\in\mathcal{T}} |c_P|

onde ψ\psi é o estado quântico, Δ\Delta é a diferença real entre a observável exata e a truncada (que é desconhecida), T\mathcal{T} é o conjunto de termos de Pauli que foram truncados e cPc_P é o coeficiente dos termos de Pauli. Essa desigualdade é um limite superior rigoroso, mas bastante aproximado, para a maioria dos cenários.

Este guia realiza a retropropagação em seis fatias do circuito de exemplo, utilizando um erro constante por fatia de 0.001. Esse valor deve ser entendido como o orçamento dentro do p_norm.

from qiskit_addon_obp.utils.truncating import setup_budget

l1_truncation_error_budget = setup_budget(max_error_per_slice=0.001, p_norm=1)
print(l1_truncation_error_budget)

Output:

TruncationErrorBudget(per_slice_budget=[0.001], max_error_total=inf, p_norm=1)
from qiskit_addon_obp import backpropagate

max_slices = 6
l1_bp_obs, l1_remaining_slices, l1_metadata = backpropagate(
    obs,
    slices[-max_slices:],
    truncation_error_budget=l1_truncation_error_budget,
)
l1_reduced_circuit = combine_slices(
    slices[:-max_slices] + l1_remaining_slices
)
print(
    f"Backpropagated {max_slices - len(l1_remaining_slices)} circuit slices."
)
print(
    f"New observable contains {len(l1_bp_obs)} terms and {len(l1_bp_obs.group_commuting(qubit_wise=True))} commuting groups."
)

Output:

Backpropagated 6 circuit slices.
New observable contains 116 terms and 10 commuting groups.

Agora podemos calcular o valor esperado do observável retropropagado, bem como o erro em relação à referência exata:

estimator = StatevectorEstimator()
job = estimator.run([(l1_reduced_circuit, l1_bp_obs)])
res = job.result()
l1_exp = res[0].data.evs
l1_error = exact_exp - l1_exp
print(l1_exp, l1_error)

Output:

9.317869899338842 0.00032796052330397174

Por fim, podemos representar graficamente o erro incorrido durante a retropropagação de cada fatia, bem como o erro acumulado. O erro acumulado é a soma dos erros das fatias. Podemos observar que o erro acumulado é um limite superior bastante aproximado do erro real.

from matplotlib import pyplot as plt
from qiskit_addon_obp.utils.visualization import (
    plot_accumulated_error,
    plot_slice_errors,
)

fig, axes = plt.subplots(1, 2, figsize=(14, 5))
axes[1].plot([6], [l1_error], "x", color="red", label="actual error")
plot_slice_errors(l1_metadata, axes[0])
plot_accumulated_error(l1_metadata, axes[1])

Output:

Output of the previous code cell

Utilize a norma “ L2 ”

Pode-se argumentar que a norma “ L2 ” é uma aproximação melhor do erro incorrido do que a norma “ L1 ”. Isso ocorre porque podemos supor que o estado quântico ∣ψ⟩|\psi\rangle seja extraído de um conjunto aleatório de Haar; nesse caso, o erro incorrido segue uma distribuição com média nula e uma variância que pode ser aproximadamente limitada pela norma L2 :

∣⟨ψ∣Δ∣ψ⟩∣≲(∑P∈T∣cP∣2)1/2|\langle\psi|\Delta|\psi\rangle| \lesssim \left( \sum_{P\in\mathcal{T}} |c_P|^2 \right)^{1/2}

Embora o limite não seja rigoroso, ele só será violado em casos patológicos.

Mais uma vez, realizamos a retropropagação em seis fatias do circuito de exemplo, utilizando um erro máximo por fatia de 0.001 (desta vez, interpretado na norma L2 ).

l2_truncation_error_budget = setup_budget(max_error_per_slice=0.001, p_norm=2)
print(l2_truncation_error_budget)

Output:

TruncationErrorBudget(per_slice_budget=[0.001], max_error_total=inf, p_norm=2)
max_slices = 6
l2_bp_obs, l2_remaining_slices, l2_metadata = backpropagate(
    obs,
    slices[-max_slices:],
    truncation_error_budget=l2_truncation_error_budget,
)
l2_reduced_circuit = combine_slices(
    slices[:-max_slices] + l2_remaining_slices
)
print(
    f"Backpropagated {max_slices - len(l2_remaining_slices)} circuit slices."
)
print(
    f"New observable contains {len(l2_bp_obs)} terms and {len(l2_bp_obs.group_commuting(qubit_wise=True))} commuting groups."
)

Output:

Backpropagated 6 circuit slices.
New observable contains 84 terms and 6 commuting groups.

Mais uma vez, calculamos o valor esperado do observável retropropagado e do erro em relação à referência exata:

estimator = StatevectorEstimator()
job = estimator.run([(l2_reduced_circuit, l2_bp_obs)])
res = job.result()
l2_exp = res[0].data.evs
l2_error = exact_exp - l2_exp
print(l2_exp, l2_error)

Output:

9.317829770853422 0.00036808900872387085

Ao traçar os erros ocorridos por fatia e o erro acumulado, obtém-se um quadro semelhante ao anterior.

fig, axes = plt.subplots(1, 2, figsize=(14, 5))
axes[1].plot([6], [l2_error], "x", color="red", label="actual error")
plot_slice_errors(l2_metadata, axes[0])
plot_accumulated_error(l2_metadata, axes[1])

Output:

Output of the previous code cell

Observe que o erro acumulado é, mais uma vez, a soma dos erros de cada fatia. Este é outro limite aproximado decorrente da desigualdade de Minkowski, já que precisamos calcular esse limite de forma recursiva:

∣⟨ψ∣Δi∣ψ⟩∣≤∣⟨ψ∣Δ~i−1∣ψ⟩∣+(∑P∈Ti∣cP∣2)1/2=∣⟨ψ∣Δ~i∣ψ⟩∣|\langle\psi|\Delta_{i}|\psi\rangle| \leq |\langle\psi|\tilde{\Delta}_{i-1}|\psi\rangle| + \left( \sum_{P\in\mathcal{T_i}} |c_P|^2 \right)^{1/2} = |\langle\psi|\tilde{\Delta}_{i}|\psi\rangle|

onde o novo índice ii indica a iteração atual da fatia, tornando Δi\Delta_i o erro real na iteração de retropropagação ii, Δ~i−1\tilde{\Delta}_{i-1} o erro de truncamento aproximado da iteração i−1i-1 e Ti\mathcal{T}_i o conjunto de termos de Pauli truncados na iteração ii.

Esta página foi útil?
Relate um bug, erro de digitação ou solicite conteúdo no GitHub.