Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Use qodec y el módulo qdk.ec del Microsoft Quantum Development Kit (QDK) para definir un código de corrección de errores cuánticos (QEC) y convertirlo en un protocolo ejecutable. En este artículo, define un código de error, compila una línea base qodec, valida sus declaraciones, calcula la distancia del gadget y guarda el protocolo.
En los ejemplos de este artículo se usa el código $[[4,2,2]]$ C4, que almacena dos cúbits lógicos en cuatro cúbits físicos y detecta cualquier error físico único Pauli.
Prerequisites
Completa estas tareas antes de empezar:
- Instale QDK para la corrección de errores.
- Utilice un entorno con Python la versión 3.11 o posterior.
- Confirme que el qodecStim analizador está disponible. En LinuxArm64 o WindowsArm64, instale Stim desde el origen y, a continuación, reinicie Python o el Jupyter kernel antes de llamar a
build_qodec.
Defina el código
Cree un objeto qodec.Code con los estabilizadores de código y un representante lógico X y Z para cada cúbit 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"],
)
El paquete qodec usa representación dispersa para cadenas Pauli. Cada término tiene una Pauli letra y una etiqueta de cúbit físico. Por ejemplo, X_0 X_2 aplica X a cúbits físicos 0 y 2.
La definición de código es un objeto matemático estático. No incluye circuitos que preparen, conserven o midan los estados codificados.
Análisis del código
Para inspeccionar una instantánea de los operadores de código, cree un 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)
El perfil no cambia si posteriormente cambia el objeto original qodec.Code .
Calcular la distancia del código
Para encontrar el número mínimo de errores físicos permitidos que causan un efecto lógico notrivial con síndrome cero, llame a 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)
De forma predeterminada, la búsqueda asigna el coste uno a cada error de X, Y o Z de un solo cúbit. El devuelto Distance tiene límites certificados y un testigo para cualquier límite superior finito.
Cuando una búsqueda exacta completa cuesta demasiado, 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)
El solucionador predeterminado es HiGHS. Para usar otro solucionador, establezca solver="enumeration"o instale MWPF y establezca solver="mwpf".
Cree una línea base qodec
Un qodec describe cómo ejecutar instrucciones codificadas. Tiene conjuntos de instrucciones, códigos, capas, gadgets, circuitos, comprobaciones y ecuaciones de lectura.
Usa build_qodec para crear un protocolo de dos capas a partir del código C4.
protocol = ec.build_qodec(
c4,
strategy="bare-css/v1",
strict=False,
)
En este ejemplo se usa bare-css/v1 para permitir inspeccionar un protocolo simple sin cúbits de bandera.
Caution
La bare-css/v1 estrategia no promete circuitos tolerantes a errores. Calcule la distancia de cada gadget antes de usar el protocolo para experimentos con ruido.
El compilador intenta crear estas instrucciones lógicas:
prepare_z_allprepare_x_allsyndromemeasure_z_allmeasure_x_allh_allcx_all
El _all sufijo significa que una instrucción actúa en todos los cúbits lógicos de un bloque. Por ejemplo, cx_all se aplica CNOT entre los cúbits lógicos correspondientes en dos bloques.
No todos los códigos admiten todas las instrucciones candidatas. Con strict=True, que es el valor predeterminado, el builder genera un error cuando no puede generar y comprobar un candidato. Con strict=False, el compilador mantiene las instrucciones admitidas y registra omisiones en protocol.metadata["qdk.ec"]["build"]["omitted"].
Inspeccione la capa lógica y sus 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)
La segunda capa contiene el conjunto de instrucciones físicas Stim que usan los circuitos generados.
Inspeccionar un gadget
Una instrucción lógica declara un contrato. Su gadget proporciona el circuito que implementa el contrato.
Inspeccione el 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 la operación calculada desde el circuito con el objetivo de la instrucción 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))
GadgetProfile toma una instantánea de su entrada, por lo que no refleja los cambios posteriores. Después de editar un gadget, cree un nuevo perfil.
Auditar el protocolo
Usa audit para comprobar todo qodec.
report = ec.audit(protocol)
print(report)
Si la auditoría no encuentra errores ni advertencias, imprime:
audit: ok
La auditoría comprueba estas áreas:
- Código de álgebra
- Estructura de instrucciones y capas
- Llamadas de circuito y vinculaciones de parámetros
- Acciones del gadget
- Comprobaciones y ecuaciones de indicación
- Marcar con indicador las ecuaciones
- Relaciones entre marcos de entrada y salida
- Límites de referencia y completitud del protocolo
Para aplicar acciones a niveles de gravedad específicos, use las colecciones de informes.
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)
El report.ok valor significa que el informe no tiene errores. Las advertencias no cambian esa propiedad.
Deducir las ecuaciones que faltan
Úselo filled cuando un gadget preliminar o qodec omita comprobaciones o ecuaciones lógicas de lectura que la simulación exacta puede derivar.
En el ejemplo siguiente se crea una copia incompleta del 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))
Rellene las ecuaciones admitidas y, a continuación, audite el resultado.
completed = ec.filled(draft)
print(ec.audit(completed))
La filled función devuelve un nuevo gadget o qodec y no cambia su entrada. Deriva verificaciones, ecuaciones de lectura observables y relaciones entre marcos. Conserva las asociaciones de flags porque la simulación sin ruido no puede identificar qué errores debe detectar un flag.
Si el protocolo original ya tiene ecuaciones directas verificadas, filled las mantiene siempre que sea posible.
Analizar fallos de gadgets
Una auditoría sin errores demuestra la coherencia sin ruido. No demuestra tolerancia a fallos.
Llame a GadgetProfile.distance para encontrar el número mínimo de errores de circuito permitidos que provocan un error lógico mientras que todas las comprobaciones y marcas declaradas siguen siendo cero. El error combinado también debe dejar cada salida dentro de su espacio 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}")
El modelo de errores predeterminado incluye estos eventos después de cada invocación de circuito:
- Error Pauli en los cúbits que afecta la llamada
- Volteo de un bit de salida que genera la llamada
- Combinación correlacionada de esos errores
Cada evento en una invocación cuesta uno, incluso cuando tiene un error multiqubit correlacionado.
Un código puede tener distancia dos mientras que uno de sus gadgets tiene distancia uno. Por ejemplo, un error de circuito único puede propagarse a través de un ancilla y crear un error lógico no detectado. Agregue comprobaciones o circuitos marcados y, a continuación, cree un nuevo GadgetProfile y vuelva a calcular la distancia.
Inspeccione un error específico
Use FaultEvent.after para definir un fallo después de un índice de llamada de circuito basado en cero.
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)
Un FaultEffect puede incluir comprobaciones modificadas, lecturas lógicas y signos de marco de salida. Utilice conjuntos de errores explícitos cuando el modelo de nivel de circuito predeterminado no coincida con las suposiciones de hardware.
result = profile.distance(faults=[fault])
print(result)
Las búsquedas exactas de gadgets pueden tardar mucho tiempo porque el conjunto de errores predeterminado crece exponencialmente con el soporte de cada llamada. Use distance_bounds, un conjunto de errores explícito o un upper_bound cuando necesite limitar la búsqueda.
Guardar el protocolo
Audite la versión final qodec antes de guardarla.
report = ec.audit(protocol)
if report.errors or report.warnings:
raise ValueError(str(report))
Guarde el protocolo como un solo YAML paquete y vuelva a cargarlo para comprobar que coincide con el 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)
Un paquete de un solo archivo almacena el archivo de manifiesto, las definiciones de código, los conjuntos de instrucciones, los gadgets y las fuentes del circuito en un archivo YAML, lo que facilita compartirlo y volver a cargarlo.
Ejecutar el protocolo
Después de que qodec supere su auditoría y cumpla los requisitos de distancia, compile un programa lógico en Quantum Intermediate Representation (QIR) y ejecútelo a través del protocolo. Consulte Ejecutar y descodificar programas lógicos.
Para obtener un ejemplo completo que agrega circuitos flag y compara las tasas de error lógicas, consulte el tutorial del protocolo QEC en el QDK repositorio de GitHub.