Skip to main content
IBM Quantum Platform

Simule um modelo de Ising com a função TEM

O método Tensor-network Error Mitigation (TEM) da Algoritmiq é um algoritmo híbrido quântico-clássico projetado para realizar a mitigação de ruído inteiramente dentro da fase clássica de pós-processamento. Com o TEM, o usuário pode calcular os valores esperados dos observáveis, mitigando os inevitáveis erros induzidos por ruído que ocorrem no hardware quântico com maior precisão e eficiência de custo, tornando-o uma opção altamente atraente para pesquisadores quânticos e profissionais da indústria.

Este tutorial demonstra como o TEM pode obter resultados significativos para a dinâmica de um sistema quântico, que seriam inacessíveis sem a mitigação de erros e que exigiriam recursos quânticos substancialmente maiores se outros métodos de mitigação de erros, como PEC e ZNE, fossem utilizados.

Estimativa de uso: este notebook usa aproximadamente 10 minutos de QPU em dispositivos Heron r3. O tempo de execução pode depender substancialmente do dispositivo escolhido. As estimativas de uso por seção podem ser encontradas abaixo.


Execute experimentos de física de muitos corpos com mitigação de erros usando a função TEM

Este tutorial baseia-se na seguinte referência: L. E. Fischer et al., Nat. Fís. (2026). Esta referência discute uma simulação real em hardware quântico de até 91 qubits. Neste tutorial, recriamos uma simulação semelhante em um circuito de tamanho menor.

O modelo de Ising com kick corresponde ao modelo de Ising usual:

H^I=Jn=0N2Z^nZ^n+1+hn=0N1Z^n\hat{H}_{\text{I}} = J \sum_{n=0}^{N-2} \hat{Z}_n \hat{Z}_{n+1} + h \sum_{n=0}^{N-1} \hat{Z}_n

ao qual é aplicado um chute transversal:

H^K=bn=0N1X^n\hat{H}_{K} = b \sum_{n=0}^{N-1} \hat{X}_n

O objetivo é simular a dinâmica de um estado sob o Hamiltoniano de Ising transversal, cuja evolução temporal pode ser implementada por um operador unitário de Floquet U^KI=eiH^KeiH^I\hat{U}_{\text{KI}} = e^{-i \hat{H}_K} e^{-i \hat{H}_I} . O estado inicial a ser evoluído é aquele em que o primeiro qubit está no estado +|+\rangle, enquanto os outros estão emparelhados e definidos no estado de Bell (00+11)/2(|00\rangle + |11\rangle)/\sqrt{2}.

A quantidade que queremos observar é a função de correlação. O artigo de referência discute como essa quantidade pode ser reescrita como um operador de Pauli X^\hat{X} no qubit nthn^{th}. Após uma série de passos de tempo físico tt, calculamos o valor do operador de Pauli X^n=t\hat{X}_{n=t}. Dependendo dos parâmetros do sistema, o valor dessa observável é igual a um valor que pode ser calculado com exatidão ou apenas simulado por meio de métodos aproximados. Especificamente, para J=b=π/4|J|=|b|=\pi/4, ele é igual a [cos(2h)]t[\cos(2h)]^t, que é o valor que usaremos para comparar os resultados deste tutorial. Além disso, em um determinado intervalo de tempo tt, X^nt\langle\hat{X}_{n\neq t}\rangle é zero. Para obter detalhes sobre como obter esses valores e para comparação com resultados aproximados de simulações clássicas fora desses parâmetros, consulte L. E. Fischer et al., Nat. Fís. (2026).

O TEM funciona caracterizando primeiro o ruído para cada camada única de portas de dois qubits no circuito, bem como caracterizando o erro de leitura. Em seguida, o circuito é executado na máquina quântica. Por fim, a mitigação do erro da rede tensorial é realizada nos recursos clássicos em IBM Cloud® e o valor mitigado é retornado. Neste exemplo, o circuito tem duas camadas únicas a caracterizar.


Instalação

Como pré-requisito, certifique-se de que as dependências necessárias estejam instaladas.

%pip install numpy matplotlib qiskit qiskit-ibm-catalog qiskit-ibm-runtime pylatexenc qiskit_qasm3_import
import os
from matplotlib import pyplot as plt
import numpy as np

from qiskit.quantum_info import SparsePauliOp
from qiskit.qasm3 import load

from qiskit_ibm_catalog import QiskitFunctionsCatalog

Mitigação de erros com TEM

Apresentamos aqui um circuito que implementa o modelo Ising descrito acima. O circuito é preparado da seguinte forma. Primeiro, há uma fase de preparação do estado, na qual o primeiro qubit está no estado +|+\rangle, enquanto os outros estão em pares de Bell (00+11)/2(|00\rangle + |11\rangle)/\sqrt{2}. Em seguida, vem a estrutura de alvenaria que implementa a evolução unitária U^KI\hat{U}_{\text{KI}}. O número de etapas de tempo físico corresponde às camadas do circuito t/2t/2.

O código a seguir baixa os dois arquivos QASM necessários para este tutorial.

# Download required QASM files
import urllib

urllib.request.urlretrieve(
    "https://ibm.box.com/shared/static/swy5jtq309b0xpzluzlmsmj908yphes8.qasm",
    "ki_30q.qasm",
)
urllib.request.urlretrieve(
    "https://ibm.box.com/shared/static/et3gkodonw6gsp2trs43lzaozrdtiu7s.qasm",
    "ki_12q.qasm",
)

Podemos visualizar uma versão reduzida do circuito, com 12 qubits e seis etapas temporais:

# Parameters of the kicked Ising model
h = 0.0
num_qubits = 12
t_steps = 6

# Load the circuit for the kicked Ising model
small_circuit = load("ki_12q.qasm")

# Draw the circuit
small_circuit.draw("mpl", scale=0.25, fold=-1)

Output:

Output of the previous code cell

Em seguida, crie o observável, X^n=t\hat{X}_{n=t}. Ele é construído como uma simples cadeia de Pauli com a ordem correspondente à usada pelo Qiskit:

def xt_observable(n_qubits, t_steps):
    pauli_str = "".join(["I" * t_steps, "X", "I" * (n_qubits - t_steps - 1)])
    pauli_str = pauli_str[::-1]  # Reverse the string to match qiskit order
    return SparsePauliOp(data=pauli_str, coeffs=1.0)

Em nosso pequeno exemplo de 12 qubits, o observável se parece com isto:

# Build the observable for the kicked Ising model
small_observable = xt_observable(n_qubits=12, t_steps=6)
print(small_observable)

Output:

SparsePauliOp(['IIIIIXIIIIII'],
              coeffs=[1.+0.j])

Qiskit Functions Use PUBs como forma de coletar as contribuições. No nosso caso, vamos considerar um único circuito e observável como nosso PUB :

# Collect the input PUBs, in this case composed of a
# single circuit and observable
pubs = [(small_circuit, [small_observable])]

Em seguida, obtemos acesso à função TEM. Primeiro, configuramos a autenticação necessária para IBM Cloud e selecionamos um backend entre os dispositivos disponíveis. O token, os back-ends disponíveis e os nomes dos recursos de nuvem (CRN) correspondentes podem ser obtidos fazendo login em sua conta no painel IBM Quantum Platform.

# Set IBM Quantum credentials and backend configuration
personal_token = os.environ.get(
    "QISKIT_IBM_TOKEN", "<API-KEY>"
)  # Replace with your personal token or set the environment variable
channel = "ibm_quantum_platform"
crn = "your_crn"  # Replace with the Cloud Resource Name (CRN)

# Select the QPU backend
backend_name = "ibm_qpu_name"  # Replace with your desired backend's name

Carregue a função TEM do Qiskit Functions Catalog :

# Load the TEM function from the Qiskit Functions Catalog
catalog = QiskitFunctionsCatalog(
    channel=channel,
    token=personal_token,
    instance=crn,
)
tem = catalog.load("algorithmiq/tem")

Agora podemos realizar um experimento no circuito Ising com mitigação de erros fornecida pelo TEM. Usando as configurações padrão, o TEM pode ser executado de maneira simples, com um tempo de execução previsto do QPU de cerca de 2.5 minutos, dependendo do QPU:

tem_job = tem.run(pubs=pubs, backend_name=backend_name)

Com as opções padrão, a função TEM executa três tarefas no computador quântico: aprendizagem de ruído, mitigação de leitura e amostragem de circuito. O número de disparos usados por cada um deles pode ser alterado nas opções passadas para a função. Por padrão, esses parâmetros são definidos para alcançar uma precisão de 0.05 nos valores esperados mitigados.

Você pode verificar o status do seu trabalho no painel IBM Quantum Platform ou com:

print(tem_job.status())

Output:

QUEUED

Quando o status é DONE, podemos verificar os resultados brutos e mitigados. Os valores tem_evs definidos abaixo são os valores esperados dos observáveis solicitados, neste caso apenas um observável, X^n=t\langle \hat X_{n=t}\rangle, e tem_std são os desvios padrão correspondentes.

# Get the results of the TEM job
tem_results = tem_job.result()[
    0
]  # Get the first and only result from the job
tem_evs = tem_results.data.evs[0]
tem_std = tem_results.data.stds[0]

print(f"TEM Result: {tem_evs:.3f} ± {tem_std:.3f}")

Output:

TEM Result: 1.031 ± 0.046

Também podemos verificar quanto tempo de execução quântico foi usado para cada chamada em IBM Quantum Platform ou inspecionando os metadados do resultado do código Python.

# Get the TEM job runtime
tem_runtime = tem_job.result().metadata["resource_usage"][
    "RUNNING: EXECUTING_QPU"
]["QPU_TIME"]

print(f"TEM Runtime: {tem_runtime} seconds")

Output:

TEM Runtime: 155.0 seconds

Personalize os parâmetros TEM e as opções avançadas

A função TEM oferece várias opções avançadas para personalizar seu fluxo de trabalho de mitigação de erros. Essas opções permitem controlar a precisão, o número de disparos, as estratégias de aprendizagem de ruído e outros parâmetros para melhor atender aos requisitos do seu experimento e aos recursos quânticos disponíveis.

As opções avançadas comuns são:

  • **precision**Especifique a precisão alvo para os valores esperados mitigados.
  • **default_shots**Em vez de precision, você pode especificar o número de disparos usados pela tarefa de medição.
  • tem_max_bond_dimension: A dimensão máxima do vínculo utilizada na rede tensorial.
  • tem_compression_cutoff: O valor de corte a ser usado para a rede tensorial.
  • Opções de aprendizagem de ruído : configure como o ruído é caracterizado, como o número de repetições ou circuitos de calibração específicos.
  • **private**Certifique-se de que os circuitos e os resultados dos experimentos sejam privados para você e desative downloads múltiplos dos resultados dos trabalhos.

Consulte a documentação do TEM ou o Qiskit Functions Catalog para obter uma lista completa das opções suportadas e suas descrições. Você pode ajustar esses parâmetros para equilibrar o tempo de execução, o uso de recursos e a precisão dos resultados.

Você pode passar essas opções como um dicionário para o options argumento ao executar a função TEM:

options = {
    "default_shots": 10_000,
    "tem_max_bond_dimension": 512,
    "tem_compression_cutoff": 1e-16,
    # This option helps optimizing the measurement
    # stage since the observable is strongly biased
    # toward the X operator for all the qubits.
    "compute_shadows_bias_from_observable": True,
    # set to True to keep experiment results private,
    # recommended for confidential circuits
    "private": False,
}

Opções personalizadas para o aprendizado de ruído também podem ser passadas. Elas seguem as definições utilizadas no Dicionário de Terminologia do Governo do Reino Unido ( Qiskit RuntimeNoiseLearnerOptions):

nl_options = {
    "num_randomizations": 32,
    "max_layers_to_learn": 2,
    "shots_per_randomization": 128,
    "layer_pair_depths": [0, 1, 2, 4, 16, 32],
}

# add noise learning options to the overall options
options |= nl_options

Repita o experimento com essas opções personalizadas ajustadas ao nosso circuito. O tempo de execução previsto é de aproximadamente quatro minutos de QPU.

tem_job_custom = tem.run(
    pubs=pubs, backend_name=backend_name, options=options
)

Se a tarefa não estiver definida como privada, podemos recuperar o resultado posteriormente. Para isso, salve o ID da tarefa impresso aqui e use tem_job_custom = catalog.get_job_by_id("your-job-id").

job_id = tem_job_custom.job_id
print(f"Job ID: {job_id}")

Output:

Job ID: 1ba10094-a541-457a-9287-dbd49306d12d
results_custom = tem_job_custom.result()
tem_evs = results_custom[0].data.evs[0]
tem_std = results_custom[0].data.stds[0]

print(f"TEM Result: {tem_evs:.3f} ± {tem_std:.3f}")

Output:

TEM Result: 0.956 ± 0.018

Agora podemos inspecionar os resultados e os metadados para obter informações sobre o experimento:

metadata_custom = results_custom[0].metadata

unmitigated_evs = metadata_custom["evs_non_mitigated"][0]
unmitigated_stds = metadata_custom["stds_non_mitigated"][0]
print(f"Unmitigated Result: {unmitigated_evs:.3f} ± {unmitigated_stds:.3f}")

# Exact result for the kicked Ising model from the reference paper
exact_evs = np.cos(2 * h) ** t_steps
print("Exact Result:", exact_evs)

Output:

Unmitigated Result: 0.894 ± 0.015
Exact Result: 1.0
# Plot comparing the different expectation values
plt.bar(
    ["Unmitigated", "TEM"],
    [unmitigated_evs, tem_evs],
    yerr=[unmitigated_stds, tem_std],
    color=["grey", "c"],
)
plt.hlines(y=exact_evs, xmin=-0.5, xmax=1.5, colors="r", linestyles="dashed")
plt.ylabel("Expectation Value")
plt.ylim(0, 1.1)
plt.show()

Output:

Output of the previous code cell

Por fim, podemos verificar o impacto das opções personalizadas no QPU e no tempo de execução clássico:

# Get the metadata of the TEM job
job_metadata = results_custom.metadata

# Get the runtime of the TEM job
qpu_runtime = job_metadata["resource_usage"]["RUNNING: EXECUTING_QPU"][
    "QPU_TIME"
]
classical_runtime = (
    job_metadata["resource_usage"]["RUNNING: OPTIMIZING_FOR_HARDWARE"][
        "CPU_TIME"
    ]
    + job_metadata["resource_usage"]["RUNNING: POST_PROCESSING"]["CPU_TIME"]
)

print(f"QPU Runtime: {qpu_runtime} seconds")
print(f"Classical Runtime: {classical_runtime} seconds")

Output:

QPU Runtime: 342.0 seconds
Classical Runtime: 107.632604 seconds

Aumente o TEM para circuitos grandes

Os circuitos grandes podem, em princípio, ser executados com a função TEM. No entanto, é importante estar ciente das limitações dos recursos clássicos, uma vez que o TEM é executado em executores de IBM Cloud s com tempos de execução potencialmente muito longos. Para circuitos extremamente grandes, entre em contato com a equipe de suporte TEM em qiskit\ [email protected].

Aqui, executamos um exemplo com um circuito maior, do tamanho de uma escala utilitária, de 30 qubits, otimizando os parâmetros TEM para velocidade em vez de precisão.

# Kicked Ising model parameters
n_qubits = 30
t_steps = 15
h = 0.0

# Load the circuit for the kicked Ising model
circuit = load("ki_30q.qasm")


# Build the observable for the kicked Ising model
observable = xt_observable(n_qubits=n_qubits, t_steps=t_steps)

# Collect the input PUBs, in this case composed of a
# single circuit and observable
pubs = [(circuit, [observable])]

Vamos definir algumas opções orientadas para o desempenho:

options = {
    "num_randomizations": 32,
    "max_layers_to_learn": 2,
    "shots_per_randomization": 128,
    "layer_pair_depths": [0, 1, 2, 4, 16, 32, 64],
    "default_shots": 5_000,
    "tem_max_bond_dimension": 128,
    "tem_compression_cutoff": 1e-10,
    "compute_shadows_bias_from_observable": True,
    "private": False,
}

Por fim, execute o experimento, obtenha o resultado e visualize-o. Isso levará aproximadamente 3.5 minutos de QPU.

tem_job_large = tem.run(pubs=pubs, backend_name=backend_name, options=options)
job_id = tem_job_large.job_id
print(f"Job ID: {job_id}")

Output:

Job ID: 9f3f190f-f4b0-4dcb-bb83-5f71f37d0d77
results_large = tem_job_large.result()
tem_evs = results_large[0].data.evs[0]
tem_std = results_large[0].data.stds[0]

print(f"TEM Result: {tem_evs:.3f} ± {tem_std:.3f}")


# Get the metadata of the TEM job
job_metadata = tem_job_large.result().metadata

# Get the runtime of the TEM job
qpu_runtime = job_metadata["resource_usage"]["RUNNING: EXECUTING_QPU"][
    "QPU_TIME"
]
classical_runtime = (
    job_metadata["resource_usage"]["RUNNING: OPTIMIZING_FOR_HARDWARE"][
        "CPU_TIME"
    ]
    + job_metadata["resource_usage"]["RUNNING: POST_PROCESSING"]["CPU_TIME"]
)

print(f"QPU Runtime: {qpu_runtime} seconds")
print(f"Classical Runtime: {classical_runtime} seconds")

Output:

TEM Result: 0.794 ± 0.026
QPU Runtime: 203.0 seconds
Classical Runtime: 251.71805499999996 seconds
# Plot comparing the different expectation values
metadata_large = results_large[0].metadata
unmitigated_evs = metadata_large["evs_non_mitigated"][0]
unmitigated_stds = metadata_large["stds_non_mitigated"][0]

exact_evs = np.cos(2 * h) ** t_steps

plt.bar(
    ["Unmitigated", "TEM"],
    [unmitigated_evs, tem_evs],
    yerr=[unmitigated_stds, tem_std],
    color=["grey", "c"],
)
plt.hlines(y=exact_evs, xmin=-0.5, xmax=1.5, colors="r", linestyles="dashed")
plt.ylabel("Expectation Value")
plt.ylim(0, 1.1)
plt.show()

Output:

Output of the previous code cell
Esta página foi útil?
Relate um bug, erro de digitação ou solicite conteúdo no GitHub.