Delen via


Specifieke geformatteerde circuits verzenden naar Azure Quantum

Meer informatie over het gebruik van het pakket voor het azure-quantumPython verzenden van circuits in specifieke indelingen naar de Azure Quantum-service. In dit artikel leest u hoe u circuits in de volgende indelingen verzendt:

Zie Quantum-circuits voor meer informatie.

Vereisten

Als u uw circuits wilt uitvoeren in een notebook in Azure Portal, hebt u het volgende nodig:

  • Een Azure-account met een actief abonnement. Als u geen Azure-account hebt, registreert u zich gratis en registreert u zich voor een abonnement op basis van betalen per gebruik.
  • Een Azure Quantum-werkruimte. Zie Een Azure Quantum-werkruimte maken voor meer informatie.

Als u uw circuits wilt ontwikkelen en uitvoeren in Visual Studio Code, hebt u ook het volgende nodig:

Een nieuw Jupyter-notebook maken

U kunt een notebook maken in VS Code of rechtstreeks in de Azure Quantum-portal.

  1. Meld u aan bij Azure Portal en selecteer de werkruimte in de vorige stap.
  2. Selecteer Notitieblokken op de linkerblade.
  3. Klik op Mijn notitieblokken en klik op Nieuw toevoegen.
  4. Selecteer IPython in kerneltype.
  5. Typ een naam voor het bestand en klik op Bestand maken.

Wanneer uw nieuwe notitieblok wordt geopend, wordt automatisch de code voor de eerste cel gemaakt op basis van uw abonnements- en werkruimtegegevens.

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

QIR-geformatteerde circuits verzenden

Quantum Intermediate Representation (QIR) is een tussenliggende weergave die fungeert als een gemeenschappelijke interface tussen kwantumprogrammeertalen/frameworks en doelplatforms voor kwantumberekeningen. Zie Quantum Intermediate Representation voor meer informatie.

  1. Maak het QIR-circuit. Met de volgende code wordt bijvoorbeeld een eenvoudig verstrengelingscircuit gemaakt.

    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. Maak een submit_qir_job helperfunctie om het QIR-circuit naar een target. Houd er rekening mee dat de indelingen voor invoer- en uitvoergegevens worden opgegeven als qir.v1 respectievelijk 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. Selecteer een target en verzend het QIR-circuit naar Azure Quantum. Als u bijvoorbeeld het QIR-circuit wilt verzenden naar de IonQ-simulator 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]}
    

Een circuit met een providerspecifieke indeling verzenden naar Azure Quantum

Naast QIR-talen, zoals Q# of Qiskit, kunt u kwantumcircuits in providerspecifieke indelingen verzenden naar Azure Quantum. Elke provider heeft een eigen indeling voor het weergeven van kwantumcircuits.

Een circuit verzenden naar IonQ met behulp van JSON-indeling

  1. Maak een kwantumcircuit met behulp van de taalagnostische JSON-indeling die wordt ondersteund door de IonQtargets, zoals beschreven in de IonQ API-documentatie. In het volgende voorbeeld wordt bijvoorbeeld een superpositie gemaakt tussen drie qubits:

    circuit = {
        "qubits": 3,
        "circuit": [
            {
            "gate": "h",
            "target": 0
            },
            {
            "gate": "cnot",
            "control": 0,
            "target": 1
            },
            {
            "gate": "cnot",
            "control": 0,
            "target": 2
            },
        ]
    }
    
  2. Verzend het circuit naar de IonQ target. In het volgende voorbeeld wordt de IonQ-simulator gebruikt, die een Job object retourneert.

    target = workspace.get_targets(name="ionq.simulator")
    job = target.submit(circuit)
    
  3. Wacht totdat de taak is voltooid en haal vervolgens de resultaten op.

    results = job.get_results()
    print(results)
    
    .....
    {'duration': 8240356, 'histogram': {'0': 0.5, '7': 0.5}}
    
  4. Vervolgens kunt u de resultaten visualiseren met 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")
    

    IonQ-taakuitvoer

  5. Voordat u een taak uitvoert op de QPU, moet u schatten hoeveel het kost om uit te voeren.

    Notitie

    Zie IonQ-prijzen voor de meest recente prijsinformatie of zoek uw werkruimte en bekijk prijsopties op het tabblad Provider van uw werkruimte via: aka.ms/aq/myworkspaces.

Een circuit verzenden naar PASQAL met behulp van Pulser SDK

Als u een circuit wilt verzenden naar PASQAL, kunt u de Pulser SDK gebruiken om pulse-reeksen te maken en deze naar de PASQAL targette verzenden.

De Pulser SDK installeren

Pulser is een framework voor het opstellen, simuleren en uitvoeren van pulse-reeksen voor kwantumapparaten met neutraal atoom. Het is ontworpen door PASQAL als passthrough om kwantumexperimenten naar hun kwantumprocessors te verzenden. Zie de Pulser-documentatie voor meer informatie.

Als u de pulse-reeksen wilt verzenden, installeert u eerst de Pulser SDK-pakketten:

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

Een kwantumregister maken

  1. Eerst maakt u een apparaatobject om de PASQAL-kwantumcomputer target, Fresnel, te importeren. Fresnel QPU biedt vooraf gedefinieerde trapindelingen die u kunt gebruiken om uw kwantumregister te maken. Als u uw kwantumregisters wilt configureren, rangschikt u een matrix met qubits.

    from pulser_pasqal import PasqalCloud
    
    devices = PasqalCloud().fetch_available_devices()
    QPU = devices["FRESNEL"]
    
    # List all available calibrated register layouts
    for calibrated_register_layout in QPU.calibrated_register_layouts.keys():
        print(calibrated_register_layout)
    
  2. Vervolgens definieert u de indeling voor uw qubitregister. In dit voorbeeld gebruikt u een TriangularLatticeLayout(61, 5.0µm) als trapindeling.

    layout = QPU.calibrated_register_layouts[
        "TriangularLatticeLayout(61, 5.0µm)"
    ]
    layout.draw()
    

    In de volgende afbeelding ziet u de weergave van de gekozen indeling.

    Diagram van de indeling die is gekozen voor het qubitregister. De indeling toont 60 punten in een matrix van 40 keer 30 micrometers.

  3. Vervolgens definieert u het qubitregister dat een set traps in de indeling selecteert. In dit voorbeeld heeft de indeling 60 traps en selecteert u 7 traps met behulp van hun id's om een kwantumregister van 7 qubits te definiëren.

    reg = layout.define_register(*[30, 21, 26, 35, 39, 34, 25])
    reg.draw()
    

    In de volgende afbeelding ziet u de uiteindelijke weergave van het qubitregister.

    Diagram met het uiteindelijke kwantumregister na het selecteren van 7 punten uit de indeling van 60 punten.

Een pulsreeks schrijven

De neutrale atomen worden geregeld met laserpulsen. Met de Pulser SDK kunt u pulse-reeksen maken die van toepassing zijn op het kwantumregister.

  1. Eerst definieert u de pulsreekskenmerken door de kanalen te declareren die worden gebruikt om de atomen te beheren. Als u een Sequenceexemplaar wilt maken, moet u een Register exemplaar opgeven, samen met het apparaat waarop de reeks wordt uitgevoerd. De volgende code declareert bijvoorbeeld één kanaal: ch0.

    Notitie

    U kunt het QPU = devices["FRESNEL"] apparaat gebruiken of een virtueel apparaat importeren vanuit Pulser voor meer flexibiliteit. Het gebruik van een VirtualDevice maakt het mogelijk om reeksen te maken die minder beperkt zijn door apparaatspecificaties, waardoor deze geschikt is voor uitvoering op een emulator. Zie de Pulser-documentatie voor meer informatie.

    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. Voeg pulsen toe aan uw reeks. Hiervoor maakt en voegt u pulsen toe aan de kanalen die u hebt gedeclareerd. Met de volgende code wordt bijvoorbeeld een puls gemaakt en toegevoegd aan het kanaal 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()
    

    In de volgende afbeelding ziet u de pulsreeks. Pulse-reeks

De reeks converteren naar een JSON-tekenreeks

Als u de pulse-reeksen wilt verzenden, moet u de Pulser-objecten converteren naar een JSON-tekenreeks die kan worden gebruikt als invoergegevens.

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

De pulsreeks verzenden naar PASQAL target

  1. Eerst moet u de juiste indelingen voor invoer- en uitvoergegevens instellen. Met de volgende code wordt bijvoorbeeld de indeling van de invoergegevens ingesteld op pasqal.pulser.v1 en de indeling van de uitvoergegevens op 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
    

    Notitie

    De tijd die nodig is om een taak uit te voeren op de QPU, is afhankelijk van de huidige wachtrijtijden. U kunt de gemiddelde wachtrijtijd voor een target weergeven door de blade Providers van uw werkruimte te selecteren.

  2. Verzend het programma naar PASQAL. Voordat u uw code indient bij echte kwantumhardware, kunt u uw code testen met behulp van de emulator pasqal.sim.emu-tn als een 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
    }
    

Geavanceerde emulatorfuncties verkennen

PASQAL's emulator biedt geavanceerde functies die nog niet worden ondersteund door Fresnel QPU. U kunt uw register op een aangepaste manier organiseren zonder beperkingen van de vooraf gekalibreerde indelingen. Met de volgende code wordt bijvoorbeeld een 4x4 vierkante raster van qubits gemaakt:

import numpy as np
from pulser import Register, Sequence

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))
custom_reg = Register(qubits)
custom_reg.draw()

seq = Sequence(custom_reg, QPU)

Plot van een 4x4 vierkante rooster met 16 qubits.

Nadat u een aangepast register hebt gedefinieerd, kunt u exact dezelfde stappen volgen die in de vorige sectie worden beschreven om een opgegeven reeks op onze emulator te verzenden.

Notitie

Aangepaste registratiefunctie is binnenkort beschikbaar op FRESNEL.

Een circuit verzenden naar Quantinuum met behulp van OpenQASM

  1. Maak een kwantumcircuit in de OpenQASM-weergave . In het volgende voorbeeld wordt bijvoorbeeld een teleportatiecircuit gemaakt:

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

    U kunt het circuit desgewenst laden vanuit een bestand:

    with open("my_teleport.qasm", "r") as f:
        circuit = f.read()
    
  2. Verzend het circuit naar quantinuum target. In het volgende voorbeeld wordt de Quantinuum API-validator gebruikt, die een Job object retourneert.

    target = workspace.get_targets(name="quantinuum.sim.h1-1sc")
    job = target.submit(circuit, shots=500)
    
  3. Wacht totdat de taak is voltooid en haal vervolgens de resultaten op.

    results = job.get_results()
    print(results)
    
    ........
    {'c0': ['000',
    '000',
    '000',
    '000',
    '000',
    '000',
    '000',
    ...
    ]}
    
  4. Vervolgens kunt u de resultaten visualiseren met Matplotlib.

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

    Uitvoer van quantinuum-taak

    Als u naar het histogram kijkt, ziet u mogelijk dat de generator voor willekeurige getallen elke keer 0 heeft geretourneerd, wat niet erg willekeurig is. Dit komt doordat de API Validator ervoor zorgt dat uw code correct wordt uitgevoerd op Quantinuum-hardware, maar ook 0 retourneert voor elke kwantummeting. Voor een echte generator voor willekeurige getallen moet u uw circuit uitvoeren op kwantumhardware.

  5. Voordat u een taak uitvoert op de QPU, moet u schatten hoeveel het kost om uit te voeren.

    Notitie

    Zie Azure Quantum-prijzen voor de meest recente prijsinformatie of zoek uw werkruimte en bekijk de prijsopties op het tabblad Provider van uw werkruimte via: aka.ms/aq/myworkspaces.

Een circuit verzenden naar Rigetti met behulp van Quil

De eenvoudigste manier om Quil-taken in te dienen, is het pyquil-for-azure-quantum-pakket , omdat u hiermee de hulpprogramma's en documentatie van de pyQuil-bibliotheek kunt gebruiken. Zonder dit pakket kan pyQuil worden gebruikt om Quil-programma's te maken , maar niet om ze in te dienen bij Azure Quantum.

U kunt Quil-programma's ook handmatig maken en verzenden met behulp van het azure-quantum pakket rechtstreeks.

  1. Laad eerst de vereiste importbewerkingen.

    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. Gebruik de get_qvm of get_qpu functie om verbinding te maken met de QVM of QPU.

    qc = get_qvm()  # For simulation
    # qc = get_qpu("Ankaa-9Q-3") for submitting to a QPU
    
  3. Maak een Quil-programma. Elk geldig Quil-programma wordt geaccepteerd, maar de leesbewerking moet de naam rohebben.

    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 Hier is een numpy matrix, zodat u methoden kunt gebruikennumpy.

    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. Alle gegevens afdrukken.

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

Belangrijk

Het verzenden van meerdere circuits op één taak wordt momenteel niet ondersteund. Als tijdelijke oplossing kunt u de backend.run methode aanroepen om elk circuit asynchroon te verzenden en vervolgens de resultaten van elke taak op te halen. Voorbeeld:

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

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