Skip to main content
IBM Quantum Platform

Utilizar a pós-seleção em cargas de trabalho

  • O código desta página foi desenvolvido com base nos seguintes requisitos. Recomendamos o uso dessas versões ou versões mais recentes.

    qiskit[all]~=2.5.0
    qiskit-ibm-runtime~=0.47.0
    qiskit-addon-utils~=0.3.1
    

Ao otimizar a estratégia de mitigação de erros de uma carga de trabalho, muitas vezes é útil filtrar as medições que se sabe terem sido contaminadas por processos de ruído não markovianos (correlacionados). Um desses métodos consiste em adicionar ao circuito uma etapa de pós-processamento que mede os qubits ativos e os qubits “espectadores” adjacentes, aplica uma rotação lenta a cada qubit e, em seguida, os mede novamente. Nos casos em que as duas medições não confirmam a inversão do qubit, conforme esperado, a medição é descartada por meio da aplicação de uma máscara aos resultados.

O pacote de utilitários do complemento Qiskit oferece um conjunto de etapas de transpilador e uma função de pós-seleção para aplicar a máscara. Esta página oferece orientações sobre como incorporar a pós-seleção em suas cargas de trabalho quânticas, utilizando como exemplo um estado GHZ de quatro qubits.


CREATE WORKLOAD

Comece preparando o circuito para ser executado e compilado para um backend que suporte portas fracionárias.

from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit.circuit import QuantumCircuit
from qiskit.transpiler import generate_preset_pass_manager

circuit = QuantumCircuit(4)
circuit.h(0)
circuit.cx(0, 1)
circuit.cx(1, 2)
circuit.cx(2, 3)
circuit.measure_all()


service = QiskitRuntimeService()
backend = service.least_busy(use_fractional_gates=True)
pm = generate_preset_pass_manager(optimization_level=3, backend=backend)

transpiled_circuit = pm.run(circuit)
transpiled_circuit.draw("mpl")

Output:

Output of the previous code cell

Adicionar etapas de transpilador de pós-seleção

Em seguida, crie um gerenciador de passagens predefinido que inclua as AddPostSelectionMeasures passagens e AddSpectatorMeasures do qiskit-addon-utils pacote. Isso acrescentará ao circuito uma sequência de rotações de ângulo RX pequeno (produzindo, na prática, um portão longo X ), juntamente com um segundo conjunto de medições.

from qiskit.transpiler import PassManager
from qiskit_addon_utils.noise_management.post_selection import PostSelector
from qiskit_addon_utils.noise_management.post_selection.transpiler.passes import (
    AddPostSelectionMeasures,
    AddSpectatorMeasures,
)


post_selection_pm = PassManager(
    [
        AddSpectatorMeasures(backend.coupling_map, add_barrier=True),
        AddPostSelectionMeasures(x_pulse_type="rx"),
    ]
)

template_circuit_ps = post_selection_pm.run(transpiled_circuit)
template_circuit_ps.draw("mpl", fold=-1, idle_wires=False)

Output:

Output of the previous code cell

Executar programa quântico

Em seguida, prepare um QuantumProgram objeto que contenha o circuito a ser executado.

from qiskit_ibm_runtime import QuantumProgram, Executor

shots = 4000

program = QuantumProgram(shots=shots)
program.append_circuit_item(template_circuit_ps)

# Initialize the Executor job and run
executor = Executor(backend)
executor_job = executor.run(program)
print(f"Job ID: {executor_job.job_id()}")

Output:

Job ID: d9bjj43v6alc73ct56rg

Agora você pode interpretar os resultados. O resultado da execução é um dicionário com várias chaves.

executor_result = executor_job.result()[0]
executor_result.keys()

Output:

KeysView(QuantumProgramItemResult({'meas': array([[False, False, False, False],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True],
       ...,
       [False, False, False, False],
       [False, False, False, False],
       [False, False, False, False]], shape=(4000, 4)), 'spec': array([[False, False],
       [False, False],
       [False, False],
       ...,
       [False, False],
       [False, False],
       [False, False]], shape=(4000, 2)), 'meas_ps': array([[ True,  True,  True,  True],
       [False, False, False, False],
       [False, False, False, False],
       ...,
       [ True,  True,  True,  True],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True]], shape=(4000, 4)), 'spec_ps': array([[ True,  True],
       [ True,  True],
       [ True,  True],
       ...,
       [ True,  True],
       [ True,  True],
       [ True,  True]], shape=(4000, 2))}, metadata=ItemMetadata()))

Essas chaves correspondem aos qubits ativo e espectador antes das rx instruções (meas e spec) e após as rx instruções (meas_ps e spec_ps). Cada um deles é uma matriz de matrizes, com base no número de disparos e qubits. Nesse caso, a forma é (1000, 4).


Criar máscara de pós-seleção

A partir dessas medidas, você pode criar uma máscara usando a PostSelector classe de qiskit-addon-utils. Esta máscara é uma matriz booleana na qual cada quadro é marcado como ou False``True com base em uma das duas estratégias de pós-seleção. A primeira estratégia nodeutiliza informações do qubit para decidir se uma tentativa de medição deve ser descartada — e a segunda edgeutiliza informações de conectividade entre vizinhos mais próximos para tomar essa decisão.

post_selector = PostSelector.from_circuit(
    circuit=template_circuit_ps, coupling_map=backend.coupling_map
)

mask_node = post_selector.compute_mask(executor_result, strategy="node")
mask_edge = post_selector.compute_mask(executor_result, strategy="edge")

Tanto a estratégia de nós quanto a de arestas costumam descartar jogadas diferentes. Você pode escolher qualquer uma delas. Este caderno utiliza uma operação AND bit a bit, que é uma estratégia conservadora que mantém uma tentativa apenas se ela for aprovada tanto pela estratégia de nós quanto pela estratégia de arestas.

mask = mask_node & mask_edge
print(f"The combined mask: {mask}")
count_retained = 0

for m in mask:
    count_retained += m

print(
    f"Percentage of the shots retained is after post selection "
    f"{100 * count_retained / shots}"
)

Output:

The combined mask: [ True  True  True ...  True  True  True]
Percentage of the shots retained is after post selection 84.675

Compare a distribuição de probabilidade com e sem pós-seleção. O trecho de código a seguir calcula a distribuição de probabilidade antes e depois da pós-seleção, bem como a distância entre as distribuições medida e ideal.

counts = {}
counts_ps = {}


for idx, measurement in enumerate(executor_result["meas"]):
    bitstring = ""
    for bit in measurement:
        bitstring += str(int(bit))

    if bitstring in counts:
        counts[bitstring] += 1
    else:
        counts[bitstring] = 1

    # Compute count data for postselected shots based on the mask
    if mask[idx]:
        bitstring = ""
        for bit in measurement:
            bitstring += str(int(bit))

        if bitstring in counts_ps:
            counts_ps[bitstring] += 1
        else:
            counts_ps[bitstring] = 1

for key, val in counts.items():
    counts[key] = val / shots


for key, val in counts_ps.items():
    counts_ps[key] = float(val / count_retained)

Para demonstrar como a pós-seleção alterou seus resultados, calcule a distância entre a distribuição de probabilidade ideal e as distribuições medidas.

import itertools
from qiskit.visualization import plot_histogram

bitstrings = ["".join(i) for i in itertools.product("01", repeat=4)]
counts_ideal = {}
for bitstring in bitstrings:
    counts_ideal[bitstring] = 0.0
counts_ideal["1111"] = 0.5
counts_ideal["0000"] = 0.5


prob_distance = 0.0
prob_distance_ps = 0.0

for bitstring in counts_ideal.keys():
    dist = 0.0
    dist_ps = 0.0
    if bitstring in counts:
        dist = abs(counts[bitstring] - counts_ideal[bitstring])
    if bitstring in counts_ps:
        dist_ps = abs(counts_ps[bitstring] - counts_ideal[bitstring])
    prob_distance += dist
    prob_distance_ps += dist_ps


print(
    f"Distance from ideal distribution before postselection: "
    f"{1-prob_distance*0.5}"
)
print(
    f"Distance from ideal distribution before after-selection: "
    f"{1-prob_distance_ps*0.5}"
)


plot_histogram([counts, counts_ps], legend=["Normal", "Post selected"])

Output:

Distance from ideal distribution before postselection: 0.95025
Distance from ideal distribution before after-selection: 0.9337171538234426
Output of the previous code cell

Embora a pós-seleção possa melhorar significativamente a qualidade dos resultados ao filtrar as medições de resultados afetadas por ruído não markoviano, ela não constitui, por si só, uma solução completa para a mitigação de erros. A pós-seleção reduz o impacto de certos erros ao descartar resultados de medição inválidos, mas isso acarreta um aumento na sobrecarga da amostragem e não resolve todos os mecanismos de erro presentes no hardware quântico de curto prazo. Consequentemente, é provável que não seja suficiente basear-se exclusivamente na pós-seleção para circuitos mais complexos ou mais profundos. Em vez disso, a pós-seleção é mais eficaz quando utilizada como parte de uma estratégia mais ampla de mitigação de erros — complementando técnicas como a mitigação de erros de medição, a compilação de circuitos sensíveis ao ruído ou o cancelamento probabilístico de erros — para melhorar a confiabilidade das cargas de trabalho quânticas, ao mesmo tempo em que equilibra a precisão e o custo dos recursos.


Próximas etapas

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