Portuguese, Brazilian
Idiomas
English
Japanese
German
Korean
Portuguese, Brazilian
French
Shortcuts

Nota

Esta página foi gerada, a partir do tutorials/noise/6_repetition_code.ipynb.

Execute interativamente no IBM Quantum lab.

Códigos De Repetição

Introdução

Na correção de erros quânticos pegamos muitos qubits ruidosos (que chamamos de qubits físicos) e os utilizamos para armazenar um número menor de qubits lógicos. O procedimento de correção de erros é projetado para detectar e corrigir os efeitos do ruído. Isto faz com que os qubits lógicos sejam muito menos ruidosos e mais confiáveis, do que os físicos, dos quais são construídos.

O código de repetição é um exemplo simples de correção de erro quântico, no qual um bit lógico é armazenado em vez de um qubit lógico. Uma dada instância do código de repetição é definida por dois números, os quais chamamos de \(d\) e \(T\).

O parâmetro \(d\) determina quantos qubits físicos são usados. As informações reais sobre o bit lógico são armazenadas em \(d\) qubits (que chamaremos de code qubits). A codificação é feita de uma forma muito simples: para codificar um 0, todos estes qubits são configurados no estado \(|0\rangle\), para codificar um 1 eles são configurados para \(|1\rangle\). Para ler o valor, você pode simplesmente olhar para qualquer qubit. Para lê-lo de uma maneira que o proteja contra erros em um único qubit, você pode ler todos os qubits e escolher o valor que mais aparece.

Não é somente na leitura que podemos extrair informações úteis, que nos ajudarão a corrigir erros. Também podemos extrair informações, enquanto o bit lógico fica ocioso, ou mesmo, enquanto ele está envolvido em uma computação.

Uma vez que este é um exercício de correção de erros quânticos, faremos isto de uma maneira que também funcionaria para os qubits lógicos. Especificamente, nosso método de extração de informações sobre erros não deve extrair nenhuma informação, a respeito das informações lógicas armazenadas. No caso quântico, isto é necessário, para que não perturbemos estados de superposição dos qubits armazenados.

Extrairemos informações ao longo de \(T\) rodadas de medições da síndrome. Para o código de repetição, estes baseiam-se no fato de que todos os qubits de código devem estar no mesmo estado (todos \(|0\rangle\) ou todos \(|1\rangle\)). Qualquer desvio disto é, portanto, um sinal de erro. Especificamente, imaginamos que nossos \(d\) qubits de código estão posicionados ao longo de uma linha. Em seguida, realizamos uma medição em cada par de qubits de código vizinhos. Isto nos dirá se eles são iguais ou diferentes, sem extrair nenhuma informação sobre quais são os seus valores.

The implementation of these measurements requires \(d-1\) additional qubits, which we will call link qubits for the repetition code. By performing two CNOTs between the pair of code qubits and a corresponding link qubit, the required information (and only the required information) is placed on the link qubit and can then be measured.

[1]:
from qiskit import *
cq = QuantumRegister(2,'code_qubit')
lq = QuantumRegister(1,'link_qubit')
qc = QuantumCircuit(cq,lq)
qc.cx(cq[0],lq[0])
qc.cx(cq[1],lq[0])
print(qc)

code_qubit_0: |0>──■───────
                   │
code_qubit_1: |0>──┼────■──
                 ┌─┴─┐┌─┴─┐
link_qubit_0: |0>┤ X ├┤ X ├
                 └───┘└───┘

Aqui, fornecemos ferramentas para a criação e o teste de códigos de repetição. A primeira coisa que precisamos fazer é importá-las.

[2]:
from qiskit.ignis.verification.topological_codes import RepetitionCode
from qiskit.ignis.verification.topological_codes import GraphDecoder
from qiskit.ignis.verification.topological_codes import lookuptable_decoding, postselection_decoding

Criando um código de repetição

A classe repetition_code cria um código para os valores indicados de \(d\) e \(T\).

[3]:
d = 3
T = 2
code = RepetitionCode(d,T)

Com isto, podemos inspecionar várias propriedades do código, como os nomes dos registradores de qubits utilizados para o código e para os qubits auxiliares.

[4]:
code.qubit_registers
[4]:
{'code_qubit', 'link_qubit'}

Estes registradores são também atributos do objeto repetition_code.

[5]:
code.code_qubit
[5]:
QuantumRegister(3, 'code_qubit')

Você também pode acessar os circuitos quânticos que implementam o código. Dois destes são produzidos: um para cada um dos dois valores de bits lógicos possíveis.

[6]:
for log in ['0','1']:
    print('\n========= logical',log,'=========\n')
    print( code.circuit[log] )

========= logical 0 =========

                      ┌───┐     ┌───┐     ┌─┐           ░ ┌───┐     ┌───┐     »
     link_qubit_0: |0>┤ X ├─────┤ X ├─────┤M├─|0>───────░─┤ X ├─────┤ X ├─────»
                      └─┬─┘┌───┐└─┬─┘┌───┐└╥┘ ┌─┐       ░ └─┬─┘┌───┐└─┬─┘┌───┐»
     link_qubit_1: |0>──┼──┤ X ├──┼──┤ X ├─╫──┤M├──|0>──░───┼──┤ X ├──┼──┤ X ├»
                        │  └─┬─┘  │  └─┬─┘ ║  └╥┘       ░   │  └─┬─┘  │  └─┬─┘»
     code_qubit_0: |0>──■────┼────┼────┼───╫───╫────────░───■────┼────┼────┼──»
                             │    │    │   ║   ║        ░        │    │    │  »
     code_qubit_1: |0>───────■────■────┼───╫───╫────────░────────■────■────┼──»
                                       │   ║   ║        ░                  │  »
     code_qubit_2: |0>─────────────────■───╫───╫────────░──────────────────■──»
                                           ║   ║        ░                     »
round_0_link_bit_0: 0 ═════════════════════╩═══╬══════════════════════════════»
                                               ║                              »
round_0_link_bit_1: 0 ═════════════════════════╩══════════════════════════════»
                                                                              »
round_1_link_bit_0: 0 ════════════════════════════════════════════════════════»
                                                                              »
round_1_link_bit_1: 0 ════════════════════════════════════════════════════════»
                                                                              »
        code_bit_0: 0 ════════════════════════════════════════════════════════»
                                                                              »
        code_bit_1: 0 ════════════════════════════════════════════════════════»
                                                                              »
        code_bit_2: 0 ════════════════════════════════════════════════════════»
                                                                              »
«                    ┌─┐           ░
«      link_qubit_0: ┤M├─|0>───────░──────────
«                    └╥┘ ┌─┐       ░
«      link_qubit_1: ─╫──┤M├──|0>──░──────────
«                     ║  └╥┘       ░ ┌─┐
«      code_qubit_0: ─╫───╫────────░─┤M├──────
«                     ║   ║        ░ └╥┘┌─┐
«      code_qubit_1: ─╫───╫────────░──╫─┤M├───
«                     ║   ║        ░  ║ └╥┘┌─┐
«      code_qubit_2: ─╫───╫────────░──╫──╫─┤M├
«                     ║   ║        ░  ║  ║ └╥┘
«round_0_link_bit_0: ═╬═══╬═══════════╬══╬══╬═
«                     ║   ║           ║  ║  ║
«round_0_link_bit_1: ═╬═══╬═══════════╬══╬══╬═
«                     ║   ║           ║  ║  ║
«round_1_link_bit_0: ═╩═══╬═══════════╬══╬══╬═
«                         ║           ║  ║  ║
«round_1_link_bit_1: ═════╩═══════════╬══╬══╬═
«                                     ║  ║  ║
«        code_bit_0: ═════════════════╩══╬══╬═
«                                        ║  ║
«        code_bit_1: ════════════════════╩══╬═
«                                           ║
«        code_bit_2: ═══════════════════════╩═
«

========= logical 1 =========

                            ░ ┌───┐     ┌───┐     ┌─┐           ░ ┌───┐     »
     link_qubit_0: |0>──────░─┤ X ├─────┤ X ├─────┤M├─|0>───────░─┤ X ├─────»
                            ░ └─┬─┘┌───┐└─┬─┘┌───┐└╥┘ ┌─┐       ░ └─┬─┘┌───┐»
     link_qubit_1: |0>──────░───┼──┤ X ├──┼──┤ X ├─╫──┤M├──|0>──░───┼──┤ X ├»
                      ┌───┐ ░   │  └─┬─┘  │  └─┬─┘ ║  └╥┘       ░   │  └─┬─┘»
     code_qubit_0: |0>┤ X ├─░───■────┼────┼────┼───╫───╫────────░───■────┼──»
                      ├───┤ ░        │    │    │   ║   ║        ░        │  »
     code_qubit_1: |0>┤ X ├─░────────■────■────┼───╫───╫────────░────────■──»
                      ├───┤ ░                  │   ║   ║        ░           »
     code_qubit_2: |0>┤ X ├─░──────────────────■───╫───╫────────░───────────»
                      └───┘ ░                      ║   ║        ░           »
round_0_link_bit_0: 0 ═════════════════════════════╩═══╬════════════════════»
                                                       ║                    »
round_0_link_bit_1: 0 ═════════════════════════════════╩════════════════════»
                                                                            »
round_1_link_bit_0: 0 ══════════════════════════════════════════════════════»
                                                                            »
round_1_link_bit_1: 0 ══════════════════════════════════════════════════════»
                                                                            »
        code_bit_0: 0 ══════════════════════════════════════════════════════»
                                                                            »
        code_bit_1: 0 ══════════════════════════════════════════════════════»
                                                                            »
        code_bit_2: 0 ══════════════════════════════════════════════════════»
                                                                            »
«                    ┌───┐     ┌─┐           ░
«      link_qubit_0: ┤ X ├─────┤M├─|0>───────░──────────
«                    └─┬─┘┌───┐└╥┘ ┌─┐       ░
«      link_qubit_1: ──┼──┤ X ├─╫──┤M├──|0>──░──────────
«                      │  └─┬─┘ ║  └╥┘       ░ ┌─┐
«      code_qubit_0: ──┼────┼───╫───╫────────░─┤M├──────
«                      │    │   ║   ║        ░ └╥┘┌─┐
«      code_qubit_1: ──■────┼───╫───╫────────░──╫─┤M├───
«                           │   ║   ║        ░  ║ └╥┘┌─┐
«      code_qubit_2: ───────■───╫───╫────────░──╫──╫─┤M├
«                               ║   ║        ░  ║  ║ └╥┘
«round_0_link_bit_0: ═══════════╬═══╬═══════════╬══╬══╬═
«                               ║   ║           ║  ║  ║
«round_0_link_bit_1: ═══════════╬═══╬═══════════╬══╬══╬═
«                               ║   ║           ║  ║  ║
«round_1_link_bit_0: ═══════════╩═══╬═══════════╬══╬══╬═
«                                   ║           ║  ║  ║
«round_1_link_bit_1: ═══════════════╩═══════════╬══╬══╬═
«                                               ║  ║  ║
«        code_bit_0: ═══════════════════════════╩══╬══╬═
«                                                  ║  ║
«        code_bit_1: ══════════════════════════════╩══╬═
«                                                     ║
«        code_bit_2: ═════════════════════════════════╩═
«

Gerando um código de repetição personalizado

Você também pode implementar rodadas de medição e portas X lógicas. Por exemplo, vamos configurar um código sem rodadas de medição da síndrome.

[7]:
empty_code = RepetitionCode(3,0)

Isto não faz nada, além de configurar dois circuitos para os dois estados codificados lógicos. Não há rodadas de medição da síndrome e nenhuma leitura final.

[8]:
def print_circuits(code):
    for log in ['0','1']:
        print('\n========= logical',log,'=========\n')
        print( code.circuit[log] )

print_circuits(empty_code)

========= logical 0 =========


link_qubit_0: |0>

link_qubit_1: |0>

code_qubit_0: |0>

code_qubit_1: |0>

code_qubit_2: |0>


========= logical 1 =========

                       ░
link_qubit_0: |0>──────░─
                       ░
link_qubit_1: |0>──────░─
                 ┌───┐ ░
code_qubit_0: |0>┤ X ├─░─
                 ├───┤ ░
code_qubit_1: |0>┤ X ├─░─
                 ├───┤ ░
code_qubit_2: |0>┤ X ├─░─
                 └───┘ ░

Podemos adicionar uma rodada usando o método syndrome_measurement().

[9]:
empty_code.syndrome_measurement()
print_circuits(empty_code)

========= logical 0 =========

                      ┌───┐     ┌───┐     ┌─┐           ░
     link_qubit_0: |0>┤ X ├─────┤ X ├─────┤M├─|0>───────░─
                      └─┬─┘┌───┐└─┬─┘┌───┐└╥┘ ┌─┐       ░
     link_qubit_1: |0>──┼──┤ X ├──┼──┤ X ├─╫──┤M├──|0>──░─
                        │  └─┬─┘  │  └─┬─┘ ║  └╥┘       ░
     code_qubit_0: |0>──■────┼────┼────┼───╫───╫────────░─
                             │    │    │   ║   ║        ░
     code_qubit_1: |0>───────■────■────┼───╫───╫────────░─
                                       │   ║   ║        ░
     code_qubit_2: |0>─────────────────■───╫───╫────────░─
                                           ║   ║        ░
round_0_link_bit_0: 0 ═════════════════════╩═══╬══════════
                                               ║
round_0_link_bit_1: 0 ═════════════════════════╩══════════


========= logical 1 =========

                            ░ ┌───┐     ┌───┐     ┌─┐           ░
     link_qubit_0: |0>──────░─┤ X ├─────┤ X ├─────┤M├─|0>───────░─
                            ░ └─┬─┘┌───┐└─┬─┘┌───┐└╥┘ ┌─┐       ░
     link_qubit_1: |0>──────░───┼──┤ X ├──┼──┤ X ├─╫──┤M├──|0>──░─
                      ┌───┐ ░   │  └─┬─┘  │  └─┬─┘ ║  └╥┘       ░
     code_qubit_0: |0>┤ X ├─░───■────┼────┼────┼───╫───╫────────░─
                      ├───┤ ░        │    │    │   ║   ║        ░
     code_qubit_1: |0>┤ X ├─░────────■────■────┼───╫───╫────────░─
                      ├───┤ ░                  │   ║   ║        ░
     code_qubit_2: |0>┤ X ├─░──────────────────■───╫───╫────────░─
                      └───┘ ░                      ║   ║        ░
round_0_link_bit_0: 0 ═════════════════════════════╩═══╬══════════
                                                       ║
round_0_link_bit_1: 0 ═════════════════════════════════╩══════════

Uma operação X lógica pode ser adicionada utilizando o método x().

[10]:
empty_code.x()
print_circuits(empty_code)

========= logical 0 =========

                      ┌───┐     ┌───┐     ┌─┐           ░       ░
     link_qubit_0: |0>┤ X ├─────┤ X ├─────┤M├─|0>───────░───────░─
                      └─┬─┘┌───┐└─┬─┘┌───┐└╥┘ ┌─┐       ░       ░
     link_qubit_1: |0>──┼──┤ X ├──┼──┤ X ├─╫──┤M├──|0>──░───────░─
                        │  └─┬─┘  │  └─┬─┘ ║  └╥┘       ░ ┌───┐ ░
     code_qubit_0: |0>──■────┼────┼────┼───╫───╫────────░─┤ X ├─░─
                             │    │    │   ║   ║        ░ ├───┤ ░
     code_qubit_1: |0>───────■────■────┼───╫───╫────────░─┤ X ├─░─
                                       │   ║   ║        ░ ├───┤ ░
     code_qubit_2: |0>─────────────────■───╫───╫────────░─┤ X ├─░─
                                           ║   ║        ░ └───┘ ░
round_0_link_bit_0: 0 ═════════════════════╩═══╬══════════════════
                                               ║
round_0_link_bit_1: 0 ═════════════════════════╩══════════════════


========= logical 1 =========

                            ░ ┌───┐     ┌───┐     ┌─┐           ░       ░
     link_qubit_0: |0>──────░─┤ X ├─────┤ X ├─────┤M├─|0>───────░───────░─
                            ░ └─┬─┘┌───┐└─┬─┘┌───┐└╥┘ ┌─┐       ░       ░
     link_qubit_1: |0>──────░───┼──┤ X ├──┼──┤ X ├─╫──┤M├──|0>──░───────░─
                      ┌───┐ ░   │  └─┬─┘  │  └─┬─┘ ║  └╥┘       ░ ┌───┐ ░
     code_qubit_0: |0>┤ X ├─░───■────┼────┼────┼───╫───╫────────░─┤ X ├─░─
                      ├───┤ ░        │    │    │   ║   ║        ░ ├───┤ ░
     code_qubit_1: |0>┤ X ├─░────────■────■────┼───╫───╫────────░─┤ X ├─░─
                      ├───┤ ░                  │   ║   ║        ░ ├───┤ ░
     code_qubit_2: |0>┤ X ├─░──────────────────■───╫───╫────────░─┤ X ├─░─
                      └───┘ ░                      ║   ║        ░ └───┘ ░
round_0_link_bit_0: 0 ═════════════════════════════╩═══╬══════════════════
                                                       ║
round_0_link_bit_1: 0 ═════════════════════════════════╩══════════════════

Este também possui um kwarg logs, que é uma lista dos circuitos para os quais X é aplicado. Ele é ['0','1'] por padrão.

A leitura final é, então, feita com o método readout().

[11]:
empty_code.readout()
print_circuits(empty_code)

========= logical 0 =========

                      ┌───┐     ┌───┐     ┌─┐           ░       ░
     link_qubit_0: |0>┤ X ├─────┤ X ├─────┤M├─|0>───────░───────░──────────
                      └─┬─┘┌───┐└─┬─┘┌───┐└╥┘ ┌─┐       ░       ░
     link_qubit_1: |0>──┼──┤ X ├──┼──┤ X ├─╫──┤M├──|0>──░───────░──────────
                        │  └─┬─┘  │  └─┬─┘ ║  └╥┘       ░ ┌───┐ ░ ┌─┐
     code_qubit_0: |0>──■────┼────┼────┼───╫───╫────────░─┤ X ├─░─┤M├──────
                             │    │    │   ║   ║        ░ ├───┤ ░ └╥┘┌─┐
     code_qubit_1: |0>───────■────■────┼───╫───╫────────░─┤ X ├─░──╫─┤M├───
                                       │   ║   ║        ░ ├───┤ ░  ║ └╥┘┌─┐
     code_qubit_2: |0>─────────────────■───╫───╫────────░─┤ X ├─░──╫──╫─┤M├
                                           ║   ║        ░ └───┘ ░  ║  ║ └╥┘
round_0_link_bit_0: 0 ═════════════════════╩═══╬═══════════════════╬══╬══╬═
                                               ║                   ║  ║  ║
round_0_link_bit_1: 0 ═════════════════════════╩═══════════════════╬══╬══╬═
                                                                   ║  ║  ║
        code_bit_0: 0 ═════════════════════════════════════════════╩══╬══╬═
                                                                      ║  ║
        code_bit_1: 0 ════════════════════════════════════════════════╩══╬═
                                                                         ║
        code_bit_2: 0 ═══════════════════════════════════════════════════╩═


========= logical 1 =========

                            ░ ┌───┐     ┌───┐     ┌─┐           ░       ░    »
     link_qubit_0: |0>──────░─┤ X ├─────┤ X ├─────┤M├─|0>───────░───────░────»
                            ░ └─┬─┘┌───┐└─┬─┘┌───┐└╥┘ ┌─┐       ░       ░    »
     link_qubit_1: |0>──────░───┼──┤ X ├──┼──┤ X ├─╫──┤M├──|0>──░───────░────»
                      ┌───┐ ░   │  └─┬─┘  │  └─┬─┘ ║  └╥┘       ░ ┌───┐ ░ ┌─┐»
     code_qubit_0: |0>┤ X ├─░───■────┼────┼────┼───╫───╫────────░─┤ X ├─░─┤M├»
                      ├───┤ ░        │    │    │   ║   ║        ░ ├───┤ ░ └╥┘»
     code_qubit_1: |0>┤ X ├─░────────■────■────┼───╫───╫────────░─┤ X ├─░──╫─»
                      ├───┤ ░                  │   ║   ║        ░ ├───┤ ░  ║ »
     code_qubit_2: |0>┤ X ├─░──────────────────■───╫───╫────────░─┤ X ├─░──╫─»
                      └───┘ ░                      ║   ║        ░ └───┘ ░  ║ »
round_0_link_bit_0: 0 ═════════════════════════════╩═══╬═══════════════════╬═»
                                                       ║                   ║ »
round_0_link_bit_1: 0 ═════════════════════════════════╩═══════════════════╬═»
                                                                           ║ »
        code_bit_0: 0 ═════════════════════════════════════════════════════╩═»
                                                                             »
        code_bit_1: 0 ═══════════════════════════════════════════════════════»
                                                                             »
        code_bit_2: 0 ═══════════════════════════════════════════════════════»
                                                                             »
«
«      link_qubit_0: ──────
«
«      link_qubit_1: ──────
«
«      code_qubit_0: ──────
«                    ┌─┐
«      code_qubit_1: ┤M├───
«                    └╥┘┌─┐
«      code_qubit_2: ─╫─┤M├
«                     ║ └╥┘
«round_0_link_bit_0: ═╬══╬═
«                     ║  ║
«round_0_link_bit_1: ═╬══╬═
«                     ║  ║
«        code_bit_0: ═╬══╬═
«                     ║  ║
«        code_bit_1: ═╩══╬═
«                        ║
«        code_bit_2: ════╩═
«

Executando um código de repetição

O objeto de código produz os circuitos necessários para o código. O usuário pode, então, executá-los usando qualquer método que preferir, permitindo o controle total sobre compilação, backends, modelos de ruído e, assim por diante.

Por exemplo, aqui, os executamos sem ruído no qasm_simulator.

[12]:
circuits = code.get_circuit_list()
job = execute( circuits, Aer.get_backend('qasm_simulator') )
raw_results = {}
for log in ['0','1']:
    raw_results[log] = job.result().get_counts(log)
    print('\n========= logical',log,'=========\n')
    print(raw_results[log])

========= logical 0 =========

{'000 00 00': 1024}

========= logical 1 =========

{'111 00 00': 1024}

Aqui, as strings da direita para a esquerda representam as saídas das rodadas de medição da síndrome, seguidas da medição final dos qubits de código.

Estes resultados precisam ser reescritos de uma forma diferente para serem decodificados. Para fazer isto, podemos utilizar o método process_results do objeto.

[13]:
code.process_results( raw_results )
[13]:
{'0': {'0 0  00 00 00': 1024}, '1': {'1 1  00 00 00': 1024}}

O resultado é um dicionário, cujas chaves são strings de bits que representam saídas do circuito, e os valores representam o número de execuções (shots) para os quais esta saída ocorreu.

As strings não são a saída direta dos circuitos. Elas foram processadas para assumir a forma que nos ajuda a corrigir erros. Aqui está uma curta visita guiada.

  • Os 0 0 mais à esquerda para o resultado 0 lógico, e o 1 1 mais à esquerda do 1 lógico são a leitura lógica. Qualquer qubit de código poderia ser usado para esta leitura, uma vez que eles devem (sem erros) ser todos iguais. Assim, poderíamos ter apenas um resultado, aqui, para um qubit de código arbitrariamente escolhido. Ou poderíamos ter \(d\), um para cada qubit. Em vez disto, temos dois dos dois qubits em qualquer extremidade da linha. Isto, porque funciona melhor com o decodificador (que usaremos depois). Na ausência de erros, esses dois valores serão sempre iguais.

  • Os 0000 seguintes são os \(d-1\) resultados das medições da síndrome para a primeira rodada. Um 0 implica que o par correspondente de qubits era igual, e 1 implica que era diferente. Existem \(d-1\) resultados porque a linha de \(d\) qubits de código tem \(d-1\) possíveis pares vizinhos. Na ausência de erros, todos serão 0.

  • O 0000 que segue, a isto, é a mudança da síndrome entre a primeira e a segunda rodadas. É, portanto, o OR bitwise dos resultados da medição da síndrome da segunda rodada com aqueles da primeira. Na ausência de erros, todos serão 0.

  • Os blocos subsequentes seguem a mesma fórmula, embora o último exija algum comentário. Este não é medido usando o método padrão (com um link qubit). Em vez disto, é calculado, a partir da medição da leitura final de todos os qubits de código. Novamente, ela é apresentada como uma mudança da síndrome, e serão todos 0 na ausência de erros. Este é o \(T+1\)-ésimo bloco de medições da síndrome, uma vez que, como não é feito da mesma forma que os outros, não é contado entre as \(T\) rodadas de medição da síndrome.

Exemplo 1: 0 0  0110 0000 0000 representaria um código de repetição com \(d=5\) e \(T=2\), tendo um 0 codificado. A síndrome mostra que (muito provavelmente) o qubit de código do meio foi invertido por conta de um erro, antes da primeira rodada de medição. Isto faz com que ele discorde de ambos os qubits de código vizinhos, durante o restante do circuito. Isto é mostrado pela síndrome na primeira rodada, mas os blocos para rodadas subsequentes não o reportam, já que ele não representa mais uma mudança. Outros conjuntos de erros, também poderiam ter causado esta síndrome, mas precisariam ser mais complexos e, por consequência, presumivelmente, menos prováveis.

Exemplo 2: 0 0  0010 0010 0000 representaria um código de repetição com \(d=5\) e \(T=2\), tendo um 0 codificado. Aqui, uma das medições da síndrome relatou uma diferença entre dois qubits de código na primeira rodada, levando a um 1. A rodada seguinte não viu o mesmo efeito e, assim, resultou em um 0. No entanto, uma vez que isto discorda do resultado anterior para a mesma medição da síndrome e, já que acompanhamos alterações da síndrome, esta alteração resulta em outro 1. As rodadas subsequentes também não detectam nada, mas isto não representa mais uma mudança e, portanto, resulta em um 0 na mesma posição. Muito, provavelmente, o resultado da medição relacionado ao primeiro 1 foi um erro.

Exemplo 3: 0 1  0000 0001 0000 representaria um código de repetição com \(d=5\) e \(T=2\), com um 1 codificado. Um qubit de código no final da linha é invertido, antes da segunda rodada de medições da síndrome. Isto é detectado por, apenas, uma única medição da síndrome, pois ele está no fim da linha. Pela mesma razão, isto, também, perturba uma das leituras lógicas.

Note que em todos estes exemplos, um único erro faz com que exatamente dois caracteres na string mudem do valor, que teriam sem erros. Isto é, na verdade, a razão pela qual a saída lógica consiste em ambos os pontos finais. É uma propriedade que será utilizada pelo decodificador.

Para ver os efeitos do ruído, precisamos especificar um modelo de ruído. Por exemplo, vamos configurar um modelo de ruído simples com erros de porta e de medição.

[14]:
from qiskit.providers.aer.noise import NoiseModel
from qiskit.providers.aer.noise.errors import pauli_error, depolarizing_error

def get_noise(p_meas,p_gate):

    error_meas = pauli_error([('X',p_meas), ('I', 1 - p_meas)])
    error_gate1 = depolarizing_error(p_gate, 1)
    error_gate2 = error_gate1.tensor(error_gate1)

    noise_model = NoiseModel()
    noise_model.add_all_qubit_quantum_error(error_meas, "measure")
    noise_model.add_all_qubit_quantum_error(error_gate1, ["u1", "u2", "u3"])
    noise_model.add_all_qubit_quantum_error(error_gate2, ["cx"])

    return noise_model
[15]:
noise_model = get_noise(0.04,0.04)

Isto pode, então, ser executado para gerar resultados ruidosos. Vamos criar uma função rápida para fazer isto e depois executá-la.

[16]:
def get_syndrome(code,noise_model,shots=1014):

    circuits = code.get_circuit_list()

    job = execute( circuits, Aer.get_backend('qasm_simulator'),noise_model=noise_model, shots=shots )
    raw_results = {}
    for log in ['0','1']:
        raw_results[log] = job.result().get_counts(log)

    return code.process_results( raw_results )
[17]:
get_syndrome(code,noise_model)
[17]:
{'0': {'1 1  10 10 11': 1,
  '0 0  00 01 01': 36,
  '0 0  10 11 10': 1,
  '0 1  00 10 11': 2,
  '0 1  01 10 10': 1,
  '0 1  00 00 10': 3,
  '0 0  00 00 11': 32,
  '0 0  00 11 11': 6,
  '0 1  01 00 00': 1,
  '0 0  01 00 10': 2,
  '0 1  00 11 10': 1,
  '0 0  11 11 00': 2,
  '0 0  00 01 10': 20,
  '1 0  01 01 10': 7,
  '0 0  01 00 01': 2,
  '0 1  10 10 01': 2,
  '1 0  00 00 01': 4,
  '1 1  00 01 10': 1,
  '0 0  10 10 00': 60,
  '0 1  00 00 01': 37,
  '1 0  00 11 10': 2,
  '0 0  00 00 00': 554,
  '0 0  10 10 11': 3,
  '1 0  01 10 10': 2,
  '0 1  00 01 00': 11,
  '1 0  10 01 01': 1,
  '0 0  11 00 00': 3,
  '1 0  10 10 10': 4,
  '1 0  00 00 10': 47,
  '0 1  00 11 01': 1,
  '1 1  01 10 00': 1,
  '0 0  01 01 00': 48,
  '1 0  00 10 00': 19,
  '0 1  10 11 00': 2,
  '0 0  00 11 00': 12,
  '0 1  01 11 11': 1,
  '0 1  00 01 11': 1,
  '1 0  00 01 11': 2,
  '0 0  00 10 10': 41,
  '1 1  00 00 11': 4,
  '0 0  10 11 01': 4,
  '0 0  01 11 10': 2,
  '1 0  01 01 01': 2,
  '0 0  00 10 01': 5,
  '0 0  01 10 00': 12,
  '0 0  01 01 11': 2,
  '0 0  10 00 10': 3,
  '1 0  11 11 10': 2,
  '0 1  01 01 01': 2},
 '1': {'0 1  10 10 10': 2,
  '1 1  00 10 10': 57,
  '0 1  10 01 01': 1,
  '1 1  11 11 11': 2,
  '1 1  11 00 00': 2,
  '1 0  00 11 01': 2,
  '1 1  00 10 01': 7,
  '1 1  11 11 00': 3,
  '1 1  01 01 11': 5,
  '1 1  10 10 11': 4,
  '1 1  01 11 10': 3,
  '1 1  00 11 11': 1,
  '1 1  01 01 00': 39,
  '0 1  00 10 11': 1,
  '0 1  01 10 10': 1,
  '0 1  00 00 10': 45,
  '1 0  00 01 00': 15,
  '0 0  00 00 11': 2,
  '1 1  10 10 00': 52,
  '1 1  10 11 10': 1,
  '1 1  00 01 01': 37,
  '1 1  01 00 01': 4,
  '1 1  01 10 11': 1,
  '0 1  00 11 10': 1,
  '1 0  01 00 00': 2,
  '1 1  00 11 00': 12,
  '1 1  10 11 01': 4,
  '0 0  00 01 10': 1,
  '1 1  11 01 10': 1,
  '1 0  00 00 01': 33,
  '1 1  00 01 10': 12,
  '1 1  11 10 01': 1,
  '0 1  00 00 01': 1,
  '1 0  00 11 10': 1,
  '1 1  01 00 10': 1,
  '0 1  00 01 00': 4,
  '1 1  00 00 00': 553,
  '1 0  10 10 10': 1,
  '1 0  00 00 10': 2,
  '0 1  00 11 01': 2,
  '1 1  01 10 00': 16,
  '0 1  01 01 10': 3,
  '0 1  00 10 00': 17,
  '1 0  00 10 00': 1,
  '1 0  10 11 00': 1,
  '0 1  00 01 11': 4,
  '1 1  01 11 01': 2,
  '1 1  00 00 11': 36,
  '1 0  01 01 01': 3,
  '1 1  10 00 10': 5,
  '1 0  00 10 11': 4,
  '1 0  10 10 01': 3}}

Aqui, os resultados sem ruído são os mais prováveis. O restante das amostras estão distribuídas entre outras possibilidades com síndromes não triviais.

Decodificando um código de repetição

Resultados com ruído podem alterar o valor lógico na leitura e, assim, afetar nossa capacidade de ler o qubit lógico. Isto pode ser mitigado olhando para os valores da síndrome. Estes podem dizer-nos se os valores lógicos são ou não mais propensos a ter mudado e, assim, permitem-nos corrigir os erros. O processo de análise da síndrome para corrigir os erros é chamado de ‘decodificação‘. Fazemos isto construindo um objeto decodificador para o nosso código.

[18]:
dec = GraphDecoder( RepetitionCode(4,2) )

Isto analisa o código ao ver como diferentes tipos de erro alteram a saída. Com estas informações, juntamente com um algoritmo de decodificação, podemos determinar qual o valor lógico mais provável de ter sido.

Por exemplo, vamos usar o algoritmo de ‘matching’ (correspondência) para decodificar, que é baseado na correspondência perfeita de peso mínimo. Ele recebe strings de saída específicas como entrada. Daremos para ele a simples string de exemplo '1 0  001 100 100', para um 1 lógico que tenha sofrido dois erros.

[19]:
dec.matching('1 0  001 100 100')
[19]:
'1 1'

A saída é o que a parte lógica deveria ter sido. Como se pode ver, o decodificador determinou corretamente que a leitura deveria ter sido de um 1 lógico.

Quando pegamos muitas amostras, podemos determinar a probabilidade com a qual o decodificador está incorreto. Isto deve diminuir, exponencialmente, à medida que o tamanho do código é aumentado, uma vez que as configurações de ruído que enganam o decodificador tornam-se menos prováveis. A probabilidade de um erro lógico é calculada utilizando o método logical_prob(). Isto executa a correspondência por padrão, mas outros algoritmos podem ser especificados pelo kwarg algorithm.

[20]:
for d in range(3,8):

    code = RepetitionCode(d,2)

    results = get_syndrome(code,noise_model=noise_model,shots=8192)

    dec = GraphDecoder(code)

    logical_prob_match = dec.get_logical_prob(results)
    logical_prob_lookup = lookuptable_decoding(results,results)
    logical_prob_post = postselection_decoding(results)

    for log in ['0','1']:
        print('d =',d,',log =',log)
        print('logical error probability for matching      =',logical_prob_match[log])
        print('logical error probability for lookup table  =',logical_prob_lookup[log])
        print('logical error probability for postselection =',logical_prob_post[log])
        print('')
    print('')
d = 3 ,log = 0
logical error probability for matching      = 0.0323486328125
logical error probability for lookup table  = 0.021484375
logical error probability for postselection = 0.0004528985507246377

d = 3 ,log = 1
logical error probability for matching      = 0.02880859375
logical error probability for lookup table  = 0.0169677734375
logical error probability for postselection = 0.0


d = 4 ,log = 0
logical error probability for matching      = 0.01806640625
logical error probability for lookup table  = 0.01171875
logical error probability for postselection = 0.0

d = 4 ,log = 1
logical error probability for matching      = 0.0206298828125
logical error probability for lookup table  = 0.0074462890625
logical error probability for postselection = 0.0


d = 5 ,log = 0
logical error probability for matching      = 0.0091552734375
logical error probability for lookup table  = 0.002197265625
logical error probability for postselection = 0.0

d = 5 ,log = 1
logical error probability for matching      = 0.0086669921875
logical error probability for lookup table  = 0.002197265625
logical error probability for postselection = 0.0


d = 6 ,log = 0
logical error probability for matching      = 0.005859375
logical error probability for lookup table  = 0.0
logical error probability for postselection = 0.0

d = 6 ,log = 1
logical error probability for matching      = 0.0050048828125
logical error probability for lookup table  = 0.0001220703125
logical error probability for postselection = 0.0


d = 7 ,log = 0
logical error probability for matching      = 0.0025634765625
logical error probability for lookup table  = 0.0
logical error probability for postselection = 0.0

d = 7 ,log = 1
logical error probability for matching      = 0.002197265625
logical error probability for lookup table  = 0.0
logical error probability for postselection = 0.0


[21]:
import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright

Version Information

Qiskit SoftwareVersion
Qiskit0.14.0
Terra0.11.0
Aer0.3.4
Ignis0.2.0
Aqua0.6.1
IBM Q Provider0.4.4
System information
Python3.7.5 (default, Oct 25 2019, 10:52:18) [Clang 4.0.1 (tags/RELEASE_401/final)]
OSDarwin
CPUs4
Memory (Gb)16.0
Tue Dec 10 17:06:25 2019 EST

This code is a part of Qiskit

© Copyright IBM 2017, 2019.

This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.

Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.

[ ]: