Cara mengirimkan sirkuit berformat tertentu ke Azure Quantum

Pelajari cara menggunakan modul qdk.azurePython untuk mengirimkan sirkuit dalam format tertentu ke layanan Azure Quantum. Artikel ini memperlihatkan kepada Anda cara mengirimkan sirkuit dalam format berikut:

Untuk informasi selengkapnya, lihat Sirkuit kuantum.

Prasyarat

Untuk mengembangkan dan menjalankan sirkuit Anda di Visual Studio Code (VISUAL Code), Anda harus memiliki hal berikut:

Membuat notebook Jupyter baru dan menyambungkan ke ruang kerja Quantum Anda

Untuk menyambungkan ke ruang kerja Anda di buku catatan Jupyter di Visual Studio Code, ikuti langkah-langkah berikut:

  1. Di Visual Studio Code, buka menu Tampilan dan pilih Palet Perintah.

  2. Masukkan Buat: Jupyter Notebook baru. File Jupyter Notebook kosong terbuka di tab baru.

  3. Di sel pertama buku catatan, jalankan kode berikut. Anda dapat menemukan ID sumber daya di panel Overview untuk ruang kerja Anda di portal Azure.

    from qdk.azure import Workspace
    
    workspace = Workspace (resource_id="") # Add your resource ID 
    

Mengirimkan sirkuit berformat QIR

Quantum Intermediate Representation (QIR) adalah representasi perantara yang berfungsi sebagai antarmuka umum antara bahasa pemrograman kuantum dan platform komputasi kuantum yang ditargetkan. Untuk informasi selengkapnya, lihat Representasi Perantara Quantum.

Untuk mengirimkan sirkuit berformat QIR, ikuti langkah-langkah berikut:

  1. Buat sirkuit QIR. Misalnya, jalankan kode berikut di sel baru untuk membuat sirkuit entanglemen sederhana.

    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}
    """
    
  2. Buat submit_qir_job fungsi pembantu untuk mengirimkan sirkuit QIR ke target. Dalam contoh ini, format data input dan output adalah qir.v1 dan microsoft.quantum-results.v1, masing-masing.

    # 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
    
  3. Kirim sirkuit QIR ke Azure Quantum target tertentu. Misalnya, untuk mengirimkan sirkuit QIR ke simulator targetIonQ , jalankan kode berikut:

    target = workspace.get_targets(name="ionq.simulator") 
    result = submit_qir_job(target, QIR_routine, "QIR routine")
    result
    

Mengirimkan sirkuit dengan format khusus penyedia ke Azure Quantum

Setiap penyedia Azure Quantum memiliki formatnya sendiri untuk mewakili sirkuit kuantum. Anda dapat mengirimkan sirkuit ke Azure Quantum dalam format khusus penyedia, bukan bahasa QIR, seperti Q# atau Qiskit.

Mengirimkan sirkuit ke IonQ dalam format JSON

IonQ menggunakan format JSON untuk menjalankan sirkuit di targets. Untuk informasi selengkapnya, lihat dokumentasi IonQ targets dan IonQ API.

Sampel berikut membuat superposisi antara tiga qubit dalam format JSON.

  1. Di sel baru, buat sirkuit kuantum dalam format JSON.

    circuit = {
        "qubits": 3,
        "circuit": [
            {
            "gate": "h",
            "target": 0
            },
            {
            "gate": "cnot",
            "control": 0,
            "target": 1
            },
            {
            "gate": "cnot",
            "control": 0,
            "target": 2
            },
        ]
    }
    
  2. Kirim sirkuit ke IonQ target. Contoh berikut menggunakan simulator IonQ, yang mengembalikan objek Job.

    target = workspace.get_targets(name="ionq.simulator")
    job = target.submit(circuit)
    
  3. Ketika pekerjaan selesai, dapatkan hasilnya.

    results = job.get_results()
    print(results)
    

Mengirimkan sirkuit ke Pasqal dalam format Pulser SDK

Anda dapat menggunakan Pulser SDK untuk membuat urutan pulsa dan mengirimkannya ke Pasqal targets.

Menginstal SDK Pulser

Pulser adalah kerangka kerja yang memungkinkan Anda membuat, mensimulasikan, dan menjalankan urutan pulsa untuk perangkat kuantum netral-atom. Pulser dirancang oleh PASQAL sebagai pass-through untuk mengirimkan eksperimen kuantum ke prosesor kuantum mereka. Untuk informasi selengkapnya, lihat dokumentasi Pulser.

Untuk mengirimkan urutan pulsa, pertama-tama instal paket Pulser SDK:

try:
    import pulser
    import pulser_azure
except ImportError:
    !pip -q install pulser pulser-azure --index-url https://pypi.org/simple

Membuat register kuantum

Tentukan register dan tata letak. Register menentukan tempat untuk mengatur atom, dan tata letak menentukan posisi perangkap yang menangkap dan menyusun atom dalam register.

Untuk detail tentang tata letak, lihat dokumentasi Pulser.

Buat devices objek untuk mengimpor komputer targetkuantum Pasqal , FRESNEL_CAN1.

from pulser_azure import AzureConnection

connection = AzureConnection(resource_id="") # Add your resource ID
devices = connection.fetch_available_devices()
QPU = devices["pasqal.qpu.fresnel-can1"]
Menyiapkan tata letak Anda

Gunakan tata letak kustom saat tata letak yang telah dikalibrasi sebelumnya tidak memenuhi persyaratan eksperimen Anda.

Untuk register arbitrer tertentu, QPU netral-atom menempatkan perangkap sesuai dengan tata letak, yang kemudian harus dikalibrasi. Karena setiap kalibrasi membutuhkan waktu, ini adalah praktik terbaik untuk menggunakan kembali tata letak kalibrasi yang ada jika memungkinkan.

Untuk membuat tata letak arbitrer, pilih salah satu opsi berikut:

  • Secara otomatis menghasilkan tata letak berdasarkan register tertentu. Untuk register besar, proses ini dapat menghasilkan solusi sub-optimal. Contohnya:

    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(QPU) 
    
  • Untuk menentukan tata letak secara manual untuk membuat register Anda, lihat dokumentasi Pulser.

Membuat rangkaian pulsa

Atom netral dikontrol dengan pulsa laser. Pulser SDK memungkinkan Anda membuat urutan pulsa untuk diterapkan ke register kuantum.

  1. Tentukan atribut urutan pulsa dengan mendeklarasikan saluran yang mengontrol atom. Untuk membuat Sequence, berikan Register instans bersama dengan perangkat tempat urutan akan dijalankan. Misalnya, kode berikut mendeklarasikan satu saluran: 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")
    

    Catatan

    Anda dapat menggunakan QPU = devices["pasqal.qpu.fresnel-can1"] perangkat atau mengimpor perangkat virtual dari Pulser untuk lebih banyak fleksibilitas. Penggunaan VirtualDevice memungkinkan pembuatan urutan yang kurang dibatasi oleh spesifikasi perangkat, yang memungkinkan Anda berjalan pada emulator. Untuk informasi selengkapnya, lihat dokumentasi Pulser.

  2. Tambahkan pulsa ke urutan Anda. Untuk melakukannya, buat dan tambahkan pulsa ke saluran yang telah Anda tetapkan. Misalnya, kode berikut membuat pulsa dan menambahkannya ke saluran 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()
    

    Gambar berikut menunjukkan urutan pulsa:

    Rangkaian pulsa

Mengonversi urutan menjadi string JSON

Untuk mengirimkan urutan pulsa, konversikan objek Pulser menjadi string JSON yang dapat digunakan sebagai data input.

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

Kirim urutan pulsa ke Pasqal target

  1. Atur format data input dan output yang tepat. Misalnya, kode berikut mengatur format data input ke pasqal.pulser.v1 dan format data output ke pasqal.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 job
    

    Catatan

    Waktu yang diperlukan untuk menjalankan pekerjaan pada QPU tergantung pada waktu antrean saat ini. Anda dapat melihat waktu antrean rata-rata untuk target di panel Penyedia ruang kerja Anda.

  2. Kirim program ke Pasqal. Sebelum Anda mengirimkan kode Anda ke perangkat keras kuantum nyata, ini adalah praktik terbaik untuk menguji kode Anda di emulator pasqal.sim.emu-mpstarget.

    target = workspace.get_targets(name="pasqal.sim.emu-mps") # Change to "pasqal.qpu.fresnel-can1" to use FRESNEL_CAN1 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)
    
    {
        "counter": {
            "1000000": 8, 
            "0010000": 1, 
            "0010101": 1
        }
    }
    

Gunakan backend pulser untuk menjalankan urutan

Dengan AzureConnection dari pulser-azure, Anda dapat langsung menggunakan backend QPU atau backend emulator jarak jauh untuk mengeksekusi sekuens.

from pulser.backends import RemoteMPSBackend, QPUBackend

backend = RemoteMPSBackend(sequence=seq, connection=connection)  # Replace RemoteMPSBackend with QPUBackend to execute the sequence on the QPU
results = backend.run(job_params=[{"runs": 10}], wait=True)

print(results.results[0].final_bitstrings)
Counter({"1000000": 8, "0010000": 1, "0010101": 1})

Mengirimkan sirkuit OpenQASM ke Quantinuum

  1. Buat sirkuit kuantum dalam representasi OpenQASM. Misalnya, kode berikut membuat sirkuit Teleportasi:

    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];
    """
    

    Atau, muat sirkuit dari file OpenQASM:

    with open("my_teleport.qasm", "r") as f:
        circuit = f.read()
    
  2. Kirimkan sirkuit ke Quantinuum target. Contoh di bawah ini mengirimkan pekerjaan ke salah satu simulator Quantinuum targets.

    target = workspace.get_targets(name="quantinuum.sim.h2-1sc")
    job = target.submit(circuit, shots=500)
    
  3. Tunggu sampai pekerjaan selesai dan kemudian ambil hasilnya.

    results = job.get_results()
    print(results)
    

Catatan

Hasil ini mengembalikan 000 untuk setiap pengambilan gambar, yang bukan acak. Ini karena Validator API hanya memeriksa apakah kode Anda dapat berjalan pada perangkat keras Quantinuum, tetapi mengembalikan 0 untuk setiap pengukuran kuantum. Untuk generator angka acak nyata, Anda harus menjalankan sirkuit di perangkat keras kuantum.

Kirimkan sirkuit Quil ke Rigetti

Untuk mengirimkan pekerjaan Quil ke Rigetti target, gunakan modul qdk.azurePython.

  1. Muat impor yang diperlukan.

    from azure.quantum import Workspace
    from azure.quantum.target.rigetti import Result, Rigetti, RigettiTarget, InputParams
    
  2. Buat target objek dan berikan nama Rigetti target yang ingin Anda ajukan tugas Anda. Misalnya, kode berikut memilih QVMtarget.

    target = Rigetti(workspace=workspace, name=RigettiTarget.QVM)
    
  3. Buat program Quil. Agar program Anda diterima, Anda harus mengatur readout ke "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 failed
    
  4. Anda dapat mengindeks Hasil dengan nama readout. Dalam kode berikut, data_per_shot adalah daftar dengan panjang num_shots, dan setiap item dalam daftar adalah daftar lain yang mencakup data untuk register dari bidikan tersebut.

    data_per_shot = result[readout]
    
    ro_data_first_shot = data_per_shot[0]
    

    Dalam hal ini, karena jenis register adalah BIT, jenisnya adalah bilangan bulat dan nilainya 0 atau 1.

    assert isinstance(ro_data_first_shot[0], int)
    assert ro_data_first_shot[0] == 1 or ro_data_first_shot[0] == 0
    
  5. Cetak semua data.

    print(f"Data from '{readout}' register:")
    for i, shot in enumerate(data_per_shot):
        print(f"Shot {i}: {shot}")
    

Penting

Anda tidak dapat mengirimkan beberapa sirkuit pada satu pekerjaan. Sebagai solusinya, Anda dapat memanggil backend.run metode untuk mengirimkan setiap sirkuit secara asinkron, lalu mengambil hasil setiap pekerjaan. Contohnya:

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

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