Share 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.

Notitie

De Microsoft Quantum Development Kit (klassieke QDK) wordt na 30 juni 2024 niet meer ondersteund. Als u een bestaande QDK-ontwikkelaar bent, raden we u aan over te stappen op de nieuwe Azure Quantum Development Kit (moderne QDK) om door te gaan met het ontwikkelen van kwantumoplossingen. Zie Uw Q#-code migreren naar de moderne QDK 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 targetplatformen 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, kunt u schatten hoeveel het kost om uit te voeren. Als u de kosten van het uitvoeren van een taak op de QPU wilt schatten, kunt u de estimate_cost methode gebruiken:

    target = workspace.get_targets(name="ionq.qpu")
    cost = target.estimate_cost(circuit, shots=500)
    
    print(f"Estimated cost: {cost.estimated_total}")
    

    Hiermee worden de geschatte kosten in USD afgedrukt.

    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
    !pip -q install pulser-core

Een kwantumregister maken

  1. Laad eerst de vereiste importbewerkingen:

    import numpy as np
    import pulser
    from pprint import pprint
    from pulser import Pulse, Sequence, Register
    
  2. DE QPU van PASQAL is gemaakt van neutrale atomen die zijn gevangen op goed gedefinieerde posities in een rooster. Als u uw kwantumregisters wilt definiëren, maakt u een matrix met qubits op een rooster. Met de volgende code wordt bijvoorbeeld een 4x4 vierkante raster van qubits gemaakt:

    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()
    

    Plot van een 4x4 vierkante rooster met 16 qubits.

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 moet u een pulsreeks instellen en de kanalen declareren die worden gebruikt om de atomen te beheren. De volgende code declareert bijvoorbeeld twee kanalen: ch0 en ch1.

    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)
    

    Enkele aandachtspunten:

    • Een Sequence in Pulser is een reeks bewerkingen die moeten worden uitgevoerd in een kwantumregister.
    • Met de code wordt een reeks bewerkingen ingesteld die op een AnalogDevice apparaat moeten worden uitgevoerd. AnalogDevice is een vooraf gedefinieerd apparaat in Pulser dat een fresnel1-equivalente kwantumcomputer vertegenwoordigt.
  2. Maak een pulsreeks. Hiervoor maakt en voegt u pulsen toe aan de kanalen die u hebt gedeclareerd. De volgende code maakt bijvoorbeeld een eenvoudige puls en voegt deze toe aan het kanaal ch0en maakt vervolgens een complexe puls en voegt deze toe aan het kanaal ch1.

    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"
    

In de afbeelding ziet u de eenvoudige en complexe polsslag.

Plot van de eenvoudige en complexe puls.

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)
    #print(json.dumps(input_data, indent=4, sort_keys=True))
    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):
        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
    

    Notitie

    De tijd die nodig is om een circuit 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. U kunt het programma bijvoorbeeld indienen bij 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}
    

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, kunt u schatten hoeveel het kost om uit te voeren. Als u de kosten van het uitvoeren van een taak op de QPU wilt schatten, kunt u de estimate_cost methode gebruiken.

    target = workspace.get_targets(name="quantinuum.qpu.h1-1")
    cost = target.estimate_cost(circuit, shots=500)
    
    print(f"Estimated cost: {cost.estimated_total}")
    

    Hiermee worden de geschatte kosten in H-System Quantum Credits (HQC's) afgedrukt.

    Notitie

    Als u een kostenraming wilt uitvoeren op basis van een Quantinuumtarget, moet u eerst het azure-quantum-pakketPython opnieuw laden met de parameter [qiskit] en ervoor zorgen dat u de nieuwste versie van Qiskit hebt. Zie Het azure-quantum-pakket bijwerken Python voor meer informatie.

    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-2") 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())