Partilhar via


Como enviar um circuito com o Qiskit para o Azure Quantum

Aprenda a submeter um circuito quântico Qiskit com o Azure Quantum Development Kit (QDK). Você pode enviar circuitos Qiskit para o Azure Quantum usando o Azure Quantum Development Kit (QDK) e o Jupyter Notebook no Visual Studio Code (VS Code). Você também pode testar os seus circuitos usando o simulador local disperso. O QDK suporta as versões 1 e 2 do Qiskit.

Para obter mais informações, consulte Circuitos quânticos.

Pré-requisitos

Para obter detalhes da instalação, consulte Configurar a extensão QDK.

  • Um espaço de trabalho do Azure Quantum em sua assinatura do Azure. Para criar um espaço de trabalho, consulte Criar um espaço de trabalho do Azure Quantum.

  • Um Python ambiente com Python e Pip instalado.

  • VS Code com as extensões do Azure Quantum Development Kit, Python e Jupyter instaladas.

  • A qdkPython biblioteca com os extras azure, qiskit e o pacote ipykernel.

    python -m pip install --upgrade "qdk[azure,qiskit]" ipykernel 
    

Criar um novo Jupyter Notebook

  1. No VS Code, abra o menu Exibir e escolha Paleta de comandos.
  2. Insira e selecione Criar: Novo Bloco de Anotações Jupyter.
  3. O VS Code deteta e exibe a versão de Python e o ambiente virtual Python que foi selecionado para o notebook. Se você tiver vários Python ambientes, talvez seja necessário selecionar um kernel usando o seletor de kernel no canto superior direito. Se nenhum ambiente foi detetado, consulte Jupyter Notebooks no VS Code para obter informações de configuração.

Carregue as importações necessárias

Na primeira célula do seu bloco de notas, execute o seguinte código para carregar as importações necessárias:

from qdk.azure import Workspace
from qdk.azure.qiskit import AzureQuantumProvider
from qiskit import QuantumCircuit
from qiskit.visualization import plot_histogram

Conectar-se ao serviço Azure Quantum

Para se conectar ao serviço Azure Quantum, você precisa da ID do recurso e do local do seu espaço de trabalho do Azure Quantum.

  1. Inicie sessão na sua conta Azure, https://portal.azure.com.
  2. Selecione o seu espaço de trabalho Azure Quantum e vá a Visão Geral.
  3. Copie o ID do Recurso e os parâmetros de localização .

Adicione uma nova célula em seu bloco de anotações e use as informações da sua conta para criar Workspace e AzureQuantumProvider objetos para se conectar ao seu espaço de trabalho do Azure Quantum.

workspace = Workspace(  
    resource_id = "", # Add the resourceID of your workspace
    location = "" # Add the location of your workspace (for example "westus")
    )

provider = AzureQuantumProvider(workspace)

Listar todos os back-ends

Agora você pode imprimir todos os back-ends de computação quântica disponíveis em seu espaço de trabalho:

print("This workspace's targets:")
for backend in provider.backends():
    print("- " + backend.name)
This workspace's targets:
- ionq.simulator
- ionq.qpu.aria-1
- ionq.qpu.forte-1
- ionq.qpu.forte-enterprise-1
- quantinuum.sim.h2-1sc
- quantinuum.sim.h2-2sc
- quantinuum.sim.h2-1e
- quantinuum.sim.h2-2e
- quantinuum.qpu.h2-1
- quantinuum.qpu.h2-2
- rigetti.sim.qvm
- rigetti.qpu.ankaa-3
- rigetti.qpu.cepheus-1-36q

Corra um circuito simples

Numa nova célula, crie um circuito Qiskit simples.

# Create a Quantum Circuit acting on the q register
circuit = QuantumCircuit(3, 3)
circuit.name = "Qiskit Sample - 3-qubit GHZ circuit"
circuit.h(0)
circuit.cx(0, 1)
circuit.cx(1, 2)
circuit.measure([0,1,2], [0, 1, 2])

# Print out the circuit
circuit.draw()
     ┌───┐          ┌─┐      
q_0: ┤ H ├──■───────┤M├──────
     └───┘┌─┴─┐     └╥┘┌─┐   
q_1: ─────┤ X ├──■───╫─┤M├───
          └───┘┌─┴─┐ ║ └╥┘┌─┐
q_2: ──────────┤ X ├─╫──╫─┤M├
               └───┘ ║  ║ └╥┘
c: 3/════════════════╩══╩══╩═
                     0  1  2 

Selecione um target para executar o seu programa

Executar no simulador IonQ

Antes de usares o teu circuito em hardware real, testa o teu circuito no simulador. Use get_backend para criar um Backend objeto para se conectar ao back-end do IonQ Simulator:

simulator_backend = provider.get_backend("ionq.simulator")

Os back-ends IonQ suportam portas de um conjunto de portas definido, que são compiladas para serem executadas de forma otimizada no hardware. Se o seu circuito contém portas que não estão nesta lista, é necessário transpilar para o suportado gateset usando a função transpile fornecida pelo Qiskit.

from qiskit import transpile
circuit = transpile(circuit, simulator_backend)

A função transpile retorna um novo objeto de circuito onde as portas são decompostas em portas que são suportadas no backend especificado.

Agora você pode executar o programa por meio do serviço Azure Quantum e obter o resultado. A célula a seguir envia um trabalho que executa o circuito com 100 disparos:

job = simulator_backend.run(circuit, shots=8)
job_id = job.id()
print("Job id", job_id)
Job id 00000000-0000-0000-0000-000000000000

Para aguardar até que o trabalho seja concluído e retornar os resultados, execute:

result = job.result()
print(result)
Result(backend_name='ionq.simulator', backend_version='1', qobj_id='Qiskit Sample - 3-qubit GHZ circuit', job_id='00000000-0000-0000-0000-000000000000', success=True, results=[ExperimentResult(shots=8, success=True, meas_level=2, data=ExperimentResultData(counts={'000': 4, '111': 4}, memory=['000', '000', '000', '000', '111', '111', '111', '111'], probabilities={'000': 0.5, '111': 0.5}), header=QobjExperimentHeader(name='Qiskit Sample - 3-qubit GHZ circuit', num_qubits=3, metadata={}), status=JobStatus.DONE, name='Qiskit Sample - 3-qubit GHZ circuit')], date=None, status=None, header=None, error_data=None)

Como o resultado é um tipo de objeto específico do pacote Qiskit, use result.get_counts e plot_histogram para visualizar os resultados. Para representar todas as possíveis etiquetas de cadeia de bits, adicione as etiquetas a counts.

counts = {format(n, "03b"): 0 for n in range(8)}
counts.update(result.get_counts(circuit))
print(counts)
plot_histogram(counts)
{'000': 4, '001': 0, '010': 0, '011': 0, '100': 0, '101': 0, '110': 0, '111': 4}

Resultado do circuito Qiskit no IonQ Simulator

Também pode usar a função get_memory para mostrar dados individuais dos disparos da tarefa:

result.get_memory(circuit)
['000', '000', '000', '000', '111', '111', '111', '111']

Nota

No IonQ targets, se você enviar um trabalho com um número ímpar de disparos, o número de tiros será arredondado para baixo para o número par mais próximo. Por exemplo, se especificar 9 disparos, então os resultados mostram dados de 8 disparos.

Estimar o custo do trabalho

Estima quanto custa a execução de um trabalho antes de o executar na QPU.

Para obter os detalhes de preços mais atuais, consulte Preços IonQ ou encontre seu espaço de trabalho e visualize as opções de preços na guia Provedor do seu espaço de trabalho via: aka.ms/aq/myworkspaces.

Executar em IonQ QPU

Para se conectar ao hardware real (uma Unidade de Processamento Quântico (QPU)), basta fornecer o nome do target"ionq.qpu.aria-1" ao método get_backend.

qpu_backend = provider.get_backend("ionq.qpu.aria-1")

Submete o circuito para correr no Azure Quantum, obtém os resultados e depois executa plot_histogram para traçar os resultados.

Nota

O tempo necessário para executar um circuito na QPU depende dos tempos de fila atuais.

# Submit the circuit to run on Azure Quantum
job = qpu_backend.run(circuit, shots=100)
job_id = job.id()
print("Job id", job_id)

# Get the job results (this method waits for the Job to complete):
result = job.result()
print(result)
counts = {format(n, "03b"): 0 for n in range(8)}
counts.update(result.get_counts(circuit))
print(counts)
plot_histogram(counts)
Job id 00000000-0000-0000-0000-000000000000
Job Status: job has successfully run
Result(backend_name='ionq.qpu.aria-1', backend_version='1', qobj_id='Qiskit Sample - 3-qubit GHZ circuit', job_id='00000000-0000-0000-0000-000000000000', success=True, results=[ExperimentResult(shots=1024, success=True, meas_level=2, data=ExperimentResultData(counts={'0': 505, '1': 6, '2': 1, '3': 1, '4': 1, '5': 10, '6': 11, '7': 488}, probabilities={'0': 0.4932, '1': 0.0059, '2': 0.001, '3': 0.001, '4': 0.001, '5': 0.0098, '6': 0.0117, '7': 0.4766}), header=QobjExperimentHeader(name='Qiskit Sample - 3-qubit GHZ circuit', num_qubits='3', qiskit='True'))])
{'000': 505, '001': 6, '010': 1, '011': 1, '100': 1, '101': 10, '110': 11, '111': 488}

Resultado do circuito Qiskit na QPU IonQ

Importante

Não podes submeter vários circuitos num único trabalho. Como solução alternativa, podes chamar o backend.run método para submeter cada circuito de forma assíncrona e depois buscar os resultados de cada trabalho. Por exemplo:

jobs = []
for circuit in circuits:
    jobs.append(backend.run(circuit, shots=N))

results = []
for job in jobs:
    results.append(job.result())

Pré-requisitos

Para obter detalhes da instalação, consulte Configurar a extensão QDK.

Corra um circuito básico

No VS Code, abre um novo Python ficheiro para criar e executar um circuito básico com o simulador sparse incorporado do qsharp módulo.

# load the required imports 
from qdk import qsharp
from qsharp.interop.qiskit import QSharpBackend
from qiskit.circuit.random import random_circuit

# define and display the circuit
circuit = random_circuit(2, 2, measure=True)
print(circuit)

# run the circuit using the built-in sparse simulator
backend = QSharpBackend()
job = backend.run(circuit)
counts = job.result().get_counts()

print(counts)

Para executar o programa, selecione o ícone Executar no canto superior direito e selecione Executar Python arquivo. A saída é exibida em uma nova janela do terminal.

                  ┌─────────────────────────┐┌─┐
q_0: ─■───────────┤0                        ├┤M├───
      │P(0.79983) │  (XX-YY)(1.9337,1.7385) │└╥┘┌─┐
q_1: ─■───────────┤1                        ├─╫─┤M├
                  └─────────────────────────┘ ║ └╥┘
c: 2/═════════════════════════════════════════╩══╩═
                                              0  1
{'11': 680, '00': 344}

Gerar QIR para o circuito

A partir desse mesmo circuito, você pode gerar QIR que é usado para rodar em hardware quântico.

Nota

Para gerar QIR, todos os registros devem ser medidos. Se houver algum registro não utilizado, um erro será gerado. Além disso, você recebe um erro quando tenta gerar QIR com um Unrestrictedtarget perfil. O perfil Unrestricted só é válido para simulação. Você deve usar TargetProfile.Base, TargetProfile.Adaptive_RI, ou TargetProfile.Adaptive_RIF. Você pode substituir o target_profile durante a chamada backend.qir(...) para mudar perfis.

  1. Importar QSharpError e TargetProfile

    from qdk.qsharp import QSharpError, TargetProfile
    
  2. Para gerar QIR, modifique a saída:

    print(backend.qir(circuit, target_profile=TargetProfile.Adaptive_RI))
    

Seu código agora deve ter esta aparência:

# load the required imports 
from qdk import qsharp
from qsharp import QSharpError, TargetProfile
from qsharp.interop.qiskit import QSharpBackend
from qiskit.circuit.random import random_circuit

# define and display the circuit
circuit = random_circuit(2, 2, measure=True)
print(circuit)

# generate QIR for the circuit
print(backend.qir(circuit, target_profile=TargetProfile.Adaptive_RI))

A saída do seu código deve ter esta aparência:

     ┌────────────┐             ┌─┐   
q_0: ┤ Rx(2.7195) ├─■───────────┤M├───
     └──┬─────┬───┘ │U1(5.5924) └╥┘┌─┐
q_1: ───┤ Tdg ├─────■────────────╫─┤M├
        └─────┘                  ║ └╥┘
c: 2/════════════════════════════╩══╩═
                                 0  1
%Result = type opaque
%Qubit = type opaque

define void @ENTRYPOINT__main() #0 {
block_0:
  call void @__quantum__qis__rx__body(double 2.7194945105768586, %Qubit* inttoptr (i64 0 to %Qubit*))
  call void @__quantum__qis__rz__body(double 2.796204066686262, %Qubit* inttoptr (i64 0 to %Qubit*))
  call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 1 to %Qubit*))
  call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*))
  call void @__quantum__qis__rz__body(double -2.796204066686262, %Qubit* inttoptr (i64 1 to %Qubit*))
  call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*))
  call void @__quantum__qis__rz__body(double 2.796204066686262, %Qubit* inttoptr (i64 1 to %Qubit*))
  call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
  call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
  call void @__quantum__rt__array_record_output(i64 2, i8* null)
  call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null)
  call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
  ret void
}

declare void @__quantum__qis__rx__body(double, %Qubit*)

declare void @__quantum__qis__rz__body(double, %Qubit*)

declare void @__quantum__qis__t__adj(%Qubit*)

declare void @__quantum__qis__cx__body(%Qubit*, %Qubit*)

declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1

declare void @__quantum__rt__array_record_output(i64, i8*)

declare void @__quantum__rt__result_record_output(%Result*, i8*)

attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="2" "required_num_results"="2" }
attributes #1 = { "irreversible" }

; module flags

!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10}

!0 = !{i32 1, !"qir_major_version", i32 1}
!1 = !{i32 7, !"qir_minor_version", i32 0}
!2 = !{i32 1, !"dynamic_qubit_management", i1 false}
!3 = !{i32 1, !"dynamic_result_management", i1 false}
!4 = !{i32 1, !"classical_ints", i1 true}
!5 = !{i32 1, !"qubit_resetting", i1 true}
!6 = !{i32 1, !"classical_floats", i1 false}
!7 = !{i32 1, !"backwards_branching", i1 false}
!8 = !{i32 1, !"classical_fixed_points", i1 false}
!9 = !{i32 1, !"user_functions", i1 false}
!10 = !{i32 1, !"multiple_target_branching", i1 false}

Nem todos os programas podem ser executados em todo o hardware. Aqui, se tentar aceder ao perfil targetBase, obterá erros detalhados sobre quais partes do programa não são suportadas.

try:
    backend.qir(qc, target_profile=TargetProfile.Base)
except QSharpError as e:
    print(e)

Próximos passos