Executar e decodificar programas lógicos

Use Microsoft Quantum Development Kit (QDK) para compilar um programa lógico Q# ou OpenQASM para Quantum Intermediate Representation (QIR) e, em seguida, use qdk.simulation.run_qir para executar o programa por meio de um qodec. Configure um modelo de ruído e escolha o decodificador de síndrome integrado ou o decodificador de nível de circuito opcional deq.

Prerequisites

Conclua estas tarefas antes de começar:

Os exemplos Q# usam um qodec chamado protocol que tem instruções para o código C4. Para criar um, consulte Desenvolver e validar um protocolo QEC.

import qodec
from qdk import ec

c4 = qodec.Code(
    "C4",
    stabilizers=["X_0 X_1 X_2 X_3", "Z_0 Z_1 Z_2 Z_3"],
    x=["X_0 X_1", "X_0 X_2"],
    z=["Z_0 Z_2", "Z_0 Z_1"],
)

protocol = ec.build_qodec(
    c4,
    strategy="bare-css/v1",
    strict=False,
)

report = ec.audit(protocol)
if report.errors or report.warnings:
    raise ValueError(str(report))

Audite o qodec antes de executá-lo. O runtime confia nas verificações, leituras, sinalizadores e equações de quadro do qodec, de modo que declarações inválidas podem causar resultados lógicos incorretos ou falhas de decodificador.

Esse fluxo de trabalho usa qdk.simulation.run_qir com um qodec. As ferramentas qodec usam o pacote separado StimPython para analisar circuitos de dispositivos no formato Stim. Ele não invoca qdk.stim.run. No LinuxArm64 ou WindowsArm64, instale Stim a partir do código-fonte e reinicie Python ou o kernel Jupyter antes de executar um qodec que tenha circuitos no formato Stim.

Corresponder chamadas QIR a instruções qodec

Corresponda cada chamada quântica no programa QIR a uma instrução no conjunto de instruções superior de qodec. A instrução mnemônica deve corresponder exatamente ao nome do receptor QIR.

A tabela a seguir mostra mapeamentos de exemplo:

Chamada de fonte Mnemônico necessário qodec
Q# X(q) ou OpenQASMx q; __quantum__qis__x__body
Q# H(q) ou OpenQASMh q; __quantum__qis__h__body
Q# CNOT(a, b) ou OpenQASMcx a, b; __quantum__qis__cx__body
Q# Adjoint S(q) ou OpenQASMsdg q; __quantum__qis__s__adj
Q# M(q) ou OpenQASMc = measure q; __quantum__qis__m__body
Q# Operação intrínseca Foo Foo
OpenQASM portão intrínseco ou def denominado foo foo

QIR os argumentos se vinculam aos qodec elementos de instrução nesta ordem:

  1. Argumentos de ponteiro de qubit são associados a operandos de bloco.
  2. Argumentos escalares são vinculados a parâmetros de instrução.
  3. Os argumentos de ponteiro de resultado recebem resultados de instrução e quaisquer sinalizadores retornados.

O compilador gerará um erro se uma chamada quântica não corresponder a uma instrução no conjunto de instruções de nível superior.

Declarar instruções lógicas em Q#

Um qodec compilado a partir do código C4 expõe instruções personalizadas como prepare_z_all, syndrome, measure_z_all e cx_all. Declare Q# operações intrínsecas com os mesmos nomes.

Inicialize Q# com o perfil de destino adaptável porque as operações de medição personalizadas exigem isso.

from qdk import TargetProfile, qsharp

qsharp.init(target_profile=TargetProfile.Adaptive_RI)

Declare as instruções C4 como operações Q#.

qsharp.eval("""
@Measurement()
operation prepare_z_all(block : Qubit) : Result { body intrinsic; }

@Measurement()
operation prepare_x_all(block : Qubit) : Result { body intrinsic; }

@Measurement()
operation measure_z_all(block : Qubit) : (Result, Result) { body intrinsic; }

@Measurement()
operation measure_x_all(block : Qubit) : (Result, Result) { body intrinsic; }

operation cx_all(control : Qubit, target : Qubit) : Unit { body intrinsic; }
operation syndrome(block : Qubit) : Unit { body intrinsic; }
""")

Neste programa, cada Q#Qubit representa um bloco codificado. O C4 bloco tem dois qubits lógicos, portanto, cada operação de medida retorna dois resultados lógicos. Cada operação de preparação retorna um sinalizador de rejeição.

Compilar um experimento lógico Q#

Defina um experimento de SPAM (preparação e medição de estado).

qsharp.eval("""
operation TestSpamZ() : (Result[], Result[]) {
    use block = Qubit();
    let reject = prepare_z_all(block);
    let (left, right) = measure_z_all(block);
    ([reject], [left, right])
}
""")

Compile a operação TestSpamZ para QIR.

qir = str(qsharp.compile("TestSpamZ()"))

O QIR mantém as chamadas para prepare_z_all e measure_z_all. Quando o programa é executado, o runtime qodec compara esses nomes com o conjunto de instruções de nível superior e substitui cada chamada lógica por seu dispositivo.

Executar com o decodificador interno

Chame a função run_qir e passe o qodec. Se decoder estiver None, o runtime usará o decodificador interno de peso mínimo Pauli.

from qdk.simulation import run_qir

results = run_qir(
    qir,
    qodec=protocol,
    type="stabilizer",
    shots=100,
    seed=42,
)

print(results[:5])

Para executar um qodec, use estas opções de simulador:

type valor Behavior
None ou "stabilizer" Usar o back-end estabilizador
"cpu" Usar o back-end de vetor de estado para programas compatíveis
"gpu" Não é compatível com um qodec

O runtime prepara cada qubit do programa antes de seu primeiro uso, a menos que a primeira instrução crie seu bloco. Uma instrução de preparação tem blocos de saída, nenhum bloco de entrada e uma ação que estabiliza o elemento lógico Z.

Correções de decodificador de qubit único Pauli e atualizações de quadro qodec usam um quadro sem ruído Pauli, não portas físicas barulhentas.

Configurar o ruído do simulador

Defina o ruído em operações de simulador, não em nomes de instrução qodec. O runtime converte cada instrução de camada inferior em uma ou mais operações de simulador, de modo que as configurações de ruído se aplicam a essas operações.

O C4 passo a passo usa estes mapeamentos:

Instrução da camada inferior Operação do simulador campo NoiseConfig
R Preparar ou redefinir para Z+ mresetz
H Hadamard h
CX Controlled X cx
M Medição na base Z mresetz

Crie um modelo de ruído simples de 1%:

from qdk.simulation import NoiseConfig

noise = NoiseConfig()
for gate in (noise.h, noise.cx):
    gate.set_depolarizing(0.01)
noise.mresetz.x = 0.01

Execute o experimento com ruído:

results = run_qir(
    qir,
    qodec=protocol,
    noise=noise,
    type="stabilizer",
    shots=1000,
    seed=42,
    on_shot_failure="discard",
)

O ruído em mresetz atua no qubit após a medição ou preparação. Ele não inverte um bit de medição que o simulador já registrou. Para estudar inversões de bits registradas, use qodec modelos de falha de leitura e análise de gadget.

Falhas de disparo de controle

Use on_shot_failure para escolher como o runtime responde quando um sinalizador qodec, decodificador ou leitura não produz uma medição aceita.

Value Behavior
"discard" Omita as capturas com falha da lista retornada. Esse valor é o padrão.
"raise" Pare quando a primeira tentativa falhar.
"retry" Reinicie a tentativa com falha, até max_retries tentativas adicionais.

Com a política "discard", o argumento shots conta disparos tentados, de modo que a lista retornada pode ter menos itens do que shots.

Um sinalizador dispara a política de disparo somente quando o programa lógico não recebe esse sinalizador. Se uma operação Q#@Measurement() retornar um Result extra para o sinalizador, o programa receberá o sinalizador e decidirá se deseja rejeitar a tentativa.

No exemplo TestSpamZ, o programa retorna o sinalizador de preparação no primeiro array. Portanto, a on_shot_failure="discard" configuração não remove linhas em que o sinalizador retornado está One.

Selecionar um decodificador

Para escolher como os erros são decodificados, use o parâmetro decoder para passar um chamável de preparação do decodificador para cada camada codificada.

O QDK tem os seguintes decodificadores:

Decodificador Use para
Decodificador de síndrome padrão Inferir correções de peso mínimo Pauli a partir da evidência de síndrome. Use decoder=None.
Decodificador de quadros Acompanhe quadros sem ruído sem inferir falhas físicas. Use prepare_frame_decoder.
deq decodificador Decodifique modelos de falha no nível do circuito com deq. Use prepare_deq_decoder.

Usar a configuração padrão deq

Instalar deq e deq-runtime, em seguida, passar prepare_deq_decoder.

from qdk.simulation.decoders import prepare_deq_decoder

results = run_qir(
    qir,
    qodec=protocol,
    decoder=prepare_deq_decoder,
    noise=noise,
    type="stabilizer",
    shots=1000,
    seed=42,
    on_shot_failure="discard",
)

Por padrão, o deq adaptador usa a propagação de crença de retransmissão e um coordenador de janela. Para alterar essas configurações, use uma fábrica de runtime.

Configurar um runtime monolítico deq

Para escolher deq as configurações diretamente, passe uma fábrica de runtime para prepare_deq_decoder.

from functools import partial
from deq.runtime import Runtime
from qdk.simulation import decoders


def make_runtime(seed: int) -> Runtime:
    return Runtime(
        decoder="black-box-relay-bp",
        decoder_config={"parallel": 1, "seed": seed},
        coordinator="monolithic",
        controller="jit",
    )


results = run_qir(
    qir,
    qodec=protocol,
    decoder=partial(
        decoders.prepare_deq_decoder,
        runtime_factory=make_runtime,
    ),
    noise=noise,
    type="stabilizer",
    shots=1000,
    seed=42,
    on_shot_failure="discard",
)

Retorne um novo Runtime de cada chamada de fábrica. O QDK inicia e encerra cada runtime.

Note

O adaptador deq requer o seguinte:

  • Uma camada codificada
  • Um rastreamento de execução independente de medição Clifford
  • O back-end estabilizador
  • Um modelo de ruído sem perda de qubit

O deq adaptador não dá suporte à política de repetição. Programas sem suporte geram um erro em vez de recorrer a outro decodificador.

Rejeitar resultados de baixa confiança deq

O deq adaptador retorna uma pontuação para cada leitura lógica. Pontuações mais baixas indicam uma preferência mais forte para a correção escolhida. As pontuações não são probabilidades de falha calibradas.

Para rejeitar resultados de baixa confiança, passe max_readout_score ao preparar o decodificador, como no exemplo a seguir:

from functools import partial
from qdk.simulation import decoders

scored_decoder = partial(
    decoders.prepare_deq_decoder,
    max_readout_score=0.2,
)

results = run_qir(
    qir,
    qodec=protocol,
    decoder=scored_decoder,
    noise=noise,
    type="stabilizer",
    shots=1000,
    on_shot_failure="discard",
)

O runtime aceita uma tentativa somente quando cada leitura lógica retornada tem uma pontuação igual ou abaixo do limite. O limite não se aplica a flags brutas ou leituras intermediárias não utilizadas.

A rejeição baseada em pontuação pode reduzir a taxa de erros lógicos de disparos aceitos. Mas também reduz o número de tentativas aceitas e adiciona trabalho de decodificador.

Compilar OpenQASM programas

As operações padrão OpenQASM usam as mesmas QIR regras mnemônicas que as operações padrão Q# .

from qdk import TargetProfile, openqasm

source = """
OPENQASM 3.0;
include "stdgates.inc";
qubit q;
bit c;
x q;
c = measure q;
"""

qir = openqasm.compile(
    source,
    target_profile=TargetProfile.Adaptive_RI,
)

print(qir)

Para executar este programa por meio de um qodec, verifique se o conjunto de instruções superior de qodec tem __quantum__qis__x__body e __quantum__qis__m__body com operandos de bloco compatíveis e um resultado de medida.

Uma OpenQASM porta intrínseca ou sub-rotina chamada foo usa o mnemônico foo. Argumentos qubit se vinculam a operandos de bloco. Os argumentos float, int e bool vinculam-se aos parâmetros da instrução na ordem de declaração.

OpenQASM tem essas limitações para chamadas qodec:

  • Intrínsecos personalizados não podem retornar um QIRResult. Para obter um resultado de medida, use a instrução padrão measure .
  • OpenQASM os programas não podem executar medições personalizadas, receber vários resultados de uma chamada ou receber sinalizadores retornados. Para esses casos, use operações Q#@Measurement().
  • O runtime controla todos os sinalizadores que o programa OpenQASM não pode receber, e esses sinalizadores podem rejeitar uma tentativa.

Para obter exemplos detalhados de mapeamento, consulte os seguintes QDK notebooks:

Examinar requisitos e limitações

Examine esses requisitos, limitações e diferenças de resultados antes de executar um grande experimento.

Requisitos:

  • Cada chamada quântica QIR deve resultar em uma instrução de camada superior qodec.
  • Audite o protocolo antes de executá-lo. O qodec runtime confia nas equações que você escreve e não as verifica.

Limitações:

  • GPU A simulação não dá suporte à qodec execução.
  • O conjunto de falhas padrão do gadget pode crescer exponencialmente, de modo que pesquisas de distância exata podem levar muito tempo.
  • Os pacotes deq e Stim têm limitações de plataforma. Para obter mais informações, consulte Instalação QDK para correção de erro.

Diferenças de resultados:

  • As contagens exatas de saída podem variar de acordo com o decodificador.
  • As políticas de descarte e repetição podem alterar a distribuição de resultados retornada.

Para obter um experimento completo Q# com varreduras de ruído, deq decodificação e rejeição baseada em confiança, consulte o passo a passo do protocolo QEC.