Skip to main content
IBM Quantum Platform

QESEM: Uma função Qiskit da Qedma

Consulte a referência da API

  • O código desta página foi desenvolvido com base nos seguintes requisitos. Recomendamos o uso dessas versões ou versões mais recentes.

    qiskit[all]~=2.5.0
    qiskit-ibm-runtime~=0.47.0
    
Nota

As funções do Qiskit são um recurso experimental disponível apenas para usuários dos planos IBM Quantum® Premium Plan, Flex Plan e On-Prem (via IBM Quantum Platform API). Eles estão no status de versão prévia e estão sujeitos a alterações.


Visão geral

Embora as unidades de processamento quântico tenham melhorado significativamente nos últimos anos, os erros causados por ruído e imperfeições no hardware existente continuam sendo um grande desafio para os desenvolvedores de algoritmos quânticos. À medida que o campo se aproxima de cálculos quânticos em escala utilitária que não podem ser verificados classicamente, soluções para cancelar ruídos com precisão garantida estão se tornando cada vez mais importantes. Para superar esse desafio, a Qedma desenvolveu o Quantum Error Mitigation (QESEM), perfeitamente integrado ao IBM Quantum Platform como uma função Qiskit.

Com o QESEM, os usuários podem executar seus circuitos quânticos em QPUs sujeitas a ruído para obter resultados altamente precisos e livres de erros, com sobrecargas de tempo de QPU altamente eficientes, próximas aos limites fundamentais. Para isso, o QESEM utiliza um conjunto de métodos proprietários desenvolvidos pela Qedma, destinados à caracterização e à redução de erros. As técnicas de redução de erros incluem otimização de portas, transpilagem sensível ao ruído, supressão de erros (ES) e mitigação imparcial de erros (EM). Com essa combinação de métodos baseados em caracterização, os usuários podem obter resultados confiáveis e sem erros para circuitos quânticos genéricos de grande volume, possibilitando aplicações que não poderiam ser realizadas de outra forma.

Para uma descrição completa dos componentes subjacentes, bem como uma demonstração em escala utilitária, consulte o artigo Mitigação de erros confiável e de alta precisão para circuitos quânticos em escala utilitária.


Descrição

Você pode usar a função QESEM da Qedma para estimar e executar facilmente seus circuitos com supressão e atenuação de erros, obtendo maiores volumes de circuitos e maior precisão. Para usar o QESEM, você fornece um circuito quântico, um conjunto de observáveis a serem medidos, uma precisão estatística alvo para cada observável e uma QPU escolhida. Antes de executar o circuito com a precisão desejada, você pode estimar o tempo de QPU necessário com base em um cálculo analítico que não requer a execução do circuito. Quando estiver satisfeito com a estimativa de tempo da QPU, você poderá executar o circuito com o QESEM.

Quando você executa um circuito, o QESEM executa um protocolo de caracterização de dispositivo adaptado ao seu circuito, produzindo um modelo de ruído confiável para os erros que ocorrem no circuito. Com base na caracterização, o QESEM primeiro implementa a transpilagem sensível ao ruído para mapear o circuito de entrada em um conjunto de qubits e portas físicos, o que minimiza o ruído que afeta o observável alvo. Isso inclui os portões disponíveis nativamente (CX/CZ em dispositivos IBM® ), bem como portões adicionais otimizados pela QESEM, formando o conjunto de portões estendido da QESEM. O QESEM então executa um conjunto de circuitos ES e EM baseados em caracterização na QPU e coleta os resultados das medições. Em seguida, eles são processados posteriormente de maneira clássica para fornecer um valor esperado imparcial e uma barra de erro para cada observável, correspondendo à precisão solicitada.

Visão geral do Qedma QESEM

Foi demonstrado que o QESEM fornece resultados de alta precisão para uma variedade de aplicações quânticas e nos maiores volumes de circuitos que podem ser obtidos atualmente. O QESEM oferece os seguintes recursos voltados para o usuário, demonstrados na seção de benchmarks abaixo:

  • Precisão garantida: o QESEM produz estimativas imparciais para valores esperados de observáveis. Seu método EM é equipado com garantias teóricas que, juntamente com a caracterização de ponta da Qedma, asseguram que a mitigação converge para a saída do circuito sem ruído até a precisão especificada pelo usuário. Em contraste com muitos métodos EM heurísticos que são propensos a erros sistemáticos ou vieses, a precisão garantida do QESEM é essencial para garantir resultados confiáveis em circuitos quânticos genéricos e observáveis.
  • Escalabilidade para QPUs grandes: o tempo de QPU do QESEM depende dos volumes dos circuitos, mas é independente do número de qubits. A Qedma demonstrou o QESEM nos maiores dispositivos quânticos disponíveis atualmente, incluindo os dispositivos Eagle de 127 qubits e Heron de 133 qubits da IBM Quantum.
  • Independente do aplicativo: O QESEM foi demonstrado em uma variedade de aplicações, incluindo simulação Hamiltoniana, VQE, QAOA e estimativa de amplitude. Os usuários podem inserir qualquer circuito quântico e observável a ser medido e obter resultados precisos e sem erros. As únicas limitações são ditadas pelas especificações de hardware e pelo tempo alocado da QPU, que determinam os volumes de circuito acessíveis e as precisões de saída. Por outro lado, muitas soluções de redução de erros são específicas do aplicativo ou envolvem heurísticas não controladas, o que as torna inaplicáveis a circuitos e aplicativos quânticos genéricos.
  • Conjunto de portas estendido: o QESEM suporta portas de ângulo fracionário e fornece portas de ângulo fracionário otimizadas para Qedma ( Rzz(θ)Rzz(\theta) ) nos dispositivos IBM Quantum Heron e Eagle. Este conjunto de portas estendido permite uma compilação mais eficiente e desbloqueia volumes de circuito maiores em até 2 vezes em comparação com a compilação CX/CZ padrão.
  • Observáveis multibase: O QESEM é compatível com observáveis de entrada compostos por muitas cadeias de Pauli não comutáveis, como Hamiltonianos genéricos. A escolha das bases de medição e a otimização da alocação de recursos da QPU (disparos e circuitos) são então executadas automaticamente pelo QESEM para minimizar o tempo necessário da QPU para a precisão solicitada. Essa otimização, que leva em conta as fidelidades do hardware e as taxas de execução, permite que você execute circuitos mais profundos e obtenha maior precisão.

Referências

O QESEM foi testado em uma ampla variedade de casos de uso e aplicativos. Os exemplos a seguir podem ajudá-lo a avaliar os tipos de cargas de trabalho que podem ser executados com o QESEM.

Uma figura-chave de mérito para quantificar a dificuldade da mitigação de erros e da simulação clássica para um determinado circuito e observável é o volume ativo : o número de portas CNOT que afetam o observável no circuito. O volume ativo depende da profundidade e da largura do circuito, do peso do observável e da estrutura do circuito, que determina o cone de luz do observável. Para obter mais detalhes, consulte a palestra do 2024 IBM Quantum Summit. O QESEM oferece um valor particularmente grande no regime de alto volume, fornecendo resultados confiáveis para circuitos e observáveis genéricos.

Volume ativo
Aplicativo
Número de qubits
Dispositivo
Descrição do circuito
Precisão
Tempo total
Uso de tempo de execução
Circuito VQE8Eagle ( r3 )21 camadas totais, 9 bases de medição, 1D chain98%35 min14 min
Chutou Ising28Eagle ( r3 )3 camadas exclusivas x 3 etapas, 2D topologia heavy-hex97%22 min4 minutos
Chutou Ising28Eagle ( r3 )3 camadas exclusivas x 8 etapas, 2D topologia heavy-hex97%116 min23 min
Simulação de Hamiltoniano Trotterizado40Eagle ( r3 )2 camadas únicas x 10 degraus de Trotter, 1D chain97%3 horas25 min
Simulação de Hamiltoniano Trotterizado119Eagle ( r3 )3 camadas exclusivas x 9 etapas de Trotter, 2D topologia heavy-hex95%6.5 horas45 min
Chutou Ising136Garça ( r2 )3 camadas exclusivas x 15 etapas, 2D topologia heavy-hexem 90%52 min9 min.

A precisão é medida aqui em relação ao valor ideal do observável: OidealϵOideal\frac{\langle O \rangle_{ideal} - \epsilon}{\langle O \rangle_{ideal}}, em que ' ϵ\epsilon ' é a precisão absoluta da atenuação (definida pela entrada do usuário), e Oideal\langle O \rangle_{ideal} é o observável no circuito sem ruído. o "uso do tempo de execução" mede o uso do parâmetro de comparação no modo de lote (soma do uso de trabalhos individuais), enquanto o "tempo total" mede o uso no modo de sessão (tempo de parede do experimento), que inclui tempos clássicos e de comunicação adicionais. O QESEM está disponível para execução em ambos os modos, para que os usuários possam fazer o melhor uso de seus recursos disponíveis.

Os circuitos Kicked Ising de 28 qubits simulam o Discrete Time Quasicrystal estudado por Shinjo et al arXiv 2403.16718 e Q2B24 Tokyo ) em três circuitos conectados de ibm_kawasaki. Os parâmetros do circuito adotados aqui são (θx,θz)=(0.9π,0)(\theta_x, \theta_z) = (0.9 \pi, 0), com um estado inicial ferromagnético ψ0=0n| \psi_0 \rangle = | 0 \rangle ^{\otimes n}. O observável medido é o valor absoluto da magnetização M=128i=027ZiM = |\frac{1}{28} \sum_{i=0}^{27} \langle Z_i \rangle|. O experimento Kicked Ising em escala de utilidade foi executado nos 136 melhores qubits do ibm_fez; esse benchmark específico foi executado no ângulo de Clifford (θx,θz)=(π,0)(\theta_x, \theta_z) = (\pi, 0), no qual o volume ativo cresce lentamente com a profundidade do circuito, o que - juntamente com as altas fidelidades do dispositivo - permite alta precisão em um curto tempo de execução.

Os circuitos de simulação de hamiltoniano troterizado são para um modelo Ising de campo transversal em ângulos fracionários: (θzz,θx)=(π/4,π/8)(\theta_{zz}, \theta_x) = (\pi / 4, \pi /8) e (θzz,θx)=(π/6,π/8)(\theta_{zz}, \theta_x) = (\pi / 6, \pi / 8), respectivamente (consulte Q2B24 em Tóquio ). O circuito em escala de utilidade foi executado nos 119 melhores qubits de ibm_brisbane, enquanto o experimento de 40 qubits foi executado na melhor cadeia disponível. A precisão é relatada para a magnetização; resultados de alta precisão também foram obtidos para observáveis de maior peso.

O circuito VQE foi desenvolvido em conjunto com pesquisadores do Center for Quantum Technology and Applications do Deutsches Elektronen-Synchrotron (DESY). O observável alvo aqui era um Hamiltoniano que consistia em um grande número de cadeias de Pauli não comutáveis, enfatizando o desempenho otimizado do QESEM para observáveis de várias bases. A atenuação foi aplicada a um ansatz otimizado classicamente; embora esses resultados ainda não tenham sido publicados, resultados da mesma qualidade serão obtidos para diferentes circuitos com propriedades estruturais semelhantes.


Introdução

Autentique-se usando sua chave de API IBM Quantum Platform e selecione a função QESEM Qiskit da seguinte forma. (Este trecho pressupõe que você já tenha salvo sua conta no seu ambiente local.)

import qiskit
from qiskit_ibm_catalog import QiskitFunctionsCatalog

catalog = QiskitFunctionsCatalog(channel="ibm_quantum_platform")

# verify that you have access to the function
catalog.list()
# load the function
qesem_function = catalog.load("qedma/qesem")

Exemplos

Exemplo de trabalho de estimativa de tempo

A tarefa de estimativa de tempo é útil para estimar o tempo de QPU necessário para um determinado pub e backend_name. backend_name também pode ser configurado para qualquer backend de simulador; por exemplo, fake_fez.

O QESEM utiliza um método EM quase-probabilístico, baseado em caracterização. Esse método apresenta uma sobrecarga de tempo da QPU que varia aproximadamente da seguinte forma:

TQPU=aeαIFVaϵ2+bT_{QPU} = a \frac{e^{\alpha IF\cdot V_a}}{\epsilon^2} + b

Onde VaV_a é o volume ativo do circuito, ϵ\epsilon é a precisão alvo e IFIF é a infidelidade das portas nativas.

Observe que "estimate_time_only": "empirical" o programa utiliza alguns minutos de tempo de QPU para estimar o tempo necessário para a tarefa (se o backend for um dispositivo real; se for um simulador, não é utilizado tempo de QPU). Isso geralmente leva cerca de 5 minutos, mas não mais do que 10 minutos. Se a infidelidade mudar drasticamente entre a tarefa de estimativa empírica de tempo e a tarefa de mitigação, o tempo de QPU também mudará drasticamente.

Para começar, experimente este exemplo básico de estimativa do tempo de QPU necessário para executar o QESEM para um determinado pub:

backend_name = "fake_fez"

circ = qiskit.QuantumCircuit(5)
circ.cx(0, 1)
circ.cx(2, 3)
circ.cx(1, 2)
circ.cx(3, 4)

avg_magnetization = qiskit.quantum_info.SparsePauliOp.from_sparse_list(
    [("Z", [q], 1 / 5) for q in range(5)], num_qubits=5
)
other_observable = qiskit.quantum_info.SparsePauliOp.from_sparse_list(
    [("ZZ", [0, 1], 1.0), ("XZ", [1, 4], 0.5)], num_qubits=5
)

time_estimation_job = qesem_function.run(
    pubs=[(circ, [avg_magnetization, other_observable])],
    options={
        "estimate_time_only": "empirical",
    },
    backend_name=backend_name,  # example: "fake_fez", "ibm_fez"
)
time_estimate_result = (
    time_estimation_job.result()
)  # a list of results per pub (circuit)

O trecho de código a seguir descreve como recuperar diferentes métricas de execução da tarefa de estimativa de tempo (estimate_time_only que está definida):

pub_result = time_estimate_result[0]

print(
    f"The estimated QPU time for mitigation for this PUB is: {pub_result.metadata['time_estimation_sec']}"
)
print(
    f"The QPU time that this time estimation job took is (here it is 0 because we used fake_fez): {pub_result.metadata['total_qpu_time']}"
)
print(
    f"Gates fidelity measured during the experiment: {pub_result.metadata['gate_fidelities']}"
)
print(f"Total shots: {pub_result.metadata['total_shots']}")
print(f"Resource usage breakdown: {pub_result.metadata['resource_usage']}")

Output:

The estimated QPU time for mitigation for this PUB is: 300
The QPU time that this time estimation job took is (here it is 0 because we used fake_fez): 0
Gates fidelity measured during the experiment: {'CZ': 0.9951354916722668, 'ID1Q': 0.9991246627329172}
Total shots: 220000
Resource usage breakdown: {'RUNNING: MAPPING': {'CPU_TIME': 33.6066133165732, 'GPU_TIME': 0.0, 'QPU_TIME': 0.0}, 'RUNNING: OPTIMIZING_FOR_HARDWARE': {'CPU_TIME': 184.53575124032795, 'GPU_TIME': 0.0, 'QPU_TIME': 0.0}, 'RUNNING: WAITING_FOR_QPU': {'CPU_TIME': 0.0, 'GPU_TIME': 0.0, 'QPU_TIME': 0.0}, 'RUNNING: EXECUTING_QPU': {'CPU_TIME': 0.0, 'GPU_TIME': 0.0, 'QPU_TIME': 0.0}, 'RUNNING: POST_PROCESSING': {'CPU_TIME': 0.0, 'GPU_TIME': 0.0, 'QPU_TIME': 0.0}}

Quando a estimativa empírica de tempo está ativada, o QESEM executa um pequeno número de circuitos para fornecer uma estimativa mais precisa do tempo de execução na QPU. Os resultados da mitigação desses circuitos estão disponíveis no empirical_estimation_mitigation_results campo do trabalho.

Como esses resultados foram obtidos a partir de uma amostra limitada de circuitos, eles são significativamente menos precisos do que os resultados finais obtidos a partir do trabalho completo de mitigação do QESEM. No entanto, quando o circuito é pequeno ou a precisão alvo é baixa, a mitigação realizada durante a estimativa empírica do tempo pode ser suficiente para atingir a precisão desejada, e talvez não seja necessário realizar todo o processo de mitigação.

O empirical_estimation_mitigation_results campo é uma lista de PrimitiveResult objetos por parâmetro de entrada. Se o circuito não estiver parametrizado, a lista terá comprimento 1.

empirical_estimation_mitigation_results = time_estimate_result[0].metadata[
    "empirical_estimation_mitigation_results"
][0]  # a list per parameter
print("Partial results for the observables:")

print(
    f"    Mitigated expectation values: {empirical_estimation_mitigation_results.data.evs}"
)
print(
    f"    Mitigated error bars: {empirical_estimation_mitigation_results.data.stds}"
)
print(
    f"    Number of shots used for mitigation: {empirical_estimation_mitigation_results.metadata['mitigation_shots']}"
)
transpiled_circ = empirical_estimation_mitigation_results.metadata[
    "transpiled_circ"
]
print(f"    Qubit mapping: {transpiled_circ['qubit_maps']}")
print(
    f"    Number of measurement bases: {transpiled_circ['num_measurement_bases']}\n"
)

# results per obs
emp_obs_results = empirical_estimation_mitigation_results.metadata["results"][
    0
]
# print(f"Results for each observable: {results}")
print("Results for each observable:")

for i, (obs_array, result_dict) in enumerate(emp_obs_results):
    # obs_array, result_dict = results
    print(f"Observable {i+1}: {obs_array}")
    print(
        f"    QESEM mitigated value: {result_dict['qesem']['value']} \u00b1 {result_dict['qesem']['error_bar']}"
    )

Output:

Partial results for the observables:
    Mitigated expectation values: [1.00347302 1.00693905]
    Mitigated error bars: [0.00304061 0.00714276]
    Number of shots used for mitigation: 180000
    Qubit mapping: [[[0, 136], [1, 143], [2, 142], [3, 141], [4, 140]]]
    Number of measurement bases: 2

Results for each observable:
Observable 1: ObservablesArray({'IIIIZ': 0.2, 'IIIZI': 0.2, 'IIZII': 0.2, 'IZIII': 0.2, 'ZIIII': 0.2}, shape=())
    QESEM mitigated value: 1.003473015776871 ± 0.0030406128032204015
Observable 2: ObservablesArray({'IIIZZ': 1.0, 'ZIIXI': 0.5}, shape=())
    QESEM mitigated value: 1.0069390542613554 ± 0.0071427606736885925

Exemplo de tarefa de mitigação no QESEM

O exemplo a seguir executa um trabalho do QESEM:

sample_job = qesem_function.run(
    pubs=[(circ, [avg_magnetization, other_observable])],
    backend_name=backend_name,  # example: "ibm_fez"
    # options = {
    #     "estimate_time_only": "empirical",
    #     "default_precision": 0.2,  # Default precision is applied to all pubs that don't have a precision specified, see API reference for more details
    #     "max_execution_time": 3600,  # You can specify a maximum QPU time in seconds, see API reference for more details
    #     "transpilation_level": "standard",  # "minimal_with_layout_opt" for minimal transpilation, see API reference for more details
    #     "parallel_execution": True,  # True for parallel execution, see API reference for more details
    # },
)

Para obter uma descrição detalhada de cada campo de entrada e opção, consulte a referência da API do QESEM.

Você pode usar as APIs conhecidas do Qiskit Serverless para verificar o status da carga de trabalho do Qiskit Function ou retornar resultados:

# Print the ID so you can use it later, if necessary
print(sample_job.job_id)
print(sample_job.status())
sample_result = sample_job.result()

Output:

3ac6b2df-15b0-4dc0-8f48-cf14bd20a1c8
DONE

O trecho de código a seguir demonstra como recuperar os resultados da mitigação e as métricas de execução. Esses dados contêm informações essenciais que permitem uma compreensão mais profunda de como os diferentes parâmetros afetam a execução do QESEM. Isso também pode ser relevante ao redigir um artigo com base em sua pesquisa.

for pub_idx, pub_result in enumerate(
    sample_result
):  # each element in the list is a result for a different pub, here we sent only one pub
    print(f"\nPUB {pub_idx}:")
    print(
        f"  The QPU time that this job took is (here it is 0 because we used fake_fez): {pub_result.metadata['total_qpu_time']}"
    )
    print(
        f"  Gates fidelity measured during the experiment: {pub_result.metadata['gate_fidelities']}"
    )
    print(f"  Total shots: {pub_result.metadata['total_shots']}")
    print(
        f"  Number of shots used for mitigation: {pub_result.metadata['mitigation_shots']}"
    )
    print(
        f"  Resource usage breakdown: {pub_result.metadata['resource_usage']}"
    )

Output:


PUB 0:
  The QPU time that this job took is (here it is 0 because we used fake_fez): 0.0
  Gates fidelity measured during the experiment: {'CZ': 0.9953704216147041, 'ID1Q': 0.9991834123567518}
  Total shots: 446000
  Number of shots used for mitigation: 194000
  Resource usage breakdown: {'RUNNING: MAPPING': {'CPU_TIME': 32.52745003718883, 'GPU_TIME': 0.0, 'QPU_TIME': 0.0}, 'RUNNING: OPTIMIZING_FOR_HARDWARE': {'CPU_TIME': 257.850521848537, 'GPU_TIME': 0.0, 'QPU_TIME': 0.0}, 'RUNNING: WAITING_FOR_QPU': {'CPU_TIME': 0.0, 'GPU_TIME': 0.0, 'QPU_TIME': 0.0}, 'RUNNING: EXECUTING_QPU': {'CPU_TIME': 0.0, 'GPU_TIME': 0.0, 'QPU_TIME': 0.0}, 'RUNNING: POST_PROCESSING': {'CPU_TIME': 0.0, 'GPU_TIME': 0.0, 'QPU_TIME': 0.0}}

metadata["results"]Nesse caso, os resultados são agrupados primeiro por instância do circuito e, em seguida, por observável.

print("Full QESEM mitigation results:")

for pub_idx, pub_result in enumerate(sample_result):
    print(f"\nPUB {pub_idx}:")

    print(f"  Mitigated expectation values: {pub_result.data.evs}")
    print(f"  Mitigated error bars: {pub_result.data.stds}")
    noisy_results = pub_result.metadata.get("noisy_results")
    print(f"  Unmitigated expectation values: {noisy_results.evs}")
    print(f"  Unmitigated error bars: {noisy_results.stds}")

    print("  Transpiled circuits:")
    for circ_idx, transpiled_circ in enumerate(
        pub_result.metadata["transpiled_circs"]
    ):
        print(f"    Circuit {circ_idx}:")
        # print(f"      Circuit: \n {transpiled_circ['circuit']}") # not printing it because it's long but you can see the transpiled circuit itself
        print(f"      Qubit mapping: {transpiled_circ['qubit_maps']}")
        print(
            f"      Measurement bases: {transpiled_circ['num_measurement_bases']}"
        )

    print("  Results for each observable:")
    for circ_idx, circ_results in enumerate(pub_result.metadata["results"]):
        print(f"    Circuit {circ_idx}:")
        for obs_idx, (obs_array, result_dict) in enumerate(circ_results):
            print(f"      Observable {obs_idx}: {obs_array}")
            print(
                f"        QESEM mitigated value: {result_dict['qesem']['value']} ± {result_dict['qesem']['error_bar']}"
            )
            print(
                f"        Unmitigated value: {result_dict['unmitigated']['value']} ± {result_dict['unmitigated']['error_bar']}"
            )

Output:

Full QESEM mitigation results:

PUB 0:
  Mitigated expectation values: [1.00648343 1.00636289]
  Mitigated error bars: [0.00253812 0.00693586]
  Unmitigated expectation values: [0.98031429 0.96357143]
  Unmitigated error bars: [0.00124128 0.00578812]
  Transpiled circuits:
    Circuit 0:
      Qubit mapping: [[[0, 140], [1, 141], [2, 142], [3, 143], [4, 136]]]
      Measurement bases: 2
  Results for each observable:
    Circuit 0:
      Observable 0: ObservablesArray({'IIIIZ': 0.2, 'IIIZI': 0.2, 'IIZII': 0.2, 'IZIII': 0.2, 'ZIIII': 0.2}, shape=())
        QESEM mitigated value: 1.0064834305181962 ± 0.002538119914534849
        Unmitigated value: 0.9803142857142859 ± 0.0012412835813609938
      Observable 1: ObservablesArray({'IIIZZ': 1.0, 'ZIIXI': 0.5}, shape=())
        QESEM mitigated value: 1.0063628870614818 ± 0.006935859820870656
        Unmitigated value: 0.9635714285714285 ± 0.005788121870526659

Discriminação dos principais resultados:

  • mitigated: o valor esperado do QESEM totalmente mitigado.
  • unmitigated: o resultado bruto do ruído físico sem mitigação de erros.

Resultados da extrapolação heurística do QESEM

Em uma execução padrão do QESEM com um único precision flutuador, os resultados também incluem pontos de escala de ruído disponíveis automaticamente, que são utilizados para a heurística do QESEM. Esses pontos são calculados sem recursos adicionais da QPU.

A escala 1.0 representa o nível de ruído do dispositivo físico com mitigação de leitura (REM), enquanto a escala 2.0 corresponde ao ponto complementar com amplificação de ruído, também com REM. Esses pontos são utilizados para gerar o qesem_heuristic resultado.

  • qesem_heuristic: uma estimativa do tipo ZNE calculada a partir dos dados disponíveis, ajustados em função do ruído. Atualmente, isso utiliza extrapolação exponencial.
  • noise_scaling.results_with_REM: valores esperados em diferentes escalas de ruído, todos com mitigação de leitura (REM).

Um detalhe sutil, mas importante, é que o resultado da escala 1.0 não é o mesmo que o unmitigated resultado. Ambos correspondem ao nível de ruído do dispositivo físico, mas o ponto da escala 1.0 inclui a mitigação da leitura, enquanto unmitigated não inclui.

print("QESEM heuristic results:")

for pub_idx, pub_result in enumerate(sample_result):
    print(f"\nPUB {pub_idx}:")
    for circ_idx, circ_results in enumerate(pub_result.metadata["results"]):
        print(f"  Circuit {circ_idx}:")
        for obs_idx, (obs_array, result_dict) in enumerate(circ_results):
            print(f"    Observable {obs_idx}: {obs_array}")
            qesem_heuristic = result_dict["qesem_heuristic"][0]
            print("      QESEM heuristic:")
            print(
                f"        Value: {qesem_heuristic['value']} ± {qesem_heuristic['error_bar']}"
            )
            print(
                f"        Extrapolation: {qesem_heuristic['extrapolation']}"
            )
            print(
                f"        Scale factors: {qesem_heuristic['scale_factors']}"
            )
            noise_scaling = result_dict["noise_scaling"]
            print("      Noise scaling results:")
            print(
                f"        Scaling method: {noise_scaling['scaling_method']}"
            )
            print("        Results with Readout mitigation (REM):")
            for rem_result in sorted(
                (
                    item
                    for item in noise_scaling["results_with_REM"]
                    if item["scale"] != 0.0
                ),
                key=lambda item: item["scale"],
            ):
                print(
                    f"          Scale factor {rem_result['scale']}: {rem_result['value']} ± {rem_result['error_bar']}"
                )

Output:

QESEM heuristic results:

PUB 0:
  Circuit 0:
    Observable 0: ObservablesArray({'IIIIZ': 0.2, 'IIIZI': 0.2, 'IIZII': 0.2, 'IZIII': 0.2, 'ZIIII': 0.2}, shape=())
      QESEM heuristic:
        Value: 1.0008161638888535 ± 0.0038859458884403964
        Extrapolation: exponential
        Scale factors: [1.0, 2.0]
      Noise scaling results:
        Scaling method: QESEM
        Results with Readout mitigation (REM):
          Scale factor 1.0: 0.9918395459270772 ± 0.0012565417579355634
          Scale factor 2.0: 0.982943441922748 ± 0.0028919278067695018
    Observable 1: ObservablesArray({'IIIZZ': 1.0, 'ZIIXI': 0.5}, shape=())
      QESEM heuristic:
        Value: 0.9960853148925298 ± 0.013811635038961175
        Extrapolation: exponential
        Scale factors: [1.0, 2.0]
      Noise scaling results:
        Scaling method: QESEM
        Results with Readout mitigation (REM):
          Scale factor 1.0: 0.9902860583785115 ± 0.005921236914723409
          Scale factor 2.0: 0.984520565414274 ± 0.006909522262347639
Note

Os exemplos a seguir se concentram em entradas e resultados específicos de cada recurso; portanto, não exibem todas as métricas de execução a cada vez. Os metadados de nível superior mostrados anteriormente, como total_qpu_time, gate_fidelities, total_chutes, medidas_de_mitigação e uso_de_recursos, também estão disponíveis para esses trabalhos.

Algumas variáveis dos exemplos anteriores, incluindo o backend, os observáveis e os parâmetros do circuito base, são reutilizadas a seguir para maior concisão.

Note

Todos os exemplos a seguir também podem ser executados com estimativa empírica de tempo. Para ativá-lo, passe <code>"estimate_time_only": "empírico"</code> nas opções da função.

Exemplo de circuito parametrizado

Muitos algoritmos avaliam o mesmo circuito com vários valores de parâmetros. O envio do circuito parametrizado como um único trabalho no QESEM permite que o QESEM compartilhe a caracterização e a calibração entre as instâncias do circuito, o que pode reduzir a sobrecarga de tempo de QPU em comparação com a execução de trabalhos separados.

Para enviar um circuito parametrizado, é necessário utilizar o "minimal_with_layout_opt" nível de transpilação. Os circuitos enviados neste nível já devem estar expressos utilizando as portas básicas do backend, dependendo do backend. Nesse nível, o QESEM mantém a estrutura enviada o mais próxima possível do circuito de entrada, respeita as barreiras durante a estratificação (agrupamento de operações em camadas de portas paralelas de dois qubits) e ainda lida automaticamente com o mapeamento de hardware para qubits de alta fidelidade e com a conectividade dos dispositivos.

Na prática, isso significa que você deve transpilá-los para as portas básicas do backend de destino antes do envio. Segue abaixo um exemplo simples de transpilagem de porta de base.

Atualmente, o QESEM suporta apenas uma variável observável por conjunto de parâmetros. As duas linhas de parâmetros abaixo estão associadas aos dois observáveis: a primeira linha é medida com avg_magnetization, e a segunda linha é medida com other_observable.

# Transpile to the backend basis gates only. With minimal_with_layout_opt, QESEM handles hardware mapping/connectivity and observable layout internally.
from qiskit_ibm_runtime.fake_provider import FakeFez

backend = FakeFez()
basis = backend.operation_names
print(basis)

param0 = qiskit.circuit.Parameter("param0")
param1 = qiskit.circuit.Parameter("param1")
parametrized_circ = qiskit.QuantumCircuit(5)
parametrized_circ.rx(param0, 0)
parametrized_circ.rx(param1, 1)
parametrized_circ.cx(0, 1)
parametrized_circ.cx(2, 3)
parametrized_circ.cx(1, 2)
parametrized_circ.cx(3, 4)

parametrized_circ = qiskit.transpile(
    parametrized_circ, basis_gates=basis, optimization_level=1
)
parametrized_parameter_values = [[0.5, 0.1], [0.0, 0.6]]
parametrized_observables = [avg_magnetization, other_observable]

parametrized_job = qesem_function.run(
    pubs=[
        (
            parametrized_circ,
            parametrized_observables,
            parametrized_parameter_values,
            0.1,
        )
    ],
    backend_name=backend_name,
    options={
        "max_execution_time": 300,
        "transpilation_level": "minimal_with_layout_opt",
    },
)
print(parametrized_job.job_id)
print(parametrized_job.status())

Output:

d1b0e29b-196c-4896-aec6-44a268ebd874
DONE
parametrized_result = parametrized_job.result()
print("Parameterized circuit QESEM results:")

for pub_idx, pub_result in enumerate(parametrized_result):
    print(f"\nPUB {pub_idx}:")
    print(f"  Mitigated expectation values: {pub_result.data.evs}")
    print(f"  Mitigated error bars: {pub_result.data.stds}")
    noisy_results = pub_result.metadata["noisy_results"]
    print(f"  Unmitigated expectation values: {noisy_results.evs}")
    print(f"  Unmitigated error bars: {noisy_results.stds}")
    print("  Results for each parameter value:")
    for param_idx, param_results in enumerate(pub_result.metadata["results"]):
        print(
            f"    Parameter set {param_idx}: {parametrized_parameter_values[param_idx]}"
        )
        for obs_idx, (obs_array, result_dict) in enumerate(param_results):
            print(f"      Observable {obs_idx}: {obs_array}")
            print(
                f"        QESEM mitigated value: {result_dict['qesem']['value']} ± {result_dict['qesem']['error_bar']}"
            )
            print(
                f"        Unmitigated value: {result_dict['unmitigated']['value']} ± {result_dict['unmitigated']['error_bar']}"
            )

Output:

Parameterized circuit QESEM results:

PUB 0:
  Mitigated expectation values: [0.92392021 0.82517653]
  Mitigated error bars: [0.00565281 0.00616016]
  Unmitigated expectation values: [0.9028     0.78771429]
  Unmitigated error bars: [0.00335142 0.00925413]
  Results for each parameter value:
    Parameter set 0: [0.5, 0.1]
      Observable 0: ObservablesArray({'IIIIZ': 0.2, 'IIIZI': 0.2, 'IIZII': 0.2, 'IZIII': 0.2, 'ZIIII': 0.2}, shape=())
        QESEM mitigated value: 0.923920210615709 ± 0.005652811570890183
        Unmitigated value: 0.9028 ± 0.0033514176105045447
    Parameter set 1: [0.0, 0.6]
      Observable 0: ObservablesArray({'IIIZZ': 1.0, 'ZIIXI': 0.5}, shape=())
        QESEM mitigated value: 0.8251765289285893 ± 0.006160161743353999
        Unmitigated value: 0.7877142857142858 ± 0.009254130564027902

Exemplo de “multi-pub”

A execução em múltiplos pubs é útil quando se deseja executar vários circuitos relacionados em um único trabalho do QESEM. Assim como a execução parametrizada, isso pode reduzir a sobrecarga, pois o QESEM pode compartilhar a caracterização e a calibração entre instâncias do circuito, em vez de repeti-las em tarefas separadas.

Isso é especialmente útil para circuitos com uma estrutura de camadas compartilhadas, como cargas de trabalho do tipo Trotter, nas quais diferentes circuitos reutilizam as mesmas camadas únicas. Nesse caso, a execução conjunta dessas análises pode reduzir o custo da caracterização em comparação com trabalhos independentes de QESEM.

Trabalhos com publicação em vários veículos exigem "transpilation_level": "minimal_with_layout_opt". Assim como no exemplo parametrizado, os circuitos devem ser transpilados para as portas básicas do backend de destino antes do envio. O QESEM, então, lida internamente com a conectividade dos dispositivos, o layout e o mapeamento para qubits de alta fidelidade.

Cada PUB abaixo contém um circuito e os mesmos dois observáveis usados anteriormente neste notebook; portanto, o resultado retornado PrimitiveResult contém um PubResult por circuito de entrada.

O exemplo abaixo utiliza dois circuitos de Trotter simples com o mesmo padrão de camadas: circ_a possui uma camada de Trotter, e circ_b repete o mesmo padrão de camadas duas vezes. Isso torna a estrutura compartilhada explícita.

def make_trotter_circuit(num_qubits, num_layers, zz_angle=0.2, x_angle=0.1):
    trotter_circ = qiskit.QuantumCircuit(num_qubits)
    for _ in range(num_layers):
        for q in range(num_qubits):
            trotter_circ.rx(x_angle, q)
        trotter_circ.barrier()
        for q in range(0, num_qubits - 1, 2):
            trotter_circ.rzz(zz_angle, q, q + 1)
        trotter_circ.barrier()
        for q in range(1, num_qubits - 1, 2):
            trotter_circ.rzz(zz_angle, q, q + 1)
        trotter_circ.barrier()
    return trotter_circ


circ_a = make_trotter_circuit(num_qubits=5, num_layers=1)
circ_b = make_trotter_circuit(num_qubits=5, num_layers=2)
multi_pubs = [
    (
        qiskit.transpile(qci, basis_gates=basis, optimization_level=1),
        [avg_magnetization, other_observable],
    )
    for qci in [circ_a, circ_b]
]

multi_circ_job = qesem_function.run(
    pubs=multi_pubs,
    backend_name=backend_name,
    options={
        "max_execution_time": 300,
        "transpilation_level": "minimal_with_layout_opt",
        "default_precision": 0.1,
    },
)
print(multi_circ_job.job_id)
print(multi_circ_job.status())

Output:

e34565b8-7262-4133-a120-de42ce624a99
DONE
multi_circ_result = multi_circ_job.result()
print("Multi-pub QESEM results:")

for pub_idx, pub_result in enumerate(multi_circ_result):
    print(f"\nPUB {pub_idx}:")
    print(f"  Mitigated expectation values: {pub_result.data.evs}")
    print(f"  Mitigated error bars: {pub_result.data.stds}")
    noisy_results = pub_result.metadata["noisy_results"]
    print(f"  Unmitigated expectation values: {noisy_results.evs}")
    print(f"  Unmitigated error bars: {noisy_results.stds}")
    print("  Results for each observable:")
    for circ_idx, circ_results in enumerate(pub_result.metadata["results"]):
        print(f"    Circuit {circ_idx}:")
        for obs_idx, (obs_array, result_dict) in enumerate(circ_results):
            print(f"      Observable {obs_idx}: {obs_array}")
            print(
                f"        QESEM mitigated value: {result_dict['qesem']['value']} ± {result_dict['qesem']['error_bar']}"
            )
            print(
                f"        Unmitigated value: {result_dict['unmitigated']['value']} ± {result_dict['unmitigated']['error_bar']}"
            )

Output:

Multi-pub QESEM results:

PUB 0:
  Mitigated expectation values: [0.99502406 1.02209332]
  Mitigated error bars: [0.00483819 0.00707488]
  Unmitigated expectation values: [0.96934286 0.97271429]
  Unmitigated error bars: [0.00124855 0.00617294]
  Results for each observable:
    Circuit 0:
      Observable 0: ObservablesArray({'IIIIZ': 0.2, 'IIIZI': 0.2, 'IIZII': 0.2, 'IZIII': 0.2, 'ZIIII': 0.2}, shape=())
        QESEM mitigated value: 0.9950240647925642 ± 0.004838188086259301
        Unmitigated value: 0.9693428571428573 ± 0.0012485470362492692
      Observable 1: ObservablesArray({'IIIZZ': 1.0, 'ZIIXI': 0.5}, shape=())
        QESEM mitigated value: 1.0220933230604674 ± 0.007074884384355636
        Unmitigated value: 0.9727142857142859 ± 0.006172939375512439

PUB 1:
  Mitigated expectation values: [0.98850017 1.02555188]
  Mitigated error bars: [0.0077912  0.01672652]
  Unmitigated expectation values: [0.93682857 0.95371429]
  Unmitigated error bars: [0.00156245 0.00665735]
  Results for each observable:
    Circuit 0:
      Observable 0: ObservablesArray({'IIIIZ': 0.2, 'IIIZI': 0.2, 'IIZII': 0.2, 'IZIII': 0.2, 'ZIIII': 0.2}, shape=())
        QESEM mitigated value: 0.988500171577252 ± 0.007791203181151346
        Unmitigated value: 0.9368285714285716 ± 0.001562451883089579
      Observable 1: ObservablesArray({'IIIZZ': 1.0, 'ZIIXI': 0.5}, shape=())
        QESEM mitigated value: 1.02555188098689 ± 0.016726524388086233
        Unmitigated value: 0.9537142857142858 ± 0.006657345655544263

Exemplo de Ajuste de Erro Quase-Probabilístico (QET)

O Ajuste de Erro Quase-Probabilístico (QET) solicita valores esperados em fatores de escala de ruído selecionados. Isso é útil para estudos de escalonamento de ruído personalizados e fluxos de trabalho de extrapolação com ruído zero. A escala 1.0 representa o nível físico de ruído; valores entre 0.0 e 1.0 reduzem parcialmente o ruído, enquanto valores acima de 1.0 amplificam o ruído.

Para usar o QET com a função Qiskit, passe um dicionário como a precisão do PUB. O dicionário mapeia cada escala de ruído solicitada para sua precisão alvo. Os resultados do fator de escala retornados são armazenados em noise_scaling.results_with_REM e incluem a mitigação da leitura. O ponto na escala 1.0 , portanto, não é idêntico ao valor não mitigado, pois 1.0 inclui a mitigação da leitura, enquanto unmitigated não inclui.

Quando uma escala é solicitada, o QESEM também retorna a escala complementar em torno de 1.0 sem consumo adicional de QPU. Por exemplo, a solicitação 0.5 de também pode retornar 1.5, e a solicitação 1.3 de também pode retornar 0.7. A precisão na escala complementar não é garantida.

noise_scale_precision = {0.5: 0.15, 1.3: 0.2}

qet_job = qesem_function.run(
    pubs=[
        (
            circ,
            [avg_magnetization, other_observable],
            None,
            noise_scale_precision,
        )
    ],
    backend_name=backend_name,
    options={"max_execution_time": 300},
)
print(qet_job.job_id)
print(qet_job.status())

Output:

8195fa58-f037-4651-8715-36ce1cdc5521
DONE
qet_result = qet_job.result()
print("QET noise-scaling results:")

for pub_idx, pub_result in enumerate(qet_result):
    print(f"\nPUB {pub_idx}:")
    noisy_results = pub_result.metadata["noisy_results"]
    print(f"  Unmitigated expectation values: {noisy_results.evs}")
    print(f"  Unmitigated error bars: {noisy_results.stds}")
    print("  Results for each observable:")
    for circ_idx, circ_results in enumerate(pub_result.metadata["results"]):
        print(f"    Circuit {circ_idx}:")
        for obs_idx, (obs_array, result_dict) in enumerate(circ_results):
            print(f"      Observable {obs_idx}: {obs_array}")
            noise_scaling = result_dict["noise_scaling"]
            print(
                f"        Scaling method: {noise_scaling['scaling_method']}"
            )
            print("        Results with Readout mitigation (REM):")
            for rem_result in sorted(
                (
                    item
                    for item in noise_scaling["results_with_REM"]
                    if item["scale"] != 0.0
                ),
                key=lambda item: item["scale"],
            ):
                print(
                    f"          Scale factor {rem_result['scale']}: {rem_result['value']} ± {rem_result['error_bar']}"
                )

Output:

QET noise-scaling results:

PUB 0:
  Unmitigated expectation values: [0.97822857 0.96171429]
  Unmitigated error bars: [0.00123812 0.00672958]
  Results for each observable:
    Circuit 0:
      Observable 0: ObservablesArray({'IIIIZ': 0.2, 'IIIZI': 0.2, 'IIZII': 0.2, 'IZIII': 0.2, 'ZIIII': 0.2}, shape=())
        Scaling method: QESEM
        Results with Readout mitigation (REM):
          Scale factor 0.5: 0.9938730340199383 ± 0.0032116907357568275
          Scale factor 0.7: 0.9963976191853445 ± 0.00036300258869586616
          Scale factor 1.0: 0.9898115079506586 ± 0.0012525947426560995
          Scale factor 1.3: 0.9864667065580341 ± 0.002633221613518526
          Scale factor 1.5: 0.9838755527197551 ± 0.002948417797996015
      Observable 1: ObservablesArray({'IIIZZ': 1.0, 'ZIIXI': 0.5}, shape=())
        Scaling method: QESEM
        Results with Readout mitigation (REM):
          Scale factor 0.5: 1.0006538544450332 ± 0.002121742014777343
          Scale factor 0.7: 1.0004159801523036 ± 0.0021671375357823794
          Scale factor 1.0: 0.9898058846339917 ± 0.00690183710903159
          Scale factor 1.3: 0.9948946719997267 ± 0.002146532859610311
          Scale factor 1.5: 0.9927220368192772 ± 0.0020875057190323882

Buscar mensagens de erro

Se o status da sua carga de trabalho for ERROR, use job.result() para obter a mensagem de erro da seguinte maneira:

# Get the result and truncate for readability
result = sample_job.result()
result_str = str(result)
max_length = 500  # Adjust this value as necessary

if len(result_str) > max_length:
    truncated = (
        result_str[:max_length]
        + f"... (truncated {len(result_str) - max_length} characters)"
    )
else:
    truncated = result_str

print(truncated)

Output:

PrimitiveResult([PubResult(data=DataBin(evs=np.ndarray(<shape=(2,), dtype=float64>), stds=np.ndarray(<shape=(2,), dtype=float64>), shape=(2,)), metadata={'gate_fidelities': {'CZ': 0.9979444718552628, 'ID1Q': 0.9991994239814883}, 'total_shots': 498600, 'mitigation_shots': 223400, 'transpiled_circs': [{'circuit': 'OPENQASM 3.0;\ninclude "stdgates.inc";\nbit[76] c0;\nqubit[76] q0;\nrx(0) q0[54];\nrx(0) q0[59];\nrx(0) q0[75];\nrz(pi/2) q0[54];\nrz(pi/2) q0[59];\nrz(pi/2) q0[75];\nrx(pi/2) q0[54];\nr... (truncated 3771 characters)

Obtenha suporte

A equipe de suporte da Qedma está aqui para ajudar! Se você encontrar algum problema ou tiver dúvidas sobre o uso da função Qiskit da QESEM, não hesite em entrar em contato. Nossa equipe de suporte experiente e amigável está pronta para ajudá-lo com qualquer dúvida técnica ou questionamento que você possa ter.

Você pode nos enviar um e-mail para [email protected] para obter assistência. Inclua o máximo de detalhes possível sobre o problema que está enfrentando para nos ajudar a fornecer uma resposta rápida e precisa. Você também pode entrar em contato com seu representante POC dedicado da Qedma por e-mail ou telefone.

Para nos ajudar a ajudá-lo de forma mais eficiente, forneça as seguintes informações ao entrar em contato conosco:

  • Uma descrição detalhada do problema
  • O ID da tarefa
  • Quaisquer mensagens ou códigos de erro relevantes

Temos o compromisso de fornecer suporte imediato e eficaz para garantir que você tenha a melhor experiência possível com a nossa função Qiskit.

Estamos sempre buscando melhorar nosso produto e agradecemos suas sugestões! Se você tiver ideias sobre como podemos melhorar nossos serviços ou recursos que gostaria de ver, envie-nos suas sugestões para [email protected].


Próximas etapas

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