Skip to main content
IBM Quantum Platform

Execute seu primeiro circuito no hardware

  • O código nesta página foi desenvolvido utilizando os seguintes requisitos. Recomendamos usar essas versões ou mais recentes.

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

Este exemplo contém duas partes. Primeiro, você criará um programa quântico simples “Olá, mundo” e o executará em uma unidade de processamento quântico (QPU). Como a pesquisa quântica real requer programas muito mais robustos, na segunda seção ( Escala para um grande número de qubits ), você ampliará o programa simples até o nível de utilidade.


Instalar e autenticar

  1. Se você ainda não instalou o Qiskit, consulte as instruções no guia de início rápido.

    • Instale Qiskit Runtime para executar tarefas em hardware quântico:

      pip install qiskit-ibm-runtime
    • Configure um ambiente para executar notebooks Jupyter localmente:

      pip install jupyter
  2. Configure sua autenticação para acessar o hardware quântico através do Open Plan gratuito.

    (Se você recebeu um convite por e-mail para participar de uma conta, siga as etapas para usuários convidados.)

    • Acesse IBM Quantum Platform para fazer login ou criar uma conta.

      Importante

      Se você se conectar por meio de um servidor proxy, deverá usar Qiskit Runtime v0.44.0 ou posterior.

    • Gere sua chave API (também chamada de token API ) no painel e copie-a para um local seguro.

    • Acesse a página Instâncias e localize a instância que deseja utilizar. Passe o cursor sobre o CRN e clique para copiá-lo.

    • Salve suas credenciais localmente com este código:

      from qiskit_ibm_runtime import QiskitRuntimeService
      
      QiskitRuntimeService.save_account(
          # For `token`, use the 44-character API_KEY you created
          # and saved from the IBM Quantum Platform Home dashboard
          token="<your-api-key>",
          instance="<CRN>", # Optional
      )
  3. Agora você pode usar este Python código sempre que quiser se autenticar no Qiskit Runtime Serviço:

    from qiskit_ibm_runtime import QiskitRuntimeService
    
    # Run every time you need the service
    service = QiskitRuntimeService()
Não está usando um ambiente Python confiável?

Se você estiver usando um computador público ou outro ambiente não seguro, siga as instruções de autenticação manual para manter suas credenciais de autenticação seguras.


Crie e execute um programa quântico simples

As quatro etapas para escrever um programa quântico usando padrões Qiskit são:

  1. Mapeie o problema para um formato nativo quântico.

  2. Otimize os circuitos e os operadores.

  3. Execute usando uma função primitiva quântica.

  4. Analise os resultados.

Etapa 1. Mapeie o problema para um formato nativo quântico

Em um programa quântico, os circuitos quânticos são o formato nativo no qual as instruções quânticas são representadas, e os operadores representam os observáveis a serem medidos. Ao criar um circuito, você geralmente cria um novo QuantumCircuit objeto e, em seguida, adiciona instruções a ele em sequência.

A seguinte célula de código cria um circuito que produz um estado Bell, que é um estado em que dois qubits estão totalmente entrelaçados entre si.

Observação: ordem dos bits

O Qiskit SDK usa a numeração de bits LSb 0, em que o nthn^{th} dígito tem valor 1n1 \ll n ou 2n2^n. Para obter mais detalhes, consulte a ordem dos bits no Qiskit SDK tópico.

from qiskit import QuantumCircuit
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import EstimatorOptions
from qiskit_ibm_runtime import EstimatorV2 as Estimator
from matplotlib import pyplot as plt
# Uncomment the next line if you want to use a simulator:
# from qiskit_ibm_runtime.fake_provider import FakeBelemV2


# Create a new circuit with two qubits
qc = QuantumCircuit(2)

# Add a Hadamard gate to qubit 0
qc.h(0)

# Perform a controlled-X gate on qubit 1, controlled by qubit 0
qc.cx(0, 1)

# Return a drawing of the circuit using MatPlotLib ("mpl").
# These guides are written by using Jupyter notebooks, which
# display the output of the last line of each cell.
# If you're running this in a script, use `print(qc.draw())` to
# print a text drawing.
qc.draw("mpl")

Output:

Output of the previous code cell

Consulte QuantumCircuit a documentação para ver todas as operações disponíveis.

Ao criar circuitos quânticos, é preciso também levar em conta que tipo de dados você deseja obter após a execução. O Qiskit oferece duas maneiras de retornar dados: você pode obter resultados de amostragem para um conjunto de qubits que decidir medir, ou pode obter o valor esperado de uma observável. Prepare sua carga de trabalho para medir seu circuito de uma dessas duas maneiras usando o Qiskit primitives (explicado em detalhes na Etapa 3 ).

Este exemplo mede valores esperados usando o qiskit.quantum_info submódulo, que é especificado usando operadores (objetos matemáticos usados para representar uma ação ou processo que altera um estado quântico). A seguinte célula de código cria seis operadores Pauli de dois qubits: IZ, IX, ZI XI, ZZ, e XX.

# Set up six different observables.

observables_labels = ["IZ", "IX", "ZI", "XI", "ZZ", "XX"]
observables = [SparsePauliOp(label) for label in observables_labels]
Notação do operador

Aqui, algo como o ZZ operador é uma abreviação do produto tensorial ZZZ\otimes Z, o que significa medir Z no qubit 1 e Z no qubit 0 juntos e obter informações sobre a correlação entre o qubit 1 e o qubit 0. Valores esperados como este também são normalmente escritos como Z1Z0\langle Z_1 Z_0 \rangle.

Se o estado estiver entrelaçado, então a medição de Z1Z0\langle Z_1 Z_0 \rangle deve ser diferente da medição de I1Z0Z1I0\langle I_1 \otimes Z_0 \rangle \langle Z_1 \otimes I_0 \rangle. Para o estado entrelaçado específico criado pelo nosso circuito descrito acima, a medição de Z1Z0\langle Z_1 Z_0 \rangle deve ser 1 e a medição de I1Z0Z1I0\langle I_1 \otimes Z_0 \rangle \langle Z_1 \otimes I_0 \rangle deve ser zero.

Etapa 2. Otimize os circuitos e operadores

Ao executar circuitos em um dispositivo, é importante otimizar o conjunto de instruções que o circuito contém e minimizar a profundidade geral (aproximadamente o número de instruções) do circuito. Isso garante que você obtenha os melhores resultados possíveis, reduzindo os efeitos de erros e ruídos. Além disso, as instruções do circuito devem estar em conformidade com a Arquitetura de Conjunto de Instruções (ISA) do dispositivo backend e devem considerar as portas básicas do dispositivo e a conectividade qubit.

O código a seguir instancia um dispositivo real para enviar um trabalho e transforma o circuito e os observáveis para corresponder à ISA desse backend. É necessário que você já tenha salvo suas credenciais.

service = QiskitRuntimeService()

backend = service.least_busy(simulator=False, operational=True)

# Convert to an ISA circuit and layout-mapped observables.
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(qc)

isa_circuit.draw("mpl", idle_wires=False)

Output:

Output of the previous code cell

Etapa 3. Executar usando os primitivos quânticos

Os computadores quânticos podem produzir resultados aleatórios; por isso, geralmente se coleta uma amostra dos resultados executando o circuito várias vezes. Você pode estimar o valor da observável usando a Estimator classe. Estimator é uma das duas primitivas; a outra é Sampler, que pode ser usada para obter dados de um computador quântico. Esses objetos possuem um run() método que executa a seleção de circuitos, observáveis e parâmetros (se aplicável), utilizando um bloco unificado primitivo ( PUB ).

# Construct the Estimator instance.

estimator = Estimator(mode=backend)
estimator.options.resilience_level = 1
estimator.options.default_shots = 5000

mapped_observables = [
    observable.apply_layout(isa_circuit.layout) for observable in observables
]

# One pub, with one circuit to run against five different observables.
job = estimator.run([(isa_circuit, mapped_observables)])

# Use the job ID to retrieve your job data later
print(f">>> Job ID: {job.job_id()}")

Output:

>>> Job ID: d9bjj3u6hjac73ffpth0

Depois que um trabalho é enviado, você pode esperar até que ele seja concluído na sua instância atual do Python ou usar o job_id para recuperar os dados posteriormente. (Consulte a seção sobre recuperação de tarefas para obter detalhes.)

Após a conclusão da tarefa, examine sua saída por meio do atributo result() da tarefa.

# This is the result of the entire submission.  You submitted one Pub,
# so this contains one inner result (and some metadata of its own).
job_result = job.result()

# This is the result from our single pub, which had six observables,
# so contains information on all six.
pub_result = job.result()[0]
Alternativa: execute o exemplo usando um simulador

Quando você executa seu programa quântico em um dispositivo real, sua carga de trabalho deve aguardar em uma fila antes de ser executada. Para economizar tempo, você pode usar o código a seguir para executar essa pequena carga de trabalho no fake_provider com o modo de Qiskit Runtime teste local. Observe que isso só é possível para um circuito pequeno. Ao ampliar na próxima seção, você precisará usar um dispositivo real.


# Use the following code instead if you want to run on a simulator:

from qiskit_ibm_runtime.fake_provider import FakeBelemV2
backend = FakeBelemV2()
estimator = Estimator(backend)

# Convert to an ISA circuit and layout-mapped observables.

pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(qc)
mapped_observables = [
    observable.apply_layout(isa_circuit.layout) for observable in observables
]

job = estimator.run([(isa_circuit, mapped_observables)])
result = job.result()

# This is the result of the entire submission. You submitted one Pub,
# so this contains one inner result (and some metadata of its own).

job_result = job.result()

# This is the result from our single pub, which had five observables,
# so contains information on all five.

pub_result = job.result()[0]

Etapa 4. Analise os resultados

A etapa de análise é normalmente onde você pode pós-processar seus resultados usando, por exemplo, mitigação de erros de medição ou extrapolação de ruído zero (ZNE). Você pode inserir esses resultados em outro fluxo de trabalho para análise posterior ou preparar um gráfico dos valores e dados principais. Em geral, esta etapa é específica para o seu problema. Para este exemplo, trace cada um dos valores esperados que foram medidos para o nosso circuito.

Os valores esperados e os desvios-padrão das variáveis observáveis que você especificou ao Estimator podem ser consultados nos atributos e PubResult.data.stds do PubResult.data.evs resultado da tarefa. Para obter os resultados do Sampler, use a PubResult.data.meas.get_counts() função, que retornará um dict conjunto de medições na forma de cadeias de bits como chaves e contagens como seus valores correspondentes. Para obter mais informações, consulte o guia de introdução rápida do Sampler.

# Plot the result

values = pub_result.data.evs

errors = pub_result.data.stds

# plotting graph
plt.plot(observables_labels, values, "-o")
plt.xlabel("Observables")
plt.ylabel("Values")
plt.show()

Output:

Output of the previous code cell

Observe que, para os qubits 0 e 1, os valores esperados independentes de X e Z são 0, enquanto as correlações (XX e ZZ) são 1. Esta é uma característica marcante do entrelaçamento quântico.


Escala para um grande número de qubits

Na computação quântica, o trabalho em escala utilitária é crucial para o progresso nessa área. Esse trabalho requer cálculos em uma escala muito maior, trabalhando com circuitos que podem usar mais de 100 qubits e mais de 1000 portas. Este exemplo demonstra como você pode realizar trabalhos em escala utilitária em IBM® QPUs criando e analisando um estado GHZ de 100 qubits. Ele usa o fluxo de trabalho de padrões Qiskit e termina medindo o valor esperado Z0Zi\langle Z_0 Z_i \rangle para cada qubit.

Etapa 1. Mapeie o problema

Escreva uma função que retorne um QuantumCircuit que prepare um nn estado GHZ de -qubit (essencialmente um estado Bell estendido) e, em seguida, use essa função para preparar um estado GHZ de 100 qubits e coletar os observáveis a serem medidos.

def get_qc_for_n_qubit_GHZ_state(n: int) -> QuantumCircuit:
    """This function will create a qiskit.QuantumCircuit (qc)
        for an n-qubit GHZ state.

    Args:
        n (int): Number of qubits in the n-qubit GHZ state

    Returns:
        QuantumCircuit: Quantum circuit that generate the n-qubit GHZ state,
            assuming all qubits start in the 0 state
    """
    if isinstance(n, int) and n >= 2:
        qc = QuantumCircuit(n)
        qc.h(0)
        for i in range(n - 1):
            qc.cx(i, i + 1)
    else:
        raise Exception("n is not a valid input")
    return qc


# Create a new circuit with 100 qubits in the GHZ state
n = 100
qc = get_qc_for_n_qubit_GHZ_state(n)

Em seguida, mapeie para os operadores de interesse. Este exemplo usa os ZZ operadores entre qubits para examinar o comportamento à medida que eles se distanciam. Valores de expectativa cada vez mais imprecisos (corrompidos) entre qubits distantes revelariam o nível de ruído presente.

# ZZII...II, ZIZI...II, ... , ZIII...IZ
operator_strings = [
    "Z" + "I" * i + "Z" + "I" * (n - 2 - i) for i in range(n - 1)
]

operators = [SparsePauliOp(operator) for operator in operator_strings]

Etapa 2. Otimize o problema para execução em hardware quântico

O código a seguir transforma o circuito e os observáveis para corresponder à ISA do backend. É necessário que você já tenha salvo suas credenciais.

service = QiskitRuntimeService()

backend = service.least_busy(
    simulator=False, operational=True, min_num_qubits=100
)
pm = generate_preset_pass_manager(optimization_level=1, backend=backend)

isa_circuit = pm.run(qc)
isa_operators_list = [op.apply_layout(isa_circuit.layout) for op in operators]

Etapa 3. Executar no hardware

Envie o trabalho e ative a supressão de erros utilizando uma técnica chamada "desacoplamento dinâmico" para reduzir os erros. O nível de resiliência especifica o grau de resiliência a ser implementado contra erros. Níveis mais altos geram resultados mais precisos, mas aumentam o tempo de processamento. Para obter mais informações sobre as opções definidas no código a seguir, consulte Configurar a mitigação de erros para o Qiskit Runtime.

options = EstimatorOptions()
options.resilience_level = 1
options.dynamical_decoupling.enable = True
options.dynamical_decoupling.sequence_type = "XY4"

# Create an Estimator object
estimator = Estimator(backend, options=options)
# Submit the circuit to Estimator
job = estimator.run([(isa_circuit, isa_operators_list)])
job_id = job.job_id()
print(job_id)

Output:

d9bk5brv6alc73ct68ag

Etapa 4. Resultados pós-processamento

Após a conclusão do trabalho, trace os resultados e observe que Z0Zi\langle Z_0 Z_i \rangle diminui com o aumento ii de, mesmo que em uma simulação ideal todos Z0Zi\langle Z_0 Z_i \rangle devessem ser 1.

# data
data = list(range(1, len(operators) + 1))  # Distance between the Z operators
result = job.result()[0]
values = result.data.evs  # Expectation value at each Z operator.
values = [
    v / values[0] for v in values
]  # Normalize the expectation values to evaluate how they decay with distance.

# plotting graph
plt.plot(data, values, marker="o", label="100-qubit GHZ state")
plt.xlabel("Distance between qubits $i$")
plt.ylabel(r"$\langle Z_i Z_0 \rangle / \langle Z_1 Z_0 \rangle $")
plt.legend()
plt.show()

Output:

Output of the previous code cell

O gráfico anterior mostra que, à medida que a distância entre os qubits aumenta, o sinal enfraquece devido à presença de ruído.


Próximas etapas

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