Sdílet prostřednictvím


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

Naučte se používat azure-quantumPython balíček k odesílání okruhů 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.

Požadavky

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

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

Vytvoření nového poznámkového bloku Jupyter

Poznámkový blok můžete vytvořit ve VS Code nebo přímo na portálu Azure Quantum.

  1. Přihlaste se k webu 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 klikněte na Přidat nový.
  4. V typu 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

Quantum Intermediate Representation (QIR) je průběžná reprezentace, která slouží jako společné rozhraní mezi kvantovými programovacími jazyky a architekturami a cílovými kvantovými výpočetními platformami. Další informace naleznete v tématu Kvantové zprostředkující reprezentace.

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

    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 vstupní a výstupní datové formáty jsou zadány jako qir.v1 a microsoft.quantum-results.v1v uvedeném pořadí.

    # 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 okruh QIR a odešlete ho do Azure Quantum. Například k odeslání okruhu 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ý poskytovatel má vlastní formát pro reprezentaci kvantových obvodů.

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

  1. Vytvořte kvantový okruh pomocí formátu JSON nezávislého na jazyce podporovaném ionQtargets, 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, až se úloha dokončí, a pak 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 v QPU byste měli odhadnout, kolik bude stát spuštění.

    Poznámka:

    Nejaktuálně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ím atomem. Je navržená nástrojem PASQAL jako průchozím pro odesílání kvantových experimentů do jejich kvantových procesorů. Další informace najdete v dokumentaci k Pulseru.

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

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

Vytvoření kvantového registru

Než budete pokračovat, musíte definovat registr i rozložení. Registr určuje, kde budou atomy uspořádány, zatímco rozložení určuje umístění pastí nezbytných k zachycení a strukturování těchto atomů v registru.

Podrobnosti o rozloženích najdete v dokumentaci pulseru.

  • Nejprve vytvoříte objekt "devices" pro import kvantového počítače targetPASQAL , Fresnel.

    from pulser_pasqal import PasqalCloud
    
    devices = PasqalCloud().fetch_available_devices()
    QPU = devices["FRESNEL"]
    
Předkalibrované rozložení

Zařízení definuje seznam předkalibrovaných rozložení. Můžete vytvořit registr z jednoho z těchto rozložení.

Toto je doporučená možnost, protože zlepší výkon QPU.

  • Možnost 1: Definování registru pomocí předem kalibrovaných rozložení

    Prozkoumejte rozložení dostupná na Fresnelu a definujte registr z tohoto rozložení. Další informace o tom, jak to udělat, najdete v dokumentaci k pulseru.

    Příklad:

    # 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()
    
Libovolná rozložení

Pokud předem kalibrovaná rozložení nevyhovují požadavkům experimentu, můžete vytvořit vlastní rozložení.

U jakéhokoliv libovolného registru umístí QPU neutrálního atomu pasti podle rozložení, které pak musí proběhnout kalibrací. Vzhledem k tomu, že každá kalibrace vyžaduje čas, je obecně vhodné opakovaně použít existující kalibrované rozložení, kdykoli je to možné.

  • Možnost 2: Automaticky odvodit rozložení z definovaného registru

    Tato možnost umožňuje automatické generování rozložení na základě zadaného registru. U velkých registrů ale tento proces může přinést suboptimální řešení z důvodu omezení algoritmu použitého k vytvoření rozložení.

    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) 
    
  • Možnost 3: Definování registru pomocí ručně definovaného rozložení

    • Vytvoření libovolného rozložení s náhodně umístěnými 20 soutisky v 2D rovině
    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")
    
    • Definování registru s konkrétními ID soutisku
    trap_ids = [4, 8, 19, 0]
    reg = layout.define_register(*trap_ids, qubit_ids=["a", "b", "c", "d"])
    reg.draw()
    

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 definujete atributy sekvence impulsů deklarováním kanálů, které se použijí k řízení atomů. Pokud chcete vytvořit Sequenceinstanci, musíte poskytnout Register instanci spolu se zařízením, ve kterém se sekvence spustí. Například následující kód deklaruje jeden kanál: ch0.

    Poznámka:

    Můžete použít QPU = devices["FRESNEL"] zařízení nebo importovat virtuální zařízení z Pulseru, abyste měli větší flexibilitu. Použití VirtualDevice umožňuje vytvoření sekvence, které je méně omezené specifikacemi zařízení, což je vhodné pro spuštění v emulátoru. Další informace najdete v dokumentaci k Pulseru.

    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. Přidejte do sekvence impulsy. Uděláte to tak, že vytvoříte a přidáte impulsy do kanálů, které jste deklarovali. Například následující kód vytvoří impuls a přidá ho do kanálu 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()
    

    Následující obrázek znázorňuje sekvenci impulsů. Pulzní sekvence

Převod sekvence na řetězec JSON

Pokud chcete odeslat sekvence impulsů, 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)
    return to_send

Odeslání sekvence impulsů 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 formát výstupních dat a na formát výstupních dat .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
    

    Poznámka:

    Doba potřebná ke spuštění úlohy v QPU závisí na aktuálních časech fronty. Průměrnou dobu target fronty můžete zobrazit výběrem okna Poskytovatelé vašeho pracovního prostoru.

  2. Odešlete program do PASQAL. Před odesláním kódu do skutečného kvantového hardwaru můžete svůj kód otestovat pomocí emulátoru pasqal.sim.emu-tn jako .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
    }
    

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šle 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 vrátil 0 pokaždé, 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 musíte spustit okruh na kvantovém hardwaru.

  5. Před spuštěním úlohy v QPU byste měli odhadnout, kolik bude stát spuštění.

    Poznámka:

    Nejaktuálně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 vašeho pracovního prostoru prostřednictvím: aka.ms/aq/myworkspaces.

Odeslání okruhu do Rigetti pomocí Quilu

Nejjednodušší způsob, jak odesílat úlohy Quil, je použití balíčku pyquil-for-azure-quantum , protože 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 odeslání do Azure Quantum.

Programy Quil můžete také sestavit ručně a odeslat 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 můžete get_qpu získat připojení k QVM nebo QPU.

    qc = get_qvm()  # For simulation
    # qc = get_qpu("Ankaa-9Q-3") for submitting to a QPU
    
  3. Vytvořte program Quil. Jakýkoli platný program Quil je přijat, ale čtení musí být pojmenováno 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. Tady je numpy pole, data_per_shot 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 pro asynchronní odeslání každého okruhu a pak načíst výsledky každé úlohy. Příklad:

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

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