Como enviar circuitos formatados específicos para o Azure Quantum
Saiba como usar o azure-quantum
Python pacote para enviar circuitos em formatos específicos para o serviço Azure Quantum. Este artigo mostra como enviar circuitos nos seguintes formatos:
Para obter mais informações, consulte Circuitos quânticos.
Nota
O Microsoft Quantum Development Kit (Classic QDK) não será mais suportado após 30 de junho de 2024. Se você for um desenvolvedor QDK existente, recomendamos que faça a transição para o novo Kit de Desenvolvimento Quântico do Azure (QDK Moderno) para continuar desenvolvendo soluções quânticas. Para obter mais informações, consulte Migrar seu código Q# para o QDK moderno.
Pré-requisitos
Para executar seus circuitos em um Bloco de Anotações no portal do Azure, você precisa:
- Uma conta do Azure com uma subscrição ativa. Se não tiver uma conta do Azure, registe-se gratuitamente e inscreva-se numa subscrição pré-paga.
- Um espaço de trabalho do Azure Quantum. Para obter mais informações, consulte Criar um espaço de trabalho do Azure Quantum.
Para desenvolver e executar seus circuitos no Visual Studio Code, você também precisa:
Um Python ambiente com Python e Pip instalado.
VS Code com o Azure Quantum Development Kit Pythone as extensões Jupyter instaladas.
O Azure Quantum
qsharp
,azure-quantum
eipykernel
pacotes.python -m pip install --upgrade qsharp azure-quantum ipykernel
Criar um novo Jupyter Notebook
Você pode criar um bloco de anotações no VS Code ou diretamente no portal do Azure Quantum.
- Faça logon no portal do Azure e selecione o espaço de trabalho na etapa anterior.
- Na folha esquerda, selecione Blocos de Notas.
- Clique em Meus Blocos de Anotações e clique em Adicionar Novo.
- Em Tipo de kernel, selecione IPython.
- Digite um nome para o arquivo e clique em Criar arquivo.
Quando o novo Bloco de Anotações é aberto, ele cria automaticamente o código para a primeira célula, com base nas informações da assinatura e do espaço de trabalho.
from azure.quantum import Workspace
workspace = Workspace (
resource_id = "", # Your resource_id
location = "" # Your workspace location (for example, "westus")
)
Enviar circuitos formatados em QIR
Quantum Intermediate Representation (QIR) é uma representação intermediária que serve como uma interface comum entre linguagens/frameworks de programação quântica e targetplataformas de computação quântica. Para obter mais informações, consulte Representação intermediária quântica.
Crie o circuito QIR. Por exemplo, o código a seguir cria 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_job
função auxiliar para submeter o circuito QIR a um target. Observe que os formatos de dados de entrada e saída são especificados comoqir.v1
emicrosoft.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 result
Selecione um target e envie o circuito QIR para o Azure Quantum. Por exemplo, para submeter o circuito QIR ao simulador targetIonQ:
target = workspace.get_targets(name="ionq.simulator") result = submit_qir_job(target, QIR_routine, "QIR routine") result
{'Histogram': ['(0, 0)', 0.5, '(1, 1)', 0.5]}
Enviar um circuito com um formato específico do provedor para o Azure Quantum
Além das linguagens QIR, como Q# ou Qiskit, você pode enviar circuitos quânticos em formatos específicos do provedor para o Azure Quantum. Cada provedor tem seu próprio formato para representar circuitos quânticos.
Enviar um circuito para IonQ usando o formato JSON
Crie um circuito quântico usando o formato JSON independente de linguagem suportado pelo IonQtargets, conforme descrito na documentação da API IonQ. Por exemplo, o exemplo a seguir cria uma superposição entre três qubits:
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
Job
objeto.target = workspace.get_targets(name="ionq.simulator") job = target.submit(circuit)
Aguarde até que o trabalho seja concluído e, em seguida, obtenha os resultados.
results = job.get_results() print(results)
..... {'duration': 8240356, 'histogram': {'0': 0.5, '7': 0.5}}
Em seguida, você pode visualizar os resultados usando Matplotlib.
import pylab as pl pl.rcParams["font.size"] = 16 hist = {format(n, "03b"): 0 for n in range(8)} hist.update({format(int(k), "03b"): v for k, v in results["histogram"].items()}) pl.bar(hist.keys(), hist.values()) pl.ylabel("Probabilities")
Antes de executar um trabalho na QPU, você pode estimar quanto custará para ser executado. Para estimar o custo de execução de um trabalho na QPU, você pode usar o
estimate_cost
método:target = workspace.get_targets(name="ionq.qpu") cost = target.estimate_cost(circuit, shots=500) print(f"Estimated cost: {cost.estimated_total}")
Isto imprime o custo estimado em dólares americanos.
Nota
Para obter os detalhes de preços mais atuais, consulte Preços IonQ ou encontre seu espaço de trabalho e veja as opções de preços na guia "Provedor" do seu espaço de trabalho via: aka.ms/aq/myworkspaces.
Enviar um circuito para PASQAL usando o SDK do Pulser
Para enviar um circuito para o PASQAL, você pode usar o SDK do Pulser para criar sequências de pulso e enviá-las para o PASQAL target.
Instalar o SDK do Pulser
Pulser é uma estrutura para compor, simular e executar sequências de pulso para dispositivos quânticos de átomos neutros. Ele foi projetado pela PASQAL como uma passagem para submeter experimentos quânticos aos 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 SDK do Pulser:
try:
import pulser
except ImportError:
!pip -q install pulser
!pip -q install pulser-core
Criar um registo quântico
Primeiro, carregue as importações necessárias:
import numpy as np import pulser from pprint import pprint from pulser import Pulse, Sequence, Register
A QPU de PASQAL é feita de átomos neutros presos em posições bem definidas em uma rede. Para definir seus registros quânticos, você cria uma matriz de qubits em uma rede. Por exemplo, o código a seguir cria uma rede quadrada 4x4 de qubits:
L = 4 square = np.array([[i, j] for i in range(L) for j in range(L)], dtype=float) square -= np.mean(square, axis=0) square *= 5 qubits = dict(enumerate(square)) reg = Register(qubits) reg.draw()
Escrever uma sequência de pulsos
Os átomos neutros são controlados com pulsos de laser. O SDK do Pulser permite criar sequências de pulso para aplicar ao registro quântico.
Primeiro, você precisa configurar uma sequência de pulsos e declarar os canais que serão usados para controlar os átomos. Por exemplo, o código a seguir declara dois canais:
ch0
ech1
.from pulser.devices import Chadoq2 seq = Sequence(reg, Chadoq2) seq.declare_channel("ch0", "raman_local") print("Available channels after declaring 'ch0':") pprint(seq.available_channels) seq.declare_channel("ch1", "rydberg_local", initial_target=4) print("\nAvailable channels after declaring 'ch1':") pprint(seq.available_channels)
Algumas coisas a considerar:
- A
Sequence
em Pulser é uma série de operações que devem ser executadas em um registro quântico. - O código configura uma sequência de operações a serem executadas em um
AnalogDevice
dispositivo.AnalogDevice
é um dispositivo predefinido em Pulser que representa um computador quântico equivalente a Fresnel1.
- A
Crie uma sequência de pulsos. Para fazer isso, você cria e adiciona pulsos aos canais que você declarou. Por exemplo, o código a seguir cria um pulso simples e o adiciona ao canal
ch0
e, em seguida, cria um pulso complexo e o adiciona ao canalch1
.seq.target(1, "ch0") # Target qubit 1 with channel "ch0" simple_pulse = Pulse.ConstantPulse(200, 2, -10, 0) seq.add(simple_pulse, "ch0") # Add the pulse to channel "ch0" seq.delay(100, "ch1") from pulser.waveforms import RampWaveform, BlackmanWaveform duration = 1000 # Create a Blackman waveform with a duration of 1000 ns and an area of pi/2 rad amp_wf = BlackmanWaveform(duration, np.pi / 2) # Create a ramp waveform with a duration of 1000 ns and a linear sweep from -20 to 20 rad/µs detuning_wf = RampWaveform(duration, -20, 20) # Create a pulse with the amplitude waveform amp_wf, the detuning waveform detuning_wf, and a phase of 0 rad. complex_pulse = Pulse(amp_wf, detuning_wf, phase=0) complex_pulse.draw() seq.add(complex_pulse, "ch1") # Add the pulse to channel "ch1"
A imagem mostra o pulso simples e complexo.
Converter a sequência em uma cadeia de caracteres JSON
Para enviar as sequências de pulso, você precisa converter 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)
#print(json.dumps(input_data, indent=4, sort_keys=True))
return to_send
Submeter a sequência de pulsos ao PASQAL target
Primeiro, você precisa definir os formatos de dados de entrada e saída adequados. Por exemplo, o código a seguir define o formato de dados de entrada como
pasqal.pulser.v1
e 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): 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=100 # Number of shots ) print(f"Queued job: {job.id}") job.wait_until_completed() print(f"Job completed with state: {job.details.status}") result = job.get_results() return result
Nota
O tempo necessário para executar um circuito na QPU depende dos tempos de fila atuais. Você pode exibir o tempo médio de fila para um target selecionando a folha Provedores do seu espaço de trabalho.
Submeta o programa ao PASQAL. Por exemplo, você pode enviar o programa para PASQAL Emu-TN target.
target = workspace.get_targets(name="pasqal.sim.emu-tn") submit_job(target, seq)
{'0000000000000000': 59, '0000100000000000': 39, '0100000000000000': 1, '0100100000000000': 1}
Envie um circuito para a Quantinuum usando o OpenQASM
Crie um circuito quântico na representação OpenQASM. Por exemplo, o exemplo 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]; """
Opcionalmente, você pode carregar o circuito de um arquivo:
with open("my_teleport.qasm", "r") as f: circuit = f.read()
Submeta o circuito ao Quantinuum target. O exemplo a seguir usa o validador de API Quantinuum, que retorna um
Job
objeto.target = workspace.get_targets(name="quantinuum.sim.h1-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)
........ {'c0': ['000', '000', '000', '000', '000', '000', '000', ... ]}
Em seguida, você pode visualizar os resultados usando Matplotlib.
import pylab as pl pl.hist(results["c0"]) pl.ylabel("Counts") pl.xlabel("Bitstring")
Olhando para o histograma, você pode notar que o gerador de números aleatórios retornou 0 todas as vezes, o que não é muito aleatório. Isso ocorre porque, embora o validador de API garanta que seu código será executado com êxito no hardware Quantinuum, ele também 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.
Antes de executar um trabalho na QPU, você pode estimar quanto custará para ser executado. Para estimar o custo de execução de um trabalho na QPU, você pode usar o
estimate_cost
método.target = workspace.get_targets(name="quantinuum.qpu.h1-1") cost = target.estimate_cost(circuit, shots=500) print(f"Estimated cost: {cost.estimated_total}")
Isso imprime o custo estimado em Créditos Quânticos do Sistema H (HQCs).
Nota
Para executar uma estimativa de custo em relação a um Quantinuum target, você deve primeiro recarregar o pacote azure-quantumPython com o parâmetro [qiskit] e garantir que você tenha a versão mais recente do Qiskit. Para obter mais informações, consulte Atualizar o pacote azure-quantumPython.
Nota
Para obter os detalhes de preços mais atuais, consulte Preços do Azure Quantum ou encontre seu espaço de trabalho e exiba as opções de preços na guia "Provedor" do seu espaço de trabalho via: aka.ms/aq/myworkspaces.
Envie um circuito para Rigetti usando Quil
A maneira mais fácil de enviar trabalhos Quil é usando o pacote pyquil-for-azure-quantum , pois ele permite que você use as ferramentas e a documentação da biblioteca pyQuil . Sem esse pacote, o pyQuil pode ser usado para construir programas Quil, mas não para enviá-los ao Azure Quantum.
Você também pode construir programas Quil manualmente e enviá-los usando o azure-quantum
pacote diretamente.
Primeiro, carregue as importações necessárias.
from pyquil.gates import CNOT, MEASURE, H from pyquil.quil import Program from pyquil.quilbase import Declare from pyquil_for_azure_quantum import get_qpu, get_qvm
Use a
get_qvm
função ouget_qpu
para obter uma conexão com a QVM ou QPU.qc = get_qvm() # For simulation # qc = get_qpu("Ankaa-2") for submitting to a QPU
Crie um programa Quil. Qualquer programa Quil válido é aceito, mas a leitura deve ser nomeada.
ro
program = Program( Declare("ro", "BIT", 2), H(0), CNOT(0, 1), MEASURE(0, ("ro", 0)), MEASURE(1, ("ro", 1)), ).wrap_in_numshots_loop(5) # Optionally pass to_native_gates=False to .compile() to skip the compilation stage result = qc.run(qc.compile(program)) data_per_shot = result.readout_data["ro"]
Aqui,
data_per_shot
é umanumpy
matriz, para que você possa usarnumpy
métodos.assert data_per_shot.shape == (5, 2) ro_data_first_shot = data_per_shot[0] assert ro_data_first_shot[0] == 1 or ro_data_first_shot[0] == 0
Imprima todos os dados.
print("Data from 'ro' register:") for i, shot in enumerate(data_per_shot): print(f"Shot {i}: {shot}")
Importante
Atualmente, não há suporte para o envio de vários circuitos em um único trabalho. Como solução alternativa, você pode chamar o método para enviar cada circuito de forma assíncrona backend.run
e, em seguida, 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())
Conteúdos relacionados
Comentários
https://aka.ms/ContentUserFeedback.
Brevemente: Ao longo de 2024, vamos descontinuar progressivamente o GitHub Issues como mecanismo de feedback para conteúdos e substituí-lo por um novo sistema de feedback. Para obter mais informações, veja:Submeter e ver comentários