Desenvolver e validar um protocolo QEC

Use qodec e o qdk.ec módulo do Microsoft Quantum Development Kit (QDK) para definir um código de correção de erro quântico (QEC) e transformá-lo em um protocolo executável. Neste artigo, você define um código de erro, cria uma linha de base qodec, valida suas declarações, calcula a distância do gadget e salva o protocolo.

Os exemplos neste artigo usam o código $[4,2,2]]$ C4 , que armazena dois qubits lógicos em quatro qubits físicos e detecta qualquer erro físico Pauli único.

Prerequisites

Conclua estas tarefas antes de começar:

  • Instale QDK para correção de erro.
  • Use um ambiente com Python 3.11 ou posterior.
  • Confirme se o qodecStim analisador está disponível. On LinuxArm64 or WindowsArm64, install Stim from source, and then restart Python or the Jupyter kernel before you call build_qodec.

Definir o código

Crie um qodec.Code objeto com os estabilizadores de código e um representante lógico de X e Z para cada qubit lógico.

import qodec

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"],
)

O qodec pacote usa notação esparsa para Pauli cadeias de caracteres. Cada termo tem uma Pauli letra e um rótulo de qubit físico. Por exemplo, X_0 X_2 aplica X aos qubits físicos 0 e 2.

A definição de código é um objeto matemático estático. Ele não inclui circuitos que preparam, preservam ou medem estados codificados.

Analisar o código

Para inspecionar um instantâneo dos operadores de código, crie um CodeProfile.

from qdk import ec

profile = ec.CodeProfile(c4)

print("Physical support:", sorted(profile.support))
print("Physical qubits:", profile.length)
print("Logical qubits:", profile.logical_qubit_count)
print("Stabilizers:", profile.stabilizers)
print("Logical X operators:", profile.x)
print("Logical Z operators:", profile.z)

O perfil não será alterado se você alterar posteriormente o objeto original qodec.Code .

Calcular a distância do código

Para localizar o número mínimo de erros físicos permitidos que causam um efeito lógico nãotrivial com síndrome zero, chame distance.

distance = profile.distance()
print("Code distance:", distance)

error = distance.witness.product
syndrome = profile.syndrome_of(error)
logical_effect = profile.logical_effect_of(error)

print("Witness:", error)
print("Syndrome:", sorted(syndrome))
print("Logical effect:", logical_effect)

assert distance.value == 2
assert profile.is_logical(error)

Por padrão, a pesquisa atribui um custo a cada erro X, Y ou Z de qubit único. O retornado Distance tem limites certificados e uma testemunha para qualquer limite superior finito.

Quando uma pesquisa exata completa custar muito, use distance_bounds.

bounds = profile.distance_bounds(upper_bound=4)
print("Distance bounds:", bounds)
print("Lower bound:", bounds.lower_bound)
print("Upper bound:", bounds.upper_bound)

O solucionador padrão é HiGHS. Para usar um solucionador diferente, defina solver="enumeration"ou instale MWPF e defina solver="mwpf".

Criar uma linha de base qodec

Descreve qodec como executar instruções codificadas. Ele tem conjuntos de instruções, códigos, camadas, gadgets, circuitos, verificações e equações de leitura.

Use build_qodec para criar um protocolo de duas camadas com base no C4 código.

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

Este exemplo usa bare-css/v1 para permitir que você inspecione um protocolo simples sem qubits de sinalizador.

Caution

A bare-css/v1 estratégia não promete circuitos tolerantes a falhas. Calcule a distância de cada gadget antes de usar o protocolo para experimentos barulhentos.

O construtor tenta criar estas instruções lógicas:

  • prepare_z_all
  • prepare_x_all
  • syndrome
  • measure_z_all
  • measure_x_all
  • h_all
  • cx_all

O _all sufixo significa que uma instrução age em cada qubit lógico em um bloco. Por exemplo, cx_all aplica-se entre qubits CNOT lógicos correspondentes em dois blocos.

Nem todo código dá suporte a todas as instruções do candidato. Com strict=True, que é o padrão, o construtor gera um erro quando não pode compilar e verificar um candidato. Com strict=Falseo construtor, o construtor mantém as instruções e registros com suporte em protocol.metadata["qdk.ec"]["build"]["omitted"].

Inspecione a camada lógica e seus gadgets.

logical_layer = protocol.layers[0]

print("Instructions:")
for mnemonic in logical_layer.instruction_set.instructions:
    print(" -", mnemonic)

print("Gadgets:")
for mnemonic in logical_layer.gadgets:
    print(" -", mnemonic)

A segunda camada contém o conjunto de instruções físicas Stim que os circuitos gerados usam.

Inspecionar um gadget

Uma instrução lógica declara um contrato. Seu gadget dá o circuito que implementa o contrato.

Inspecione o prepare_z_all gadget.

gadget = logical_layer.gadgets["prepare_z_all"]
gadget_profile = ec.GadgetProfile(gadget)

print("Declared objective:")
print(gadget_profile.objective)

print("Computed action:")
print(gadget_profile.action)

Compare a ação computada do circuito com o objetivo de instrução declarado.

objective = gadget_profile.objective
assert objective is not None

if gadget_profile.action.is_equivalent_to(objective):
    print("The gadget implements its declared instruction.")
else:
    print(objective.why_not_equivalent_to(gadget_profile.action))

Um GadgetProfile captura um instantâneo de sua entrada, para que ele não reflita alterações posteriores. Depois de editar um gadget, crie um novo perfil.

Auditar o protocolo

Use audit para verificar o todo qodec.

report = ec.audit(protocol)
print(report)

Se a auditoria não encontrar erros ou avisos, ela imprimirá:

audit: ok

A auditoria verifica estas áreas:

  • Álgebra de código
  • Instrução e estrutura de camadas
  • Chamadas de circuito e associações de parâmetro
  • Ações de gadget
  • Verificações e equações de leitura
  • Equações de sinalizador
  • Relações de quadro de entrada e saída
  • Limites de referência e integridade do protocolo

Para agir sobre severidades específicas, use as coleções de relatórios.

if report.errors:
    for diagnostic in report.errors:
        print(diagnostic)
    raise ValueError("The qodec has audit errors.")

if report.warnings:
    for diagnostic in report.warnings:
        print(diagnostic)

O report.ok valor significa que o relatório não tem erros. Avisos não alteram essa propriedade.

Derivar equações ausentes

Use filled quando um gadget de rascunho ou qodec omite verificações ou equações de leitura lógicas que a simulação exata pode derivar.

O exemplo a seguir cria uma cópia incompleta do protocolo.

draft = qodec.Qodec.loads(protocol.dumps())
draft_gadgets = draft.layers[0].gadgets

draft_gadgets["measure_x_all"].readouts = []
draft_gadgets["cx_all"].checks = []

print(ec.audit(draft))

Preencha as equações com suporte e audite o resultado.

completed = ec.filled(draft)
print(ec.audit(completed))

A filled função retorna um novo gadget ou qodec não altera sua entrada. Ele deriva verificações, equações de leitura observáveis e relações de quadro. Ele preserva as associações de sinalizador porque a simulação sem ruído não pode identificar quais falhas um sinalizador deve detectar.

Se o protocolo original já tiver equações diretas verificadas, filled mantenha-as quando possível.

Analisar falhas de gadget

Uma auditoria sem erros prova a consistência sem ruídos. Não prova tolerância a falhas.

Chame GadgetProfile.distance para localizar o número mínimo de falhas de circuito permitidas que causam uma falha lógica enquanto todas as verificações e sinalizadores declarados permanecem zero. A falha combinada também deve deixar cada saída dentro de seu espaço de código.

protocol = completed

for mnemonic, gadget in sorted(protocol.layers[0].gadgets.items()):
    result = ec.GadgetProfile(gadget).distance()
    witness = result.witness if result.upper_bound is not None else "none"
    print(f"{mnemonic}: distance={result}, witness={witness}")

O modelo de falha padrão inclui esses eventos após cada chamada de circuito:

  • Um Pauli erro nos qubits que a chamada toca
  • Um inverso de um bit de leitura que a chamada produz
  • Uma combinação correlacionada desses erros

Cada evento em uma chamada custa um, mesmo quando tem um erro de multiqubit correlacionado.

Um código pode ter distância dois, enquanto um de seus gadgets tem distância um. Por exemplo, uma única falha de circuito pode se espalhar por meio de uma ancilla e criar um erro lógico não detectado. Adicione verificações ou circuitos de sinalizador, crie um novo GadgetProfile e calcule a distância novamente.

Inspecionar uma falha específica

Use FaultEvent.after para definir uma falha após um índice de chamada de circuito baseado em zero.

gadget = protocol.layers[0].gadgets["syndrome"]
profile = ec.GadgetProfile(gadget)

fault = ec.FaultEvent.after(0, ec.Pauli("X_0"))
(effect,) = profile.effects_of([fault])

print("Fault:", fault)
print("Changed quantities:", effect)

Pode FaultEffect incluir verificações alteradas, leituras lógicas e sinais de quadro de saída. Use conjuntos de falhas explícitos quando o modelo de nível de circuito padrão não corresponder às suas suposições de hardware.

result = profile.distance(faults=[fault])
print(result)

As pesquisas exatas de gadget podem levar muito tempo porque o conjunto de falhas padrão cresce exponencialmente com o suporte de cada chamada. Use distance_bounds, um conjunto de falhas explícito ou um upper_bound quando você precisar limitar a pesquisa.

Salvar o protocolo

Audite a final qodec antes de salvá-la.

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

Salve o protocolo como um único YAML pacote e carregue-o novamente para verificar se ele corresponde ao original.

from pathlib import Path

output_directory = Path("qodec-output")
output_directory.mkdir(exist_ok=True)

saved_path = protocol.save(str(output_directory), single_file=True)
loaded = qodec.Qodec.load(saved_path)

assert loaded == protocol
print("Saved qodec:", saved_path)

Um pacote de arquivo único armazena o manifesto, definições de código, conjuntos de instruções, gadgets e fontes de circuito em um YAML arquivo, o que facilita o compartilhamento e o recarregamento.

Executar o protocolo

Depois que a qodec auditoria for aprovada e atender aos requisitos de distância, compile um programa lógico para a Representação Intermediária Quântica (QIR) e execute-o por meio do protocolo. Consulte Executar e decodificar programas lógicos.

Para obter um exemplo completo que adiciona circuitos de sinalizador e compara as taxas de erro lógicas, consulte o passo a passo do protocolo QEC no QDK repositório GitHub.