Utilizzo di diverse norme Lp per il troncamento del termine di Pauli
Nota: prima di leggere questa guida, è consigliabile consultare la guida sui termini di Pauli troncati, che descrive il troncamento dei termini di Pauli a basso peso integrato nel metodo di retropropagazione in base a un valore specificato di “ TruncationErrorBudget ”.
In questa guida imparerai a conoscere l'argomento della parola chiave backpropagate p_norm, che può essere utilizzato per modificare la norma Lp impiegata per stimare l'errore causato dai termini di Pauli troncati.
Costruire un circuito di esempio
Questa guida utilizza lo stesso circuito di esempio riportato nella guida sui termini di Pauli troncati :
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:
Definire un osservabile per la magnetizzazione totale:
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 titolo di riferimento, calcolare il valore esatto dell'aspettativa:
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
Utilizzare la norma " L1 "
Per impostazione predefinita, e come hai già visto nella guida sui termini di Pauli troncati, p_norm=1, il che significa che l'errore viene stimato come:
dove è lo stato quantistico, è la differenza reale tra l'osservabile esatto e quello troncato (che è sconosciuta), è l'insieme dei termini di Pauli che sono stati troncati e è il coefficiente dei termini di Pauli. Questa disuguaglianza rappresenta un limite superiore rigoroso ma molto approssimativo per la maggior parte degli scenari.
Questa guida esegue la retropropagazione su sei sezioni del circuito di esempio, utilizzando un errore costante per sezione pari a 0.001. Questo valore va inteso come il budget previsto nell'ambito di quanto indicato 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.
Ora possiamo calcolare il valore atteso dell'osservabile sottoposto a retropropagazione, nonché l'errore rispetto al riferimento esatto:
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
Infine, possiamo rappresentare graficamente l'errore verificatosi durante la retropropagazione di ciascuna sezione, nonché l'errore accumulato. L'errore cumulativo è dato dalla somma degli errori delle singole sezioni. Possiamo notare che l'errore accumulato rappresenta un limite superiore molto approssimativo dell'errore effettivo.
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:
Utilizzare la norma " L2 "
Si potrebbe sostenere che la norma “ L2 ” rappresenti un’approssimazione migliore dell’errore commesso rispetto alla norma “ L1 ”. Questo perché possiamo supporre che lo stato quantistico sia tratto da un insieme casuale di Haar; in tal caso, l’errore commesso segue una distribuzione con media nulla e una varianza che può essere approssimativamente limitata dalla norma L2 :
Sebbene il limite non sia rigoroso, verrà superato solo in casi patologici.
Eseguiamo nuovamente la retropropagazione su sei sezioni del circuito di esempio, utilizzando un errore massimo per sezione pari a ( 0.001 questa volta, interpretato secondo la norma di 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.
Ancora una volta, calcoliamo il valore atteso dell'osservabile sottoposto a retropropagazione e l'errore rispetto al riferimento esatto:
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
Se si rappresentano graficamente gli errori verificatisi per ogni sezione e l'errore accumulato, si ottiene un quadro simile a quello precedente.
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:
Si noti che l'errore accumulato è, ancora una volta, la somma degli errori delle singole sezioni. Si tratta di un altro limite approssimativo dovuto alla disuguaglianza di Minkowski, poiché dobbiamo calcolare questo limite in modo ricorsivo:
dove il nuovo indice indica l'iterazione corrente della slice, per cui rappresenta l'errore effettivo all'iterazione di retropropagazione , l'errore di troncamento approssimato dall'iterazione e l'insieme dei termini di Pauli troncati all'iterazione .