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.)
Resultados de aprendizado
Após concluir este tutorial, os usuários devem esperar os seguintes resultados:
- Compreender a transição de fase de Nishimori e como ela se manifesta como o surgimento de emaranhamento de longo alcance no modelo de Ising com ligações aleatórias.
- Implementar o protocolo de geração de emaranhamento por medição (GEM, do inglês generation of entanglement by measurement) em hardware quântico usando medições intermediárias e circuitos de profundidade constante.
- Caracterizar a transição extraindo a correlação de dois pontos e a variância normalizada da magnetização a partir de dados experimentais.
Pré-requisitos
Recomendamos familiaridade com os seguintes tópicos antes de prosseguir com este tutorial:
- O guia Medir qubits, em particular a seção sobre medições intermediárias das quais o protocolo GEM depende.
- Simulação exata e ruidosa com as primitivas do Qiskit Aer, que é como a seção de pequena escala é executada.
- Emaranhamento de longo alcance com circuitos dinâmicos, um tutorial complementar que usa o mesmo paradigma de emaranhamento baseado em medição.
- Rede heavy-hex, a topologia de hardware IBM® sobre a qual a rede de plaquetas é construída.
Contexto
Este tutorial demonstra como realizar uma transição de fase de Nishimori em um processador quântico. 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). 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) - Qiskit Aer v0.14 ou posterior (
pip install qiskit-aer) - GEM Suite (
pip install gem-suite)
Configuração
# Added by doQumentation — required packages for this notebook
!pip install -q gem-suite matplotlib qiskit qiskit-aer qiskit-ibm-runtime
import matplotlib.pyplot as plt
import warnings
from collections import defaultdict
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_aer import AerSimulator
from qiskit.transpiler import generate_preset_pass_manager
from gem_suite import PlaquetteLattice
from gem_suite.experiments import GemExperiment
Exemplo em simulador de pequena escala
Nesta seção, o fluxo de trabalho completo é percorrido no AerSimulator sem ruído. A rede de plaquetas é restrita a uma única plaqueta (12 qubits), para que a simulação permaneça pequena e rápida, mas ainda exercite cada parte do protocolo GEM: medição intermediária, varredura do ângulo , decodificação e a análise de variância normalizada. O mesmo fluxo de trabalho é escalado posteriormente para múltiplas plaquetas e a rede completa em hardware real.
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 IBM Quantum® atuais 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 redes, onde duas plaquetas estão conectadas se compartilham quaisquer qubits. Na rede heavy-hex, plaquetas vizinhas compartilham três 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 uma unidade de processamento quântico (QPU). 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, compreendendo 18 plaquetas; ibm_pittsburgh (156 qubits) similarmente encaixa 144 qubits em 21 plaquetas. O mesmo padrão se aplica a outros QPUs heavy-hex com diferentes contagens de qubits.
# QiskitRuntimeService.save_account(channel="ibm_quantum", token="<YOUR_API_KEY>", overwrite=True,
# set_as_default=True)
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=127
)
aer_backend = AerSimulator.from_backend(backend)
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()))}")
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()

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=[3, 4, 5, 6, 7, 16, 17, 23, 24, 25, 26, 27], neighbors=[4, 3, 1])
Você também pode produzir um diagrama dos qubits subjacentes que formam a rede de plaquetas.
plaquette_lattice.draw_qubits()

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 uma única plaqueta (12 qubits), mantendo a simulação pequena e rápida. A rede de plaquetas completa é usada posteriormente ao escalar para hardware real.
# Filter the plaquette lattice down to a single plaquette (12 qubits)
# so the AerSimulator run stays fast. The full lattice is used later
# in the large-scale hardware example.
gem_exp = GemExperiment(plaquette_lattice.filter([9]), backend=aer_backend)
# visualize the plaquette lattice after filtering
plaquette_lattice.filter([9]).draw_qubits()
Um circuito do protocolo GEM é construído usando os seguintes passos:
- Preparar o estado todo- aplicando uma porta de Hadamard a cada qubit.
- Aplicar uma porta entre cada par de qubits conectados. Isso pode ser feito usando três camadas de portas. Cada porta 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 . 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 e , inclusive.
- Medir cada qubit de ligação na base de Pauli . Como os qubits são medidos na base de Pauli , isso pode ser feito 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 , que difere da convenção usada neste tutorial por um fator de 2.
No passo três, 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 aplicado aos qubits de sítio (A) no passo dois é igual a . Neste caso, os qubits de sítio são deixados em um estado altamente emaranhado semelhante ao estado GHZ,
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, . No entanto, o estado GHZ pode ser recuperado aplicando uma operação de decodificação baseada nos resultados de medição. Quando o ângulo é reduzido a partir de , a ordem de longo alcance ainda pode ser recuperada até um ângulo crítico, que na ausência de ruído é aproximadamente . Abaixo deste ângulo, o estado resultante não mais exibe emaranhamento de longo alcance. Esta transição entre a presença e a 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, 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 você pode medir os qubits de sítio junto com os qubits de ligação, e aplicar a operação de decodificação em uma etapa de pós-processamento clássico.
Além de depender do ângulo no passo dois, que por padrão varre 21 valores, o circuito do protocolo GEM também depende do padrão de escalonamento usado para implementar as três camadas de portas . Como discutido anteriormente, existem 12 desses padrões de escalonamento. Portanto, o número total de circuitos no experimento é .
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 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)
Passo 2: Otimizar problema para execução em hardware quântico
Transpilar circuitos quânticos para execução em hardware geralmente 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 adequar à 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 é 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=aer_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)
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, portanto é 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.
# The noiseless AerSimulator produces zero-variance UFloat objects in the
# analysis, which triggers a harmless warning from the `uncertainties`
# library. Suppress it so the output stays clean.
with warnings.catch_warnings():
warnings.filterwarnings(
"ignore", message="Using UFloat objects with std_dev==0"
)
exp_data.block_for_results()
exp_data
ExperimentData(GemExperiment, 90bf2a90-f729-4c4e-a6da-664aecb11039, job_ids=['04a7c405-47fd-46ca-aa4b-aaf7e339cfbe'], 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 de , 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 é definida como a soma dos operadores de Pauli de qubit único,
onde é 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, portanto a magnetização seria metade do tempo e 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 estaria concentrada próximo a e .
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 de .
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')
Para caracterizar de forma mais rigorosa a ordem de longo alcance, você pode examinar a correlação média de dois pontos , definida como
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")
Para determinar o ponto crítico da transição de fase de Nishimori, você pode observar a variância normalizada de , definida como
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 . 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 .
exp_data.figure("normalized_variance")
Exemplo de hardware em grande escala
Após validar o protocolo em um simulador, você pode agora escalar o experimento e executá-lo no backend de hardware quântico real selecionado na seção Configuração. Este exemplo usa dois tamanhos de problema maiores:
- Seis plaquetas (~49 qubits): uma execução de tamanho intermediário que já mostra o deslocamento para a direita do ponto crítico sob ruído de hardware.
- A rede de plaquetas completa: cada plaqueta suportada pela topologia heavy-hex do dispositivo (por exemplo, 18 plaquetas / 125 qubits em
ibm_torinoou 21 plaquetas / 144 qubits emibm_pittsburgh), emaranhando qubits por todo o dispositivo com circuitos de profundidade constante.
A única célula de código abaixo é autossuficiente: ela constrói a rede de plaquetas a partir do mapa de acoplamento do backend e executa ambos os experimentos, de forma que esta seção pode ser executada após as células de Configuração sem precisar executar primeiro a seção de pequena escala.
# -------------------------Step 1-------------------------
# Initialize the runtime service, pick a real quantum hardware backend,
# and build the plaquette lattice from its coupling map. This is repeated
# from the small-scale example so this cell can run standalone after the
# Setup section. The full plaquette lattice is the "large-scale" target;
# a six-plaquette subset (range(3, 9)) is also used to show an intermediate
# scaling step.
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=127
)
plaquette_lattice = PlaquetteLattice.from_coupling_map(backend.coupling_map)
# Build a GemExperiment for the full plaquette lattice and one for the
# six-plaquette subset, each restricted to a single scheduling pattern so
# the experiment has one circuit per RZZ angle (21 circuits total).
gem_exp_full = GemExperiment(plaquette_lattice, backend=backend)
gem_exp_full.set_experiment_options(schedule_idx=0)
gem_exp_6 = GemExperiment(
plaquette_lattice.filter(range(3, 9)), backend=backend
)
gem_exp_6.set_experiment_options(schedule_idx=0)
circuits = gem_exp_full.circuits()
print(f"Total number of circuits (full lattice): {len(circuits)}")
# -------------------------Step 2-------------------------
# GemExperiment transpiles internally for the target backend: the layout
# and routing stages are overridden because the plaquette lattice already
# matches the hardware connectivity, and optimization is restricted so the
# RZZ angle structure is preserved. The code below manually transpiles one
# circuit from the six-plaquette experiment with the same settings this
# experiment will use, and draws it for inspection. (The full-lattice
# transpiled circuit has too many qubits to visualize cleanly, so the
# six-plaquette circuit is used here as a representative example.)
gem_exp_6.set_transpile_options(optimization_level=1)
circuits_6 = gem_exp_6.circuits()
pass_manager = generate_preset_pass_manager(
backend=backend,
initial_layout=list(gem_exp_6.physical_qubits),
**dict(gem_exp_6.transpile_options),
)
transpiled = pass_manager.run(circuits_6[5])
display(transpiled.draw("mpl", idle_wires=False, fold=-1, scale=0.5))
# -------------------------Step 3-------------------------
# Run both problem sizes on real hardware:
# 1. Six plaquettes (~49 qubits) — an intermediate scale-up.
# 2. The full plaquette lattice — every plaquette the device supports.
exp_data_6 = gem_exp_6.run(shots=10_000, job_tags=["TUT_NPT"])
exp_data_full = gem_exp_full.run(shots=10_000, job_tags=["TUT_NPT"])
exp_data_6.block_for_results()
exp_data_full.block_for_results()
# -------------------------Step 4-------------------------
# Plot the normalized variance at each scale. The peak marks the critical
# point of the Nishimori transition; as the system grows, hardware noise
# shifts the peak rightward.
display(exp_data_6.figure("normalized_variance"))
exp_data_full.figure("normalized_variance")
Total number of circuits (full lattice): 21

Observe que, dependendo do nível de ruído do backend usado, as curvas de variância normalizada nos tamanhos maiores podem não mostrar um pico claro dentro do intervalo de ângulos varrido. Nas execuções acima, o pico foi empurrado até , a borda direita da varredura (a análise relata critical_angle = 0.5000 tanto para as execuções de seis plaquetas quanto de rede completa). Isso significa que o ruído do hardware deslocou o ponto crítico para (ou apenas além de) o limite da faixa de ângulos fisicamente significativa do protocolo, de forma que a transição está na borda do que esta varredura pode resolver.
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ê emaranhou 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 considere explorar mais usando esse estado em processamento quântico adicional.
Próximos passos
Se você achou este trabalho interessante, pode se interessar pelo seguinte material:
Referências
[1] E. H. Chen, G.-Y. Zhu, R. Verresen, A. Seif, E. Bäumer, D. Layden, N. Tantivasadakarn, G. Zhu, S. Sheldon, A. Vishwanath, S. Trebst, A. Kandala. Realizing the Nishimori transition across the error threshold for constant-depth quantum circuits. arXiv:2309.02863 (2023).
[2] GEM Suite software package.