Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Saiba como usar o módulo qdk.azurePython para enviar circuitos em formatos específicos para o serviço Azure Quantum. Este artigo mostra como enviar circuitos nos seguintes formatos:
Para mais informações, consulte Circuitos quânticos.
Pré-requisitos
Para desenvolver e executar seus circuitos em Visual Studio Code (VS Code), você deve ter o seguinte:
Uma conta Azure com uma assinatura ativa. Se você não tiver uma conta da Azure, registre-se gratuitamente e inscreva-se em uma assinatura pague conforme o uso.
Um workspace Azure Quantum. Para obter mais informações, consulte Criar um workspace Azure Quantum.
Um ambiente Python com Python e Pip instalados.
O VS Code com as extensões Microsoft Quantum Development Kit (QDK), Python e Jupyter instaladas.
A biblioteca
qdkPython com oazureextra e o pacoteipykernel.python -m pip install --upgrade "qdk[azure]" ipykernel
Criar um novo jupyter notebook e conectar-se ao workspace do Quantum
Para se conectar ao seu workspace em um Jupyter Notebook no VS Code, siga os passos:
No VS Code, abra o menu Exibir e escolha Paleta de Comandos.
Insira Criar: Novo Jupyter Notebook. Um arquivo de Jupyter Notebook vazio é aberto em uma nova guia.
Na primeira célula do notebook, execute o código a seguir. Você pode encontrar a ID do recurso no painel Overview para seu workspace no portal Azure.
from qdk.azure import Workspace workspace = Workspace (resource_id="") # Add your resource ID
Enviar circuitos formatados em QIR
A QIR (Representação Intermediária Quântica) é uma representação intermediária que serve como uma interface comum entre linguagens de programação quântica e plataformas de computação quântica direcionadas. Para obter mais informações, veja Representação intermediária quântica.
Para enviar um circuito formatado por QIR, siga estas etapas:
Crie o circuito QIR. Por exemplo, execute o código a seguir em uma nova célula para criar um circuito de emaranhamento simples.
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 enviar o circuito QIR para um target. Neste exemplo, os formatos de dados de entrada e saída sãoqir.v1emicrosoft.quantum-results.v1, respectivamente.# 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 resultEnvie o circuito de QIR para um Azure Quantum target específico. Por exemplo, para enviar o circuito QIR para o simulador targetIonQ, execute o seguinte código:
target = workspace.get_targets(name="ionq.simulator") result = submit_qir_job(target, QIR_routine, "QIR routine") result
Enviar um circuito com um formato específico do provedor para Azure Quantum
Cada provedor Azure Quantum tem seu próprio formato para representar circuitos quânticos. Você pode enviar circuitos para Azure Quantum em formatos específicos do provedor em vez de linguagens QIR, como Q# ou Qiskit.
Enviar um circuito para o IonQ no formato JSON
O IonQ usa o formato JSON para executar circuitos em seus targets. Para obter mais informações, consulte a documentação do IonQ targets e da API do IonQ.
O exemplo a seguir cria uma superposição entre três qubits no formato JSON.
Em uma nova célula, crie um circuito quântico no formato JSON.
circuit = { "qubits": 3, "circuit": [ { "gate": "h", "target": 0 }, { "gate": "cnot", "control": 0, "target": 1 }, { "gate": "cnot", "control": 0, "target": 2 }, ] }Envie o circuito para o IonQ target. O exemplo a seguir usa o simulador do IonQ, que retorna um objeto
Job.target = workspace.get_targets(name="ionq.simulator") job = target.submit(circuit)Quando o trabalho for concluído, obtenha os resultados.
results = job.get_results() print(results)
Enviar um circuito para PASQAL no formato do SDK do Pulser
Você pode usar o SDK do Pulser para criar sequências de pulso e enviá-las para PASQAL targets.
Instalar o SDK do Pulsser
O Pulser é uma estrutura que permite criar, simular e executar sequências de pulso para dispositivos quânticos neutros. O Pulser foi projetado pela PASQAL como uma passagem para enviar experimentos quânticos para seus processadores quânticos. Para obter mais informações, consulte a documentação do Pulser.
Para enviar as sequências de pulso, primeiro instale os pacotes do 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 registro quântico
Defina um registro e um layout. O registro especifica onde organizar os átomos e o layout especifica as posições das armadilhas que capturam e estruturam os átomos dentro do registro.
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. Você pode criar seu registro a partir de um desses layouts.
Use layouts pré-calibrados quando possível porque eles melhoram o desempenho da QPU.
O exemplo a seguir usa o primeiro layout pré-calibrado no 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 atenderem aos requisitos do experimento.
Para um determinado registro arbitrário, uma QPU de átomos neutros posiciona armadilhas conforme o layout previsto, que depois precisa ser calibrado. Como cada calibragem leva tempo, é uma prática recomendada reutilizar um layout calibrado existente quando possível.
Para criar um layout arbitrário, escolha uma das seguintes opções:
Gere automaticamente um layout com base em um registro especificado. Para registros grandes, esse processo pode produzir soluções sub-ideais. 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 seu registro. Por exemplo, crie um layout arbitrário com 20 armadilhas que são posicionadas aleatoriamente em um 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()
Escreva uma sequência de pulso
Átomos neutros são controlados com pulsos laser. O SDK do Pulsser permite que você crie sequências de pulso para aplicar ao registro quântico.
Defina os atributos de sequência de pulso declarando os canais que controlam os átomos. Para criar uma
Sequence, forneça umaRegisterinstância junto com o dispositivo em que 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")Observação
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 com menos restrições das especificações do dispositivo, o que permite a execução em um emulador. Para obter mais informações, consulte a documentação do Pulser.Adicione pulsos à sua sequência. Para fazer isso, crie e adicione pulsos aos canais que você declarou. Por exemplo, o código a seguir cria um pulso e o adiciona 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 a seguir mostra a sequência de pulso:
Converter a sequência em uma string JSON
Para enviar as sequências de pulso, converta os objetos Pulser em uma cadeia de caracteres 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
Enviar a sequência de pulso para um PASQAL target
Defina os formatos de dados de entrada e saída adequados. Por exemplo, o código a seguir configura o formato de dados de entrada para
pasqal.pulser.v1e o formato de dados de saída parapasqal.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 jobObservação
O tempo necessário para executar um trabalho na QPU depende dos tempos de fila atuais. Você pode visualizar o tempo médio da fila para um target no painel Provedores do seu espaço de trabalho.
Envie o programa para o PASQAL. Antes de enviar seu código para hardware quântico real, é uma prática recomendada testar seu 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 }
Enviar um circuito do OpenQASM para o Quantinuum
Crie um circuito quântico na representação OpenQASM. Por exemplo, o código a seguir 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 carregue o circuito de um arquivo OpenQASM:
with open("my_teleport.qasm", "r") as f: circuit = f.read()Envie o circuito para um Quantinuum target. O exemplo a seguir envia o trabalho para um dos simuladores targetsde Quantinuum.
target = workspace.get_targets(name="quantinuum.sim.h2-1sc") job = target.submit(circuit, shots=500)Aguarde até que o trabalho seja concluído e obtenha os resultados.
results = job.get_results() print(results)
Observação
Esses resultados retornam 000 para cada tiro, o que não é aleatório. Isso ocorre porque o Validador de API verifica apenas se o código pode ser executado no hardware Quantinuum, mas retorna 0 para cada medida quântica. Para um gerador de número aleatório verdadeiro, você precisa executar o circuito no hardware quântico.
Enviar um circuito Quil para a Rigetti
Para enviar um trabalho Quil para 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 para o qual você deseja enviar seu trabalho. Por exemplo, o código a seguir seleciona oQVMtarget.target = Rigetti(workspace=workspace, name=RigettiTarget.QVM)Crie um programa Quil. Para que seu programa seja aceito, você deve definir o parâmetro como
"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 Result com o nome do resultado. No código a seguir,
data_per_shothá uma lista de comprimentonum_shotse cada item na lista é outra lista que contém os dados do registro dessa captura.data_per_shot = result[readout] ro_data_first_shot = data_per_shot[0]Nesse caso, como o tipo do registro é 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
Você não pode enviar vários circuitos em um único trabalho. Como solução alternativa, você pode chamar o método backend.run para enviar cada circuito de forma assíncrona e buscar os resultados de cada tarefa. Por exemplo:
jobs = []
for circuit in circuits:
jobs.append(backend.run(circuit, shots=N))
results = []
for job in jobs:
results.append(job.result())