Dela via


Skicka specifika formaterade kretsar till Azure Quantum

Lär dig hur du använder azure-quantumPython paketet för att skicka kretsar i specifika format till Azure Quantum-tjänsten. Den här artikeln visar hur du skickar kretsar i följande format:

Mer information finns i Kvantkretsar.

Förutsättningar

Om du vill köra dina kretsar i en notebook-fil i Azure Portal behöver du:

För att utveckla och köra dina kretsar i Visual Studio Code behöver du också:

Skapa en ny Jupyter Notebook

Du kan skapa en notebook-fil i VS Code eller direkt i Azure Quantum-portalen.

  1. Logga in på Azure Portal och välj arbetsytan från föregående steg.
  2. Välj Notebooks på det vänstra bladet.
  3. Klicka på Mina anteckningsböcker och klicka på Lägg till ny.
  4. I KernelTyp väljer du IPython.
  5. Skriv ett namn på filen och klicka på Skapa fil.

När den nya notebook-filen öppnas skapas automatiskt koden för den första cellen baserat på din prenumerations- och arbetsyteinformation.

from azure.quantum import Workspace
workspace = Workspace ( 
    resource_id = "", # Your resource_id 
    location = ""  # Your workspace location (for example, "westus") 
)

Skicka QIR-formaterade kretsar

Quantum Intermediate Representation (QIR) är en mellanliggande representation som fungerar som ett gemensamt gränssnitt mellan kvantprogrammeringsspråk/ramverk och riktade kvantberäkningsplattformar. Mer information finns i Quantum Intermediate Representation(Kvantintermediär representation).

  1. Skapa QIR-kretsen. Följande kod skapar till exempel en enkel sammanflätningskrets.

    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. Skapa en submit_qir_job hjälpfunktion för att skicka QIR-kretsen till en target. Observera att indata- och utdataformaten anges som qir.v1 respektive microsoft.quantum-results.v1.

    # 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. Välj en target och skicka QIR-kretsen till Azure Quantum. Om du till exempel vill skicka QIR-kretsen till IonQ-simulatorn target:

    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]}
    

Skicka en krets med ett providerspecifikt format till Azure Quantum

Förutom QIR-språk, till exempel Q# eller Qiskit, kan du skicka kvantkretsar i providerspecifika format till Azure Quantum. Varje provider har ett eget format för att representera kvantkretsar.

Skicka en krets till IonQ med JSON-format

  1. Skapa en kvantkrets med det språkagnostiska JSON-format som stöds av IonQtargets , enligt beskrivningen i IonQ API-dokumentationen. Följande exempel skapar till exempel en superposition mellan tre kvantbitar:

    circuit = {
        "qubits": 3,
        "circuit": [
            {
            "gate": "h",
            "target": 0
            },
            {
            "gate": "cnot",
            "control": 0,
            "target": 1
            },
            {
            "gate": "cnot",
            "control": 0,
            "target": 2
            },
        ]
    }
    
  2. Skicka kretsen till IonQ target. I följande exempel används IonQ-simulatorn, som returnerar ett Job objekt.

    target = workspace.get_targets(name="ionq.simulator")
    job = target.submit(circuit)
    
  3. Vänta tills jobbet är klart och hämta sedan resultatet.

    results = job.get_results()
    print(results)
    
    .....
    {'duration': 8240356, 'histogram': {'0': 0.5, '7': 0.5}}
    
  4. Du kan sedan visualisera resultaten med hjälp av 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")
    

    Utdata för IonQ-jobb

  5. Innan du kör ett jobb på QPU:n bör du uppskatta hur mycket det kommer att kosta att köra.

    Kommentar

    Den senaste prisinformationen finns i IonQ-priser eller hitta din arbetsyta och visa prisalternativ på fliken "Provider" på arbetsytan via: aka.ms/aq/myworkspaces.

Skicka en krets till PASQAL med Pulser SDK

Om du vill skicka en krets till PASQAL kan du använda Pulser SDK för att skapa pulssekvenser och skicka dem till PASQAL target.

Installera Pulser SDK

Pulser är ett ramverk för att skapa, simulera och köra pulssekvenser för kvantenheter med neutral atom. Den är utformad av PASQAL som en direktströmning för att skicka kvantexperiment till sina kvantprocessorer. Mer information finns i Pulser-dokumentationen.

Om du vill skicka pulssekvenserna installerar du först Pulser SDK-paketen:

try:
    import pulser
except ImportError:
    !pip -q install pulser

Skapa ett kvantregister

Du måste definiera både ett register och en layout innan du fortsätter. Registret anger var atomer ska ordnas, medan layouten anger placering av traps som krävs för att fånga och strukturera dessa atomer i registret.

Mer information om layouter finns i Pulser-dokumentationen.

  • Först skapar du ett "enheter"-objekt för att importera PASQAL-kvantdatorn target, Fresnel.

    from pulser_pasqal import PasqalCloud
    
    devices = PasqalCloud().fetch_available_devices()
    QPU = devices["FRESNEL"]
    
Förkalibrerade layouter

Enheten definierar en lista över förkalibrerade layouter. Du kan skapa ditt register av någon av dessa layouter.

Det här är det rekommenderade alternativet eftersom det förbättrar QPU:ns prestanda.

  • Alternativ 1: Definiera ditt register med förkalibrerade layouter

    Granska de layouter som är tillgängliga på Fresnel och definiera ditt register från den här layouten. Mer information om hur du gör finns i pulserdokumentationen.

    Exempel:

    # let's say we are interested in 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,3,5]
    reg = layout.define_register(*traps)
    # You can draw the resulting register to verify it matches your expectations
    reg.draw()
    
Godtyckliga layouter

Om förkalibrerade layouter inte uppfyller kraven för experimentet kan du skapa en anpassad layout.

För varje givet godtyckligt register placerar en neutral atom QPU fällor enligt layouten, som sedan måste genomgå kalibrering. Eftersom varje kalibrering kräver tid rekommenderar vi vanligtvis att du återanvänder en befintlig kalibrerad layout när det är möjligt

  • Alternativ 2: Härled automatiskt en layout från ditt definierade register

    Det här alternativet möjliggör automatisk generering av en layout baserat på ett angivet register. För stora register kan den här processen dock ge suboptimala lösningar på grund av begränsningar i algoritmen som används för att skapa layouten.

    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) 
    
  • Alternativ 3: Definiera ditt register med en manuellt definierad layout

    • Skapa en godtycklig layout med 20 traps slumpmässigt placerade i ett 2D-plan
    import numpy as np
    
    # Generating 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)
    # Creating the layout
    layout = RegisterLayout(traps, slug="random_20")
    
    • Definiera ditt register med specifika trap-ID:er
    trap_ids = [4, 8, 19, 0]
    reg = layout.define_register(*trap_ids, qubit_ids=["a", "b", "c", "d"])
    reg.draw()
    

Skriva en pulssekvens

De neutrala atomerna styrs med laserpulser. Med Pulser SDK kan du skapa pulssekvenser som ska tillämpas på kvantregistret.

  1. Först definierar du pulssekvensattributen genom att deklarera de kanaler som ska användas för att styra atomerna. För att skapa en Sequencemåste du ange en Register instans tillsammans med enheten där sekvensen ska köras. Följande kod deklarerar till exempel en kanal: ch0.

    Kommentar

    Du kan använda QPU = devices["FRESNEL"] enheten eller importera en virtuell enhet från Pulser för mer flexibilitet. Användningen av en VirtualDevice möjliggör sekvensskapande som är mindre begränsad av enhetsspecifikationer, vilket gör den lämplig för körning på en emulator. Mer information finns i Pulser-dokumentationen.

    from pulser import Sequence
    
    seq = Sequence(reg, QPU)
    # print the available channels for your sequence
    print(seq.available_channels)
    # Declare a channel. In this example we will be using `rydberg_global`
    seq.declare_channel("ch0", "rydberg_global")
    
  2. Lägg till pulser i sekvensen. Det gör du genom att skapa och lägga till pulser i de kanaler som du har deklarerat. Följande kod skapar till exempel en puls och lägger till den i kanalen 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()
    

    Följande bild visar pulssekvensen. Pulssekvens

Konvertera sekvensen till en JSON-sträng

Om du vill skicka pulssekvenserna måste du konvertera Pulser-objekten till en JSON-sträng som kan användas som indata.

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

Skicka pulssekvensen till PASQAL target

  1. Först måste du ange rätt indata- och utdataformat. Följande kod anger till exempel indataformatet till pasqal.pulser.v1 och utdataformatet till 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
    

    Kommentar

    Den tid som krävs för att köra ett jobb på QPU:n beror på aktuella kötider. Du kan visa den genomsnittliga kötiden för en target genom att välja bladet Providers för din arbetsyta.

  2. Skicka programmet till PASQAL. Innan du skickar koden till verklig kvantmaskinvara kan du testa koden med emulatorn pasqal.sim.emu-tn som .target

    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
    }
    

Skicka en krets till Quantinuum med OpenQASM

  1. Skapa en kvantkrets i OpenQASM-representationen. I följande exempel skapas till exempel en teleporteringskrets:

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

    Du kan också läsa in kretsen från en fil:

    with open("my_teleport.qasm", "r") as f:
        circuit = f.read()
    
  2. Skicka kretsen till Quantinuum target. I följande exempel används Quantinuum API-validatorn, som returnerar ett Job objekt.

    target = workspace.get_targets(name="quantinuum.sim.h1-1sc")
    job = target.submit(circuit, shots=500)
    
  3. Vänta tills jobbet är klart och hämta sedan resultatet.

    results = job.get_results()
    print(results)
    
    ........
    {'c0': ['000',
    '000',
    '000',
    '000',
    '000',
    '000',
    '000',
    ...
    ]}
    
  4. Du kan sedan visualisera resultaten med hjälp av Matplotlib.

    import pylab as pl
    pl.hist(results["c0"])
    pl.ylabel("Counts")
    pl.xlabel("Bitstring")
    

    Quantinuum-jobbutdata

    När du tittar på histogrammet kanske du märker att slumptalsgeneratorn returnerade 0 varje gång, vilket inte är särskilt slumpmässigt. Detta beror på att även om API Validator ser till att koden körs korrekt på Quantinuum-maskinvara, returnerar den också 0 för varje kvantmätning. För en verklig slumptalsgenerator måste du köra kretsen på kvantmaskinvara.

  5. Innan du kör ett jobb på QPU:n bör du uppskatta hur mycket det kommer att kosta att köra.

    Kommentar

    Den senaste prisinformationen finns i Priser för Azure Quantum eller hitta din arbetsyta och visa prisalternativ på fliken "Provider" på arbetsytan via: aka.ms/aq/myworkspaces.

Skicka en krets till Rigetti med quil

Det enklaste sättet att skicka Quil-jobb är att använda pyquil-for-azure-quantum-package , eftersom du kan använda verktygen och dokumentationen för pyQuil-biblioteket . Utan det här paketet kan pyQuil användas för att skapa Quil-program men inte för att skicka dem till Azure Quantum.

Du kan också skapa Quil-program manuellt och skicka dem direkt med paketet azure-quantum .

  1. Läs först in de importer som krävs.

    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
    
  2. get_qvm Använd funktionen eller get_qpu för att hämta en anslutning till QVM eller QPU.

    qc = get_qvm()  # For simulation
    # qc = get_qpu("Ankaa-9Q-3") for submitting to a QPU
    
  3. Skapa ett Quil-program. Alla giltiga Quil-program accepteras, men avläsningen måste ha namnet 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"]
    
  4. data_per_shot Här är en numpy matris, så du kan använda numpy metoder.

    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
    
  5. Skriv ut alla data.

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

Viktigt!

Det går för närvarande inte att skicka flera kretsar på ett enda jobb. Som en lösning kan du anropa backend.run metoden för att skicka varje krets asynkront och sedan hämta resultatet av varje jobb. Till exempel:

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

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