Odeslání konkrétních formátovaných okruhů do Azure Quantum

Zjistěte, jak pomocí azure-quantumPython balíčku odesílat okruhy v konkrétních formátech do služby Azure Quantum. V tomto článku se dozvíte, jak odesílat okruhy v následujících formátech:

Další informace najdete v tématu Kvantové obvody.

Poznámka

Sada Microsoft Quantum Development Kit (Classic QDK) už nebude po 30. červnu 2024 podporována. Pokud jste stávající vývojář sady QDK, doporučujeme přejít na novou sadu Azure Quantum Development Kit (Moderní QDK) a pokračovat ve vývoji kvantových řešení. Další informace najdete v tématu Migrace kódu Q# do moderní sady QDK.

Požadavky

Ke spouštění okruhů v poznámkovém bloku v Azure Portal potřebujete:

K vývoji a spouštění okruhů v editoru Visual Studio Code potřebujete také:

Vytvoření nového Jupyter Notebook

  1. Přihlaste se k Azure Portal a vyberte pracovní prostor z předchozího kroku.
  2. V levém okně vyberte Poznámkové bloky.
  3. Klikněte na Moje poznámkové bloky a pak na Přidat nový.
  4. V části Typ jádra vyberte IPython.
  5. Zadejte název souboru a klikněte na Vytvořit soubor.

Když se nový poznámkový blok otevře, automaticky vytvoří kód pro první buňku na základě informací o vašem předplatném a pracovním prostoru.

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

Odeslání okruhů ve formátu QIR

QIR (Quantum Intermediate Representation) je přechodná reprezentace, která slouží jako společné rozhraní mezi kvantovými programovacími jazyky a architekturami a targetplatformami pro kvantové výpočty. Další informace najdete v tématu Quantum Intermediate Representation.

  1. Vytvořte okruh QIR. Například následující kód vytvoří jednoduchý okruh propletení.

    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. Vytvořte pomocnou submit_qir_job funkci pro odeslání okruhu QIR do target. Všimněte si, že formáty vstupních a výstupních dat jsou zadané jako qir.v1 a 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. target Vyberte a odešlete okruh QIR do Azure Quantum. Pokud chcete například odeslat okruh QIR do simulátoru 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]}
    

Odeslání okruhu s formátem specifickým pro poskytovatele do Azure Quantum

Kromě jazyků QIR, jako je Q# nebo Qiskit, můžete do Azure Quantum odesílat kvantové okruhy ve formátech specifických pro poskytovatele. Každý zprostředkovatel má svůj vlastní formát pro reprezentaci kvantových obvodů.

Odeslání okruhu do IonQ pomocí formátu JSON

  1. Vytvořte kvantový okruh pomocí formátu JSON nezávislého na jazyce, který podporuje IonQ targets, jak je popsáno v dokumentaci k rozhraní IonQ API. Například následující ukázka vytvoří superpozici mezi třemi qubity:

    circuit = {
        "qubits": 3,
        "circuit": [
            {
            "gate": "h",
            "target": 0
            },
            {
            "gate": "cnot",
            "control": 0,
            "target": 1
            },
            {
            "gate": "cnot",
            "control": 0,
            "target": 2
            },
        ]
    }
    
  2. Odešlete okruh do IonQ target. Následující příklad používá simulátor IonQ, který vrací Job objekt .

    target = workspace.get_targets(name="ionq.simulator")
    job = target.submit(circuit)
    
  3. Počkejte na dokončení úlohy a načtěte výsledky.

    results = job.get_results()
    print(results)
    
    .....
    {'duration': 8240356, 'histogram': {'0': 0.5, '7': 0.5}}
    
  4. Výsledky pak můžete vizualizovat pomocí knihovny 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")
    

    Výstup úlohy IonQ

  5. Před spuštěním úlohy na QPU můžete odhadnout, kolik bude jeho spuštění stát. K odhadu nákladů na spuštění úlohy na QPU můžete použít metodu estimate_cost :

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

    Tím se zobrazí odhadované náklady v USD.

    Poznámka

    Nejnovější podrobnosti o cenách najdete v tématu Ceny ionQ nebo vyhledejte svůj pracovní prostor a zobrazte cenové možnosti na kartě Poskytovatel vašeho pracovního prostoru prostřednictvím : aka.ms/aq/myworkspaces.

Odeslání okruhu do PASQAL pomocí sady Pulser SDK

Pokud chcete odeslat okruh do PASQAL, můžete pomocí sady Pulser SDK vytvořit sekvence impulsů a odeslat je do PASQAL target.

Instalace sady Pulser SDK

Pulser je architektura pro vytváření, simulaci a spouštění pulzních sekvencí pro kvantová zařízení s neutrálními atomy. Nástroj PASQAL ho navrhl jako průchozí k odesílání kvantových experimentů do jejich kvantových procesorů. Další informace najdete v dokumentaci k pulseru.

Pokud chcete odeslat pulzní sekvence, nejprve nainstalujte balíčky Pulser SDK:

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

Vytvoření kvantového registru

  1. Nejprve načtěte požadované importy:

    import numpy as np
    import pulser
    from pprint import pprint
    from pulser import Pulse, Sequence, Register
    
  2. QPU pasqal se skládá z neutrálních atomů zachycených v přesně definovaných pozicích v mřížce. K definování kvantových registrů vytvoříte pole qubitů na mřížce. Například následující kód vytvoří čtvercovou mřížku qubitů 4x4:

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

    Vykreslení čtvercové mřížky 4x4 s 16 qubity

Zápis sekvence impulsů

Neutrální atomy jsou řízeny laserovými impulsy. Sada Pulser SDK umožňuje vytvářet sekvence impulsů, které se použijí pro kvantový registr.

  1. Nejprve musíte nastavit sekvenci impulsů a deklarovat kanály, které se použijí k ovládání atomů. Následující kód například deklaruje dva kanály: ch0 a 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)
    

    Pár věcí, které byste měli zvážit:

    • V Sequence Pulseru je řada operací, které se mají provést v kvantovém registru.
    • Kód nastaví sekvenci operací, které se mají provést na AnalogDevice zařízení. AnalogDevice je předdefinované zařízení v Pulseru, které představuje kvantový počítač ekvivalentní Fresnel1.
  2. Vytvořte sekvenci impulsů. Uděláte to tak, že vytvoříte a přidáte do kanálů, které jste deklarovali, a přidáte do nich impulsy. Například následující kód vytvoří jednoduchý impuls a přidá ho do kanálu ch0a pak vytvoří komplexní puls a přidá ho do kanálu 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"
    

Na obrázku je vidět jednoduchý a složitý puls.

Graf jednoduchého a komplexního pulsu.

Převod sekvence na řetězec JSON

Pokud chcete odeslat pulzní sekvence, musíte převést objekty Pulser na řetězec JSON, který lze použít jako vstupní data.

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

Odeslání pulzní sekvence do PASQAL target

  1. Nejprve je potřeba nastavit správné formáty vstupních a výstupních dat. Následující kód například nastaví formát vstupních dat na pasqal.pulser.v1 a formát výstupních dat na 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
    

    Poznámka

    Doba potřebná ke spuštění okruhu na QPU závisí na aktuální době fronty. Průměrnou dobu fronty můžete zobrazit tak, target že v pracovním prostoru vyberete okno Poskytovatelé .

  2. Odešlete program do služby PASQAL. Můžete například odeslat program do 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}
    

Odeslání okruhu do Quantinuum pomocí OpenQASM

  1. Vytvořte kvantový okruh v reprezentaci OpenQASM . Například následující příklad vytvoří teleportační okruh:

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

    Volitelně můžete okruh načíst ze souboru:

    with open("my_teleport.qasm", "r") as f:
        circuit = f.read()
    
  2. Odešlete okruh do Quantinuum target. Následující příklad používá validátor rozhraní API Quantinuum, který vrací Job objekt.

    target = workspace.get_targets(name="quantinuum.sim.h1-1sc")
    job = target.submit(circuit, shots=500)
    
  3. Počkejte, až se úloha dokončí, a pak načtěte výsledky.

    results = job.get_results()
    print(results)
    
    ........
    {'c0': ['000',
    '000',
    '000',
    '000',
    '000',
    '000',
    '000',
    ...
    ]}
    
  4. Výsledky pak můžete vizualizovat pomocí knihovny Matplotlib.

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

    Výstup úlohy Quantinuum

    Při pohledu na histogram si můžete všimnout, že generátor náhodných čísel pokaždé vrátil hodnotu 0, což není příliš náhodné. Je to proto, že zatímco validátor rozhraní API zajišťuje úspěšné spuštění kódu na hardwaru Quantinuum, vrátí také hodnotu 0 pro každé kvantové měření. Pro skutečný generátor náhodných čísel je nutné spustit okruh na kvantovém hardwaru.

  5. Před spuštěním úlohy na QPU můžete odhadnout, kolik bude jeho spuštění stát. K odhadu nákladů na spuštění úlohy na QPU můžete použít metodu estimate_cost .

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

    Tím se vytisknou odhadované náklady v kreditech H-System Quantum Credits (HQCs).

    Poznámka

    Pokud chcete spustit odhad nákladů pro Quantinuum target, musíte nejprve znovu načíst balíček azure-quantumPython pomocí parametru [qiskit]. Další informace najdete v tématu Aktualizace balíčku azure-quantumPython.

    Poznámka

    Nejnovější podrobnosti o cenách najdete v tématu Ceny služby Azure Quantum nebo vyhledejte svůj pracovní prostor a zobrazte cenové možnosti na kartě Poskytovatel pracovního prostoru prostřednictvím : aka.ms/aq/myworkspaces.

Odeslání okruhu do Rigetti pomocí Quil

Nejjednodušším způsobem, jak odeslat úlohy Quil, je použít balíček pyquil-for-azure-quantum , který umožňuje používat nástroje a dokumentaci knihovny pyQuil . Bez tohoto balíčku lze pyQuil použít k vytváření programů Quil, ale ne k jejich odesílání do Azure Quantum.

Programy Quil můžete také vytvářet ručně a odesílat je přímo pomocí azure-quantum balíčku.

  1. Nejprve načtěte požadované importy.

    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 Pomocí funkce nebo get_qpu získejte připojení k QVM nebo QPU.

    qc = get_qvm()  # For simulation
    # qc = get_qpu("Ankaa-2") for submitting to a QPU
    
  3. Vytvořte program Quil. Je přijat jakýkoli platný program Quil, ale čtení musí mít název 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 Tady je numpy pole, takže můžete použít numpy metody.

    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. Vytiskněte všechna data.

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

Důležité

Odesílání více okruhů v jedné úloze se v současné době nepodporuje. Jako alternativní řešení můžete volat metodu backend.run , která asynchronně odešle každý okruh a pak načte výsledky jednotlivých úloh. Příklad:

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

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

Další kroky