Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Aprende a usar o módulo qdk.azurePython para submeter circuitos em formatos específicos ao serviço Azure Quantum. Este artigo mostra como enviar circuitos nos seguintes formatos:
Para obter mais informações, consulte Circuitos quânticos.
Pré-requisitos
Para desenvolver e executar os seus circuitos no Visual Studio Code (VS Code), deve ter o seguinte:
Uma conta no Azure com uma subscrição ativa. Se não tiver uma conta Azure, registe-se gratuitamente e inscreva-se numa subscrição pay-as-you-go.
Um espaço de trabalho Azure Quantum. Para mais informações, consulte Criar um espaço de trabalho Azure Quantum.
Um ambiente Python com Python e Pip instalados.
VS Code com as extensões Microsoft Quantum Development Kit (QDK), Python e Jupyter instaladas.
A biblioteca
qdkPython com o extraazuree o pacoteipykernel.python -m pip install --upgrade "qdk[azure]" ipykernel
Crie um novo caderno Jupyter e ligue-se ao seu espaço de trabalho Quantum
Para se ligar ao seu espaço de trabalho num caderno Jupyter no VS Code, siga estes passos:
No VS Code, abra o menu Exibir e escolha Paleta de comandos.
Insira Create: New Jupyter Notebook. Um ficheiro vazio do Jupyter Notebook abre-se num novo separador.
Na primeira célula do caderno, execute o seguinte código. Podes encontrar o ID do recurso no painel Overview do teu espaço de trabalho no portal Azure.
from qdk.azure import Workspace workspace = Workspace (resource_id="") # Add your resource ID
Enviar circuitos formatados em QIR
A Representação Intermédia Quântica (QIR) é uma representação intermédia que serve como interface comum entre linguagens de programação quântica e plataformas de computação quântica direcionadas. Para obter mais informações, consulte Representação intermediária quântica.
Para submeter um circuito com formato QIR, siga estes passos:
Crie o circuito QIR. Por exemplo, execute o código seguinte numa nova célula para criar um circuito simples de emaranhamento.
QIR_routine = """%Result = type opaque %Qubit = type opaque define void @ENTRYPOINT__main() #0 { call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*)) call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*)) call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*)) call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*)) call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 3 to %Qubit*)) call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 3 to %Qubit*)) call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) #1 call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Result* inttoptr (i64 1 to %Result*)) #1 call void @__quantum__rt__tuple_record_output(i64 2, i8* null) call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null) call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null) ret void } declare void @__quantum__qis__ccx__body(%Qubit*, %Qubit*, %Qubit*) declare void @__quantum__qis__cx__body(%Qubit*, %Qubit*) declare void @__quantum__qis__cy__body(%Qubit*, %Qubit*) declare void @__quantum__qis__cz__body(%Qubit*, %Qubit*) declare void @__quantum__qis__rx__body(double, %Qubit*) declare void @__quantum__qis__rxx__body(double, %Qubit*, %Qubit*) declare void @__quantum__qis__ry__body(double, %Qubit*) declare void @__quantum__qis__ryy__body(double, %Qubit*, %Qubit*) declare void @__quantum__qis__rz__body(double, %Qubit*) declare void @__quantum__qis__rzz__body(double, %Qubit*, %Qubit*) declare void @__quantum__qis__h__body(%Qubit*) declare void @__quantum__qis__s__body(%Qubit*) declare void @__quantum__qis__s__adj(%Qubit*) declare void @__quantum__qis__t__body(%Qubit*) declare void @__quantum__qis__t__adj(%Qubit*) declare void @__quantum__qis__x__body(%Qubit*) declare void @__quantum__qis__y__body(%Qubit*) declare void @__quantum__qis__z__body(%Qubit*) declare void @__quantum__qis__swap__body(%Qubit*, %Qubit*) declare void @__quantum__qis__mz__body(%Qubit*, %Result* writeonly) #1 declare void @__quantum__rt__result_record_output(%Result*, i8*) declare void @__quantum__rt__array_record_output(i64, i8*) declare void @__quantum__rt__tuple_record_output(i64, i8*) attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="4" "required_num_results"="2" } attributes #1 = { "irreversible" } ; module flags !llvm.module.flags = !{!0, !1, !2, !3} !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} """Crie uma
submit_qir_jobfunção auxiliar para submeter o circuito QIR a um target. Neste exemplo, os formatos de dados de entrada e saída sãoqir.v1emicrosoft.quantum-results.v1, respetivamente.# Submit the job with proper input and output data formats def submit_qir_job(target, input, name, count=100): job = target.submit( input_data=input, input_data_format="qir.v1", output_data_format="microsoft.quantum-results.v1", name=name, input_params = { "entryPoint": "ENTRYPOINT__main", "arguments": [], "count": count } ) print(f"Queued job: {job.id}") job.wait_until_completed() print(f"Job completed with state: {job.details.status}") #if job.details.status == "Succeeded": result = job.get_results() return resultSubmeta o circuito QIR a um Azure Quantum target específico. Por exemplo, para submeter o circuito QIR ao simulador targetIonQ, execute o seguinte código:
target = workspace.get_targets(name="ionq.simulator") result = submit_qir_job(target, QIR_routine, "QIR routine") result
Submeter um circuito com formato específico de fornecedor ao Azure Quantum
Cada fornecedor Azure Quantum tem o seu próprio formato para representar circuitos quânticos. Pode submeter circuitos ao Azure Quantum em formatos específicos de fornecedores em vez de linguagens QIR, como Q# ou Qiskit.
Submeter um circuito ao IonQ em formato JSON
A IonQ utiliza o formato JSON para executar circuitos no seu targets. Para mais informações, consulte o IonQ targets e a documentação da API IonQ.
O exemplo seguinte cria uma superposição entre três qubits em formato JSON.
Numa célula nova, crie um circuito quântico em formato JSON.
circuit = { "qubits": 3, "circuit": [ { "gate": "h", "target": 0 }, { "gate": "cnot", "control": 0, "target": 1 }, { "gate": "cnot", "control": 0, "target": 2 }, ] }Submeta o circuito ao IonQ target. O exemplo a seguir usa o simulador IonQ, que retorna um
Jobobjeto.target = workspace.get_targets(name="ionq.simulator") job = target.submit(circuit)Quando o trabalho estiver concluído, receba os resultados.
results = job.get_results() print(results)
Submeter um circuito para PASQAL em formato Pulser SDK
Pode usar o Pulser SDK para criar sequências de pulsos e submetê-las ao PASQAL targets.
Instalar o SDK do Pulser
O Pulser é uma estrutura que permite criar, simular e executar sequências de pulsos para dispositivos quânticos de átomos neutros. O Pulser foi concebido pela PASQAL como uma passagem para submeter experiências quânticas aos seus processadores quânticos. Para mais informações, consulte a documentação do Pulser.
Para enviar as sequências de pulso, primeiro instale os pacotes SDK do Pulser:
try:
import pulser
import pulser_pasqal
except ImportError:
!pip -q install pulser pulser-pasqal --index-url https://pypi.org/simple
Criar um registo quântico
Defina tanto um registo como um layout. O registo especifica onde organizar os átomos, e o layout especifica as posições das armadilhas que capturam e estruturam os átomos dentro do registo.
Para obter detalhes sobre layouts, consulte a documentação do Pulser.
Crie um devices objeto para importar o computador targetquântico PASQAL, Fresnel.
from pulser_pasqal import PasqalCloud
devices = PasqalCloud().fetch_available_devices()
QPU = devices["FRESNEL"]
Layouts pré-calibrados
O dispositivo define uma lista de layouts pré-calibrados. Pode construir o seu registo a partir de um destes layouts.
Use layouts pré-calibrados sempre que possível, pois melhoram o desempenho da QPU.
O exemplo seguinte utiliza o primeiro layout pré-calibrado do dispositivo:
# Use the first layout available on the device
layout = QPU.pre_calibrated_layouts[0]
# Select traps 1, 3 and 5 of the layout to define the register
traps = [1,v3,v5]
reg = layout.define_register(*traps)
# Draw the register to verify that it matches your expectations
reg.draw()
Layouts arbitrários
Use um layout personalizado quando os layouts pré-calibrados não satisfizerem os requisitos do seu experimento.
Para um dado registo arbitrário, uma QPU de átomo neutro coloca armadilhas de acordo com a disposição, que depois devem ser calibradas. Como cada calibração demora tempo, é uma boa prática reutilizar um layout calibrado existente sempre que possível.
Para criar um layout arbitrário, escolha uma das seguintes opções:
Gerar automaticamente um layout com base num registo especificado. Para registos grandes, este processo pode produzir soluções subótimas. Por exemplo:
from pulser import Register qubits = { "q0": (0, 0), "q1": (0, 10), "q2": (8, 2), "q3": (1, 15), "q4": (-10, -3), "q5": (-8, 5), } reg = Register(qubits).with_automatic_layout(device)Defina manualmente um layout para criar o seu registo. Por exemplo, crie um layout arbitrário com 20 armadilhas posicionadas aleatoriamente num plano 2D:
import numpy as np from pulser.register.register_layout import RegisterLayout # Generate random coordinates np.random.seed(301122) # Keeps results consistent between runs traps = np.random.randint(0, 30, size=(20, 2)) traps = traps - np.mean(traps, axis=0) # Create the layout layout = RegisterLayout(traps, slug="random_20") # Define your register with specific trap IDs trap_ids = [4, 8, 19, 0] reg = layout.define_register(*trap_ids, qubit_ids=["a", "b", "c", "d"]) reg.draw()
Escrever uma sequência de pulsos
Átomos neutros são controlados por pulsos laser. O SDK do Pulser permite criar sequências de pulso para aplicar ao registro quântico.
Defina os atributos da sequência de pulsos declarando os canais que controlam os átomos. Para criar um
Sequence, forneça umaRegisterinstância juntamente com o dispositivo onde a sequência será executada. Por exemplo, o código a seguir declara um canal:ch0.from pulser import Sequence seq = Sequence(reg, QPU) # Print the available channels for your sequence print(seq.available_channels) # Declare a channel. For example, `rydberg_global` seq.declare_channel("ch0", "rydberg_global")Nota
Você pode usar o
QPU = devices["FRESNEL"]dispositivo ou importar um dispositivo virtual do Pulser para obter mais flexibilidade. O uso de umVirtualDevicepermite a criação de sequências menos limitadas pelas especificações do dispositivo, o que permite correr num emulador. Para obter mais informações, consulte a documentação do Pulser.Adicione pulsos à sua sequência. Para isso, crie e adicione pulsos aos canais que declarou. Por exemplo, o seguinte código cria um pulso e adiciona-o ao canal
ch0:from pulser import Pulse from pulser.waveforms import RampWaveform, BlackmanWaveform import numpy as np amp_wf = BlackmanWaveform(1000, np.pi) det_wf = RampWaveform(1000, -5, 5) pulse = Pulse(amp_wf, det_wf, 0) seq.add(pulse, "ch0") seq.draw()A imagem seguinte mostra a sequência de impulsos:
Converter a sequência em uma cadeia de caracteres JSON
Para submeter as sequências de pulsos, converta os objetos Pulser numa string JSON que pode ser usada como dados de entrada.
import json
# Convert the sequence to a JSON string
def prepare_input_data(seq):
input_data = {}
input_data["sequence_builder"] = json.loads(seq.to_abstract_repr())
to_send = json.dumps(input_data)
return to_send
Submeter a sequência de impulsos a um PASQAL target
Defina os formatos corretos de dados de entrada e saída. Por exemplo, o código a seguir define o formato de dados de entrada como
pasqal.pulser.v1e o formato de dados de saída comopasqal.pulser-results.v1.# Submit the job with proper input and output data formats def submit_job(target, seq, shots): job = target.submit( input_data=prepare_input_data(seq), # Take the JSON string previously defined as input data input_data_format="pasqal.pulser.v1", output_data_format="pasqal.pulser-results.v1", name="PASQAL sequence", shots=shots # Number of shots ) print(f"Queued job: {job.id}") return jobNota
O tempo necessário para executar um trabalho na QPU depende dos tempos de fila atuais. Pode ver o tempo médio de espera para a target no painel de Fornecedores do seu espaço de trabalho.
Submeta o programa ao PASQAL. Antes de submeteres o teu código para hardware quântico real, é uma boa prática testar o teu código no emulador
pasqal.sim.emu-tntarget.target = workspace.get_targets(name="pasqal.sim.emu-tn") # Change to "pasqal.qpu.fresnel" to use Fresnel QPU job = submit_job(target, seq, 10) job.wait_until_completed() print(f"Job completed with state: {job.details.status}") result = job.get_results() print(result){ "1000000": 3, "0010000": 1, "0010101": 1 }
Submeter um circuito OpenQASM ao Quantinuum
Crie um circuito quântico na representação OpenQASM. Por exemplo, o seguinte código cria um circuito de Teletransporte:
circuit = """OPENQASM 2.0; include "qelib1.inc"; qreg q[3]; creg c0[3]; h q[0]; cx q[0], q[1]; cx q[1], q[2]; measure q[0] -> c0[0]; measure q[1] -> c0[1]; measure q[2] -> c0[2]; """Ou, carregar o circuito a partir de um ficheiro OpenQASM:
with open("my_teleport.qasm", "r") as f: circuit = f.read()Submeter o circuito a um Quantinuum target. O exemplo seguinte submete o trabalho a um dos simuladores da Quantinuum targets.
target = workspace.get_targets(name="quantinuum.sim.h2-1sc") job = target.submit(circuit, shots=500)Aguarde até que o trabalho seja concluído e, em seguida, obtenha os resultados.
results = job.get_results() print(results)
Nota
Estes resultados devolvem 000 por cada disparo, o que não é aleatório. Isto porque o API Validator só verifica se o seu código pode correr em hardware Quantinuum, mas retorna 0 para cada medição quântica. Para um verdadeiro gerador de números aleatórios, você precisa executar seu circuito em hardware quântico.
Submeter um circuito Quil à Rigetti
Para submeter um trabalho Quil a um Rigetti target, use o módulo qdk.azurePython.
Carregue as importações necessárias.
from azure.quantum import Workspace from azure.quantum.target.rigetti import Result, Rigetti, RigettiTarget, InputParamsCrie um
targetobjeto e passe o nome do Rigetti target a que você quer submeter o seu trabalho. Por exemplo, o código a seguir seleciona oQVMtarget.target = Rigetti(workspace=workspace, name=RigettiTarget.QVM)Crie um programa Quil. Para que o seu programa seja aceite, deve definir a leitura para
"ro".readout = "ro" bell_state_quil = f""" DECLARE {readout} BIT[2] H 0 CNOT 0 1 MEASURE 0 {readout}[0] MEASURE 1 {readout}[1] """ num_shots = 5 job = target.submit( input_data=bell_state_quil, name="bell state", shots=100, input_params=InputParams(skip_quilc=False) ) print(f"Job completed with state: {job.details.status}") result = Result(job) # This throws an exception if the job failedVocê pode indexar um Resultado com o nome da leitura. No código seguinte,
data_per_shoté uma lista com um comprimento denum_shots, e cada item da lista é outra lista que armazena os dados para o registo dessa captura.data_per_shot = result[readout] ro_data_first_shot = data_per_shot[0]Neste caso, como o tipo do registo é BIT, o tipo é inteiro e o valor é 0 ou 1.
assert isinstance(ro_data_first_shot[0], int) assert ro_data_first_shot[0] == 1 or ro_data_first_shot[0] == 0Imprima todos os dados.
print(f"Data from '{readout}' register:") for i, shot in enumerate(data_per_shot): print(f"Shot {i}: {shot}")
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 obter 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())