Executar e decodificar programas lógicos

Utilize o Microsoft Quantum Development Kit (QDK) para compilar um programa lógico Q# ou OpenQASM para Quantum Intermediate Representation (QIR), e depois use qdk.simulation.run_qir para executar o programa através de um qodec. Configure um modelo de ruído e depois escolha o descodificador de síndrome incorporado ou o decodificador opcional deq ao nível do circuito.

Prerequisites

Conclua estas tarefas antes de começar:

Os Q# exemplos usam um qodec denominado protocol que tem instruções para o C4 código. 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))

Audita o/a qodec antes de o executares. O runtime confia nas verificações, leituras, flags e equações de frame do qodec, pelo que declarações inválidas podem causar resultados lógicos incorretos ou falhas no descodificador.

Este fluxo de trabalho utiliza qdk.simulation.run_qir com um qodec. A qodec ferramenta utiliza o pacote separado StimPython pacote para analisar circuitos de gadgets no formato Stim. Não chama qdk.stim.run. Em LinuxArm64 ou Arm64Windows, instale Stim a partir da fonte e reinicie Python o Jupyter kernel antes de executar um qodec que tenha circuitos no formato Stim.

Associar chamadas QIR a instruções qodec

Associe cada chamada quântica no programa QIR a uma instrução no conjunto de instruções superior do qodec. O mnemónico da instrução deve corresponder exatamente ao nome da rotina chamada QIR.

A tabela seguinte mostra exemplos de mapeamentos:

Chamada de origem Tecla mnemónica qodec obrigatória
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 gate intrínseco ou def denominado foo foo

QIR Os argumentos ligam-se aos qodec elementos da instrução nesta ordem:

  1. Os argumentos ponteiro de qubit ligam-se a operandos de bloco.
  2. Argumentos escalares associam-se aos parâmetros da instrução.
  3. Os argumentos do ponteiro de resultado recebem resultados da instrução e quaisquer flags retornadas.

O compilador gera um erro se uma chamada quântica não corresponder a uma instrução no conjunto de instruções de topo.

Declarar instruções lógicas em Q#

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

Inicialize Q# com o perfil de alvo adaptativo porque as operações de medição personalizadas assim o exigem.

from qdk import TargetProfile, qsharp

qsharp.init(target_profile=TargetProfile.Adaptive_RI)

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

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, pelo que cada operação de medição retorna dois resultados lógicos. Cada operação de preparação devolve um indicador de rejeição.

Criar uma experiência lógica Q#

Defina uma experiência de preparação e medição de estado (SPAM).

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 para QIR.

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

O QIR mantém as chamadas a prepare_z_all e measure_z_all. Quando o programa é executado, o qodec tempo de execução compara estes nomes com o conjunto de instruções de topo e substitui cada chamada lógica pelo seu gadget.

Execute com o descodificador incorporado

Ligue run_qir e passe o qodec. Se decoder for None, o runtime utiliza o descodificador incorporado 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 do simulador:

type valor Behavior
None ou "stabilizer" Usar o backend do estabilizador
"cpu" Utilize o backend do vetor de estado para programas suportados
"gpu" Não suportado com um qodec

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

As Pauli correções de um único qubit do descodificador e qodec as atualizações de frame utilizam uma frame sem Pauli ruído, não portas físicas ruidosas.

Configurar ruído do simulador

Defina ruído nas operações do simulador, e não nos nomes das instruções qodec. O runtime traduz cada instrução da camada inferior numa ou mais operações do simulador, pelo que as configurações de ruído se aplicam a essas operações.

O C4 guia utiliza estes mapeamentos:

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

Crie um modelo simples de ruído 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

Faz 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 no mresetz atua sobre o qubit após a medição ou preparação. Não inverte um bit de medição que o simulador já registou. Para estudar os flips de bits registados, utilize qodec modelos de falhas de leitura e análise de gadgets.

Falhas de captura de controlo

Use on_shot_failure para escolher como o tempo de execução responde quando um qodec flag, descodificador ou saída de leitura não produz um shot aceite.

Value Behavior
"discard" Excluir as capturas falhadas da lista devolvida. Esse valor é o padrão.
"raise" Pára se o primeiro disparo falhar.
"retry" Reinicia a captura falhada, com um máximo de max_retries tentativas adicionais.

Com a política "discard", o argumento shots conta as tentativas, por isso a lista devolvida pode ter menos itens do que shots.

Uma flag só ativa a política de shot quando o programa lógico não recebe essa flag. Se uma operação Q#@Measurement() devolver uma Result extra para a flag, o programa recebe a flag e decide se rejeita o disparo.

No TestSpamZ exemplo, o programa devolve a bandeira de preparação no primeiro array. Assim, a on_shot_failure="discard" definição não remove as linhas onde está a flag Onedevolvida .

Selecione um descodificador

Para escolher como os erros são descodificados, use o parâmetro decoder para passar um callable de preparação do descodificador para cada camada codificada.

O QDK tem os seguintes descodificadores:

Descodificador Usa-o para
Descodificador predefinido de síndrome Inferir correções de peso Pauli mínimo a partir da evidência da síndrome. Utilize decoder=None.
Descodificador de frame Acompanhar frames sem ruído sem inferir falhas físicas. Utilize prepare_frame_decoder.
deq descodificador Descodifique modelos de falha ao nível do circuito com deq. Utilize prepare_deq_decoder.

Usar a configuração padrão deq

Instale deq e deq-runtime, depois passe 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 defeito, o adaptador deq utiliza relay belief propagation e um coordenador de janela. Para alterar estas definições, usa uma fábrica de execução.

Configurar um runtime monolítico deq

Para escolher deq definiçõ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",
)

Retorna um novo Runtime de cada chamada de fábrica. O QDK inicia e fecha cada runtime.

Note

O deq adaptador requer o seguinte:

  • Uma camada codificada
  • Um traço de execução independente da medição Clifford
  • O backend de estabilização
  • Um modelo de ruído sem perda de qubits

O deq adaptador não suporta a política de retentativas. Programas não suportados geram um erro em vez de recorrer a outro decodificador.

Rejeitar resultados de baixa confiança deq

O deq adaptador devolve uma pontuação para cada leitura lógica. Pontuações mais baixas indicam uma preferência mais forte pela 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 quando prepara o descodificador, como no seguinte exemplo:

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 um disparo apenas quando cada leitura lógica retornada tiver uma pontuação igual ou inferior ao limiar. O limiar não se aplica a flags brutos ou leituras intermédias não utilizadas.

A rejeição baseada na pontuação pode reduzir a taxa de erro lógica das capturas aceites. Mas também reduz o número de tentativas aceites e adiciona trabalho ao descodificador.

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 através de um qodec, certifique-se de que o conjunto de instruções de nível superior do qodec tem __quantum__qis__x__body e __quantum__qis__m__body, com operandos de bloco compatíveis e um resultado de medição.

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

OpenQASM Tem estas limitações para as chamadas qodec:

  • intrinsics personalizados não podem devolver um QIRResult. Para obter um resultado de medição, use a instrução padrão measure.
  • OpenQASM Os programas não conseguem executar medições personalizadas, receber vários resultados de uma chamada ou receber flags devolvidos. Nestes casos, use as operações Q#@Measurement().
  • O runtime controla quaisquer flags que o OpenQASM programa não consiga receber, e essas opções podem rejeitar uma execução.

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

Rever requisitos e limitações

Reveja estes requisitos, limitações e diferenças de resultados antes de realizar um grande experimento.

Requisitos:

  • Cada QIR chamada quântica deve corresponder a uma instrução de camada qodec superior.
  • Audite o protocolo antes de o executar. O qodec runtime confia nas equações que escreves e não as verifica.

Limitações:

  • GPU A simulação não suporta qodec a execução.
  • O conjunto padrão de falhas de gadget pode crescer exponencialmente, por isso as pesquisas de distância exata podem demorar muito tempo.
  • Os pacotes deq e Stim têm limitações na plataforma. Para mais informações, consulte Instalação QDK para correção de erros.

Diferenças de resultados:

  • As contagens exatas de saída podem variar consoante o decodificador.
  • As políticas de descarte e retentativa podem alterar a distribuição dos resultados devolvidos.

Para uma experiência completa Q# com varreduras de ruído, deq descodificação e rejeição baseada em confiança, consulte o guia do protocolo QEC.