Pular para o conteúdo principal

Transição de fase de Nishimori

Estimativa de uso: 3 minutos em um processador Heron r2 (NOTA: Esta é apenas uma estimativa. Seu tempo de execução pode variar.)

Contexto

Este tutorial demonstra como realizar uma transição de fase de Nishimori em um processador quântico IBM®. Este experimento foi originalmente descrito em Realizing the Nishimori transition across the error threshold for constant-depth quantum circuits.

A transição de fase de Nishimori refere-se à transição entre fases ordenadas de curto e longo alcance no modelo de Ising com ligações aleatórias. Em um computador quântico, a fase ordenada de longo alcance se manifesta como um estado no qual os qubits estão emaranhados por todo o dispositivo. Este estado altamente emaranhado é preparado usando o protocolo de geração de emaranhamento por medição (GEM, do inglês generation of entanglement by measurement). Ao utilizar medições intermediárias, o protocolo GEM é capaz de emaranhar qubits por todo o dispositivo usando circuitos de apenas profundidade constante. Este tutorial usa a implementação do protocolo GEM do pacote de software GEM Suite.

Requisitos

Antes de começar este tutorial, certifique-se de ter o seguinte instalado:

  • Qiskit SDK v1.0 ou posterior, com suporte para visualização
  • Qiskit Runtime v0.22 ou posterior ( pip install qiskit-ibm-runtime )
  • GEM Suite ( pip install gem-suite )

Configuração

# Added by doQumentation — required packages for this notebook
!pip install -q gem-suite matplotlib qiskit qiskit-ibm-runtime
import matplotlib.pyplot as plt

from collections import defaultdict

from qiskit_ibm_runtime import QiskitRuntimeService

from qiskit.transpiler import generate_preset_pass_manager

from gem_suite import PlaquetteLattice
from gem_suite.experiments import GemExperiment

Passo 1: Mapear entradas clássicas para um problema quântico

O protocolo GEM funciona em um processador quântico com conectividade de qubits descrita por uma rede. Os processadores quânticos da IBM de hoje usam a rede heavy hex. Os qubits do processador são agrupados em plaquetas com base em qual célula unitária da rede eles ocupam. Como um qubit pode ocorrer em mais de uma célula unitária, as plaquetas não são disjuntas. Na rede heavy hex, uma plaqueta contém 12 qubits. As próprias plaquetas também formam uma rede, onde duas plaquetas estão conectadas se compartilham quaisquer qubits. Na rede heavy hex, plaquetas vizinhas compartilham 3 qubits.

No pacote de software GEM Suite, a classe fundamental para implementar o protocolo GEM é PlaquetteLattice, que representa a rede de plaquetas (que é distinta da rede heavy hex). Uma PlaquetteLattice pode ser inicializada a partir de um mapa de acoplamento de qubits. Atualmente, apenas mapas de acoplamento heavy hex são suportados.

A célula de código a seguir inicializa uma rede de plaquetas a partir do mapa de acoplamento de um processador quântico da IBM. A rede de plaquetas nem sempre engloba todo o hardware. Por exemplo, ibm_torino tem 133 qubits no total, mas a maior rede de plaquetas que cabe no dispositivo usa apenas 125 deles e compreende um total de 18 plaquetas. Algo similar pode ser observado para dispositivos IBM Quantum® com contagens de qubits diferentes também.

# QiskitRuntimeService.save_account(channel="ibm_quantum", token="<YOUR_API_KEYN>", overwrite=True, set_as_default=True)
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=127
)
plaquette_lattice = PlaquetteLattice.from_coupling_map(backend.coupling_map)

print(f"Number of qubits in backend: {backend.num_qubits}")
print(
f"Number of qubits in plaquette lattice: {len(list(plaquette_lattice.qubits()))}"
)
print(f"Number of plaquettes: {len(list(plaquette_lattice.plaquettes()))}")
Number of qubits in backend: 133
Number of qubits in plaquette lattice: 125
Number of plaquettes: 18

Você pode visualizar a rede de plaquetas gerando um diagrama de sua representação em grafo. No diagrama, as plaquetas são representadas por hexágonos rotulados, e duas plaquetas estão conectadas por uma aresta se compartilham qubits.

plaquette_lattice.draw_plaquettes()

Output of the previous code cell

Você pode recuperar informações sobre plaquetas individuais, como os qubits que elas contêm, usando o método plaquettes.

# Get a list of the plaquettes
plaquettes = list(plaquette_lattice.plaquettes())
# Display information about plaquette 0
plaquettes[0]
PyPlaquette(index=0, qubits=[0, 1, 2, 3, 4, 15, 16, 19, 20, 21, 22, 23], neighbors=[3, 1])

Você também pode produzir um diagrama dos qubits subjacentes que formam a rede de plaquetas.

plaquette_lattice.draw_qubits()

Output of the previous code cell

Além dos rótulos de qubits e das arestas indicando quais qubits estão conectados, o diagrama contém três informações adicionais que são relevantes para o protocolo GEM:

  • Cada qubit está sombreado (cinza) ou não sombreado. Os qubits sombreados são qubits de "sítio" que representam os sítios do modelo de Ising, e os qubits não sombreados são qubits de "ligação" usados para mediar interações entre os qubits de sítio.
  • Cada qubit de sítio é rotulado como (A) ou (B), indicando um de dois papéis que um qubit de sítio pode desempenhar no protocolo GEM (os papéis são explicados posteriormente).
  • Cada aresta é colorida usando uma de seis cores, particionando assim as arestas em seis grupos. Esse particionamento determina como portas de dois qubits podem ser paralelizadas, bem como diferentes padrões de escalonamento que provavelmente incorrerão em diferentes quantidades de erro em um processador quântico ruidoso. Como as arestas em um grupo são disjuntas, uma camada de portas de dois qubits pode ser aplicada nessas arestas simultaneamente. Na verdade, é possível particionar as seis cores em três grupos de duas cores de tal forma que a união de cada grupo de duas cores ainda seja disjunta. Portanto, apenas três camadas de portas de dois qubits são necessárias para ativar cada aresta. Existem 12 maneiras de particionar as seis cores dessa forma, e cada partição produz um cronograma de portas de 3 camadas diferente.

Agora que você criou uma rede de plaquetas, o próximo passo é inicializar um objeto GemExperiment, passando tanto a rede de plaquetas quanto o backend no qual você pretende executar o experimento. A classe GemExperiment gerencia a implementação real do protocolo GEM, incluindo a geração de circuitos, envio de trabalhos e análise dos dados. A célula de código a seguir inicializa a classe de experimento enquanto restringe a rede de plaquetas a apenas duas das plaquetas (21 qubits), reduzindo o tamanho do experimento para garantir que o ruído no hardware não sobrecarregue o sinal.

gem_exp = GemExperiment(plaquette_lattice.filter([9, 12]), backend=backend)

# visualize the plaquette lattice after filtering
plaquette_lattice.filter([9, 12]).draw_qubits()

Output of the previous code cell

Um circuito do protocolo GEM é construído usando os seguintes passos:

  1. Preparar o estado todo-+|+\rangle aplicando uma porta de Hadamard a cada qubit.
  2. Aplicar uma porta RZZR_{ZZ} entre cada par de qubits conectados. Isso pode ser alcançado usando 3 camadas de portas. Cada porta RZZR_{ZZ} age em um qubit de sítio e um qubit de ligação. Se o qubit de sítio for rotulado (B), então o ângulo é fixado em π2\frac{\pi}{2}. Se o qubit de sítio for rotulado (A), então o ângulo pode variar, produzindo circuitos diferentes. Por padrão, o intervalo de ângulos é definido como 21 pontos igualmente espaçados entre 00 e π2\frac{\pi}{2}, inclusive.
  3. Medir cada qubit de ligação na base de Pauli XX. Como os qubits são medidos na base de Pauli ZZ, isso pode ser realizado aplicando uma porta de Hadamard antes de medir o qubit.

Observe que o artigo citado na introdução deste tutorial usa uma convenção diferente para o ângulo RZZR_{ZZ}, que difere da convenção usada neste tutorial por um fator de 2.

No passo 3, apenas os qubits de ligação são medidos. Para entender em que estado os qubits de sítio permanecem, é instrutivo considerar o caso em que o ângulo RZZR_{ZZ} aplicado aos qubits de sítio (A) no passo 2 é igual a π2\frac{\pi}{2}. Neste caso, os qubits de sítio são deixados em um estado altamente emaranhado semelhante ao estado GHZ,

GHZ=0000+1111.\lvert \text{GHZ} \rangle = \lvert 00 \cdots 00 \rangle + \lvert 11 \cdots 11 \rangle.

Devido à aleatoriedade nos resultados de medição, o estado real dos qubits de sítio pode ser um estado diferente com ordem de longo alcance, por exemplo, 00110+11001\lvert 00110 \rangle + \lvert 11001 \rangle. No entanto, o estado GHZ pode ser recuperado aplicando uma operação de decodificação baseada nos resultados de medição. Quando o ângulo RZZR_{ZZ} é ajustado para baixo a partir de π2\frac{\pi}{2}, a ordem de longo alcance ainda pode ser recuperada até um ângulo crítico, que na ausência de ruído, é aproximadamente 0.3π0.3 \pi. Abaixo deste ângulo, o estado resultante não mais exibe emaranhamento de longo alcance. Esta transição entre a presença e ausência de ordem de longo alcance é a transição de fase de Nishimori.

Na descrição acima, os qubits de sítio foram deixados sem medir, e a operação de decodificação pode ser realizada aplicando portas quânticas. No experimento conforme implementado na suíte GEM, que este tutorial segue, os qubits de sítio são de fato medidos, e a operação de decodificação é aplicada em uma etapa de pós-processamento clássico.

Na descrição acima, a operação de decodificação pode ser realizada aplicando portas quânticas aos qubits de sítio para recuperar o estado quântico. No entanto, se o objetivo é medir imediatamente o estado, por exemplo, para fins de caracterização, então os qubits de sítio são medidos junto com os qubits de ligação, e a operação de decodificação pode ser aplicada em uma etapa de pós-processamento clássico. É assim que o experimento é implementado na suíte GEM, que este tutorial segue.

Além de depender do ângulo RZZR_{ZZ} no passo 2, que por padrão varre 21 valores, o circuito do protocolo GEM também depende do padrão de escalonamento usado para implementar as 3 camadas de portas RZZR_{ZZ}. Como discutido anteriormente, existem 12 desses padrões de escalonamento. Portanto, o número total de circuitos no experimento é 21×12=25221 \times 12 = 252.

Os circuitos do experimento podem ser gerados usando o método circuits da classe GemExperiment.

circuits = gem_exp.circuits()
print(f"Total number of circuits: {len(circuits)}")
Total number of circuits: 252

Para os propósitos deste tutorial, é suficiente considerar apenas um único padrão de escalonamento. A célula de código a seguir restringe o experimento ao primeiro padrão de escalonamento. Como resultado, o experimento tem apenas 21 circuitos, um para cada ângulo RZZR_{ZZ} varrido.

# Restrict experiment to the first scheduling pattern
gem_exp.set_experiment_options(schedule_idx=0)

# There are less circuits now
circuits = gem_exp.circuits()
print(f"Total number of circuits: {len(circuits)}")

# Print the RZZ angles swept over
print(f"RZZ angles:\n{gem_exp.parameters()}")
Total number of circuits: 21
RZZ angles:
[0. 0.07853982 0.15707963 0.23561945 0.31415927 0.39269908
0.4712389 0.54977871 0.62831853 0.70685835 0.78539816 0.86393798
0.9424778 1.02101761 1.09955743 1.17809725 1.25663706 1.33517688
1.41371669 1.49225651 1.57079633]

A célula de código a seguir desenha um diagrama do circuito no índice 5. Para reduzir o tamanho do diagrama, as portas de medição no final do circuito são removidas.

# Get the circuit at index 5
circuit = circuits[5]
# Remove the final measurements to ease visualization
circuit.remove_final_measurements()
# Draw the circuit
circuit.draw("mpl", fold=-1, scale=0.5)

Output of the previous code cell

Passo 2: Otimizar problema para execução em hardware quântico

Transpilar circuitos quânticos para execução em hardware normalmente envolve várias etapas. Normalmente, as etapas que incorrem em maior sobrecarga computacional são a escolha do layout de qubits, o roteamento das portas de dois qubits para se conformar à conectividade de qubits do hardware e a otimização do circuito para minimizar sua contagem de portas e profundidade. No protocolo GEM, as etapas de layout e roteamento são desnecessárias porque a conectividade do hardware já está incorporada no design do protocolo. Os circuitos já têm um layout de qubits, e as portas de dois qubits já estão mapeadas em conexões nativas. Além disso, para preservar a estrutura do circuito conforme o ângulo RZZR_{ZZ} é variado, apenas otimização de circuito muito básica deve ser realizada.

A classe GemExperiment transpila circuitos de forma transparente ao executar o experimento. As etapas de layout e roteamento já são substituídas por padrão para não fazer nada, e a otimização de circuito é realizada em um nível que otimiza apenas portas de qubit único. No entanto, você pode substituir ou passar opções adicionais usando o método set_transpile_options. Para fins de visualização, a célula de código a seguir transpila manualmente o circuito exibido anteriormente e desenha o circuito transpilado.

# Demonstrate setting transpile options
gem_exp.set_transpile_options(
optimization_level=1 # This is the default optimization level
)
pass_manager = generate_preset_pass_manager(
backend=backend,
initial_layout=list(gem_exp.physical_qubits),
**dict(gem_exp.transpile_options),
)
transpiled = pass_manager.run(circuit)
transpiled.draw("mpl", idle_wires=False, fold=-1, scale=0.5)

Output of the previous code cell

Passo 3: Executar usando primitivas Qiskit

Para executar os circuitos do protocolo GEM no hardware, chame o método run do objeto GemExperiment. Você pode especificar o número de disparos que deseja amostrar de cada circuito. O método run retorna um objeto ExperimentData que você deve salvar em uma variável. Observe que o método run apenas envia trabalhos sem esperar que eles terminem, então é uma chamada não bloqueante.

exp_data = gem_exp.run(shots=10_000)

Para aguardar os resultados, chame o método block_for_results do objeto ExperimentData. Esta chamada fará com que o interpretador aguarde até que os trabalhos sejam concluídos.

exp_data.block_for_results()
ExperimentData(GemExperiment, d0d5880a-34c1-4aab-a7b6-c4f58516bc03, job_ids=['cwg12ptmptp00082khhg'], metadata=<5 items>, figure_names=['two_point_correlation.svg', 'normalized_variance.svg', 'plaquette_ops.svg', 'bond_ops.svg'])

Passo 4: Pós-processar e retornar resultado em formato clássico desejado

Em um ângulo RZZR_{ZZ} de π2\frac{\pi}{2}, o estado decodificado seria o estado GHZ na ausência de ruído. A ordem de longo alcance do estado GHZ pode ser visualizada plotando a magnetização das cadeias de bits medidas. A magnetização MM é definida como a soma dos operadores de Pauli ZZ de qubit único,

M=j=1NZj,M = \sum_{j=1}^N Z_j,

onde NN é o número de qubits de sítio. Seu valor para uma cadeia de bits é igual à diferença entre o número de zeros e o número de uns. Medir o estado GHZ produz o estado todo zeros ou o estado todo uns com igual probabilidade, então a magnetização seria +N+N metade do tempo e N-N a outra metade do tempo. Na presença de erros devido ao ruído, outros valores também apareceriam, mas se o ruído não for muito grande, a distribuição ainda seria concentrada perto de +N+N e N-N.

Para as cadeias de bits brutas antes da decodificação, a distribuição da magnetização seria equivalente à de cadeias de bits uniformemente aleatórias, na ausência de ruído.

A célula de código a seguir plota a magnetização das cadeias de bits brutas e das cadeias de bits decodificadas no ângulo RZZR_{ZZ} de π2\frac{\pi}{2}.

def magnetization_distribution(
counts_dict: dict[str, int],
) -> dict[str, float]:
"""Compute magnetization distribution from counts dictionary."""
# Construct dictionary from magnetization to count
mag_dist = defaultdict(float)
for bitstring, count in counts_dict.items():
mag = bitstring.count("0") - bitstring.count("1")
mag_dist[mag] += count
# Normalize
shots = sum(counts_dict.values())
for mag in mag_dist:
mag_dist[mag] /= shots
return mag_dist

# Get counts dictionaries with and without decoding
data = exp_data.data()
# Get the last data point, which is at the angle for the GHZ state
raw_counts = data[-1]["counts"]
# Without decoding
site_indices = [
i for i, q in enumerate(gem_exp.plaquettes.qubits()) if q.role == "Site"
]
site_raw_counts = defaultdict(int)
for key, val in raw_counts.items():
site_str = "".join(key[-1 - i] for i in site_indices)
site_raw_counts[site_str] += val
# With decoding
_, site_decoded_counts = gem_exp.plaquettes.decode_outcomes(
raw_counts, return_counts=True
)

# Compute magnetization distribution
raw_magnetization = magnetization_distribution(site_raw_counts)
decoded_magnetization = magnetization_distribution(site_decoded_counts)

# Plot
plt.bar(*zip(*raw_magnetization.items()), label="raw")
plt.bar(*zip(*decoded_magnetization.items()), label="decoded", width=0.3)
plt.legend()
plt.xlabel("Magnetization")
plt.ylabel("Frequency")
plt.title("Magnetization distribution with and without decoding")
Text(0.5, 1.0, 'Magnetization distribution with and without decoding')

Output of the previous code cell

Para caracterizar de forma mais rigorosa a ordem de longo alcance, você pode examinar a correlação média de dois pontos ff, definida como

f=1N2(M2M2).f = \frac{1}{N^2} \left(\langle M^2 \rangle - \langle M \rangle ^2\right).

Um valor maior indica um grau maior de emaranhamento. A classe GemExperiment calcula automaticamente esse valor para as cadeias de bits decodificadas como parte do processamento dos dados experimentais. Ela armazena uma figura que é acessível via o método figure da classe de dados experimentais. Neste caso, o nome da figura é two_point_correlation.

exp_data.figure("two_point_correlation")

Output of the previous code cell

Para determinar o ponto crítico da transição de fase de Nishimori, você pode observar a variância normalizada de M2/NM^2 / N, definida como

g=1N3(M4M22),g = \frac{1}{N^3} \left(\langle M^4 \rangle - \langle M^2 \rangle^2\right),

que quantifica a quantidade de flutuação na magnetização ao quadrado. Esse valor é maximizado no ponto crítico da transição de fase de Nishimori. Na ausência de ruído, o ponto crítico ocorre em aproximadamente 0.3π0.3 \pi. Na presença de ruído, o ponto crítico é deslocado para cima, mas a transição de fase ainda é observada desde que o ponto crítico ocorra abaixo de 0.5π0.5 \pi.

exp_data.figure("normalized_variance")

Output of the previous code cell

Escalar o experimento

As células de código a seguir executam o experimento para seis plaquetas (49 qubits) e para as 12 plaquetas completas (125 qubits) e plotam a variância normalizada. À medida que o experimento é escalado para tamanhos maiores, a quantidade maior de ruído desloca o ponto crítico para a direita.

gem_exp = GemExperiment(
plaquette_lattice.filter(range(3, 9)), backend=backend
)
gem_exp.set_experiment_options(schedule_idx=0)
exp_data = gem_exp.run(shots=10_000)
exp_data.block_for_results()
exp_data.figure("normalized_variance")

Output of the previous code cell

gem_exp = GemExperiment(plaquette_lattice, backend=backend)
gem_exp.set_experiment_options(schedule_idx=0)
exp_data = gem_exp.run(shots=10_000)
exp_data.block_for_results()
exp_data.figure("normalized_variance")

Output of the previous code cell

Conclusão

Neste tutorial, você realizou uma transição de fase de Nishimori em um processador quântico usando o protocolo GEM. As métricas que você examinou durante o pós-processamento, em particular a correlação de dois pontos e a variância normalizada, servem como benchmarks da capacidade do dispositivo de gerar estados emaranhados de longo alcance. Esses benchmarks estendem a utilidade do protocolo GEM além da sondagem de física interessante. Como parte do protocolo, você emaranhado qubits por todo o dispositivo usando circuitos de apenas profundidade constante. Esse feito só é possível devido ao uso de medições intermediárias pelo protocolo. Neste experimento, o estado emaranhado foi imediatamente medido, mas uma via interessante a explorar seria continuar usando o estado em processamento quântico adicional!

Pesquisa sobre o tutorial

Por favor, responda a esta breve pesquisa para fornecer feedback sobre este tutorial. Seus insights nos ajudarão a melhorar nossas ofertas de conteúdo e experiência do usuário.

Link to survey