Różne sposoby uruchamiania narzędzia do szacowania zasobów

Z tego artykułu dowiesz się, jak pracować z narzędziem do szacowania zasobów usługi Azure Quantum. Narzędzie do szacowania zasobów jest dostępne zarówno w programie VS Code, jak i w trybie online w Azure Portal.

W poniższej tabeli przedstawiono różne sposoby uruchamiania narzędzia do szacowania zasobów.

Scenariusz użytkownika Platforma Samouczek
Szacowanie zasobów programu w języku Q# Visual Studio Code Wybierz pozycję Q# w programie VS Code w górnej części strony
Szacowanie zasobów programu w języku Q# (zaawansowane) Jupyter Notebook w usłudze Visual Studio Code Wybierz pozycję Q# w Jupyter Notebook w górnej części strony
Szacowanie zasobów programu Qiskit Azure Quantum Portal Wybierz pozycję Qiskit w Azure Portal w górnej części strony
Szacowanie zasobów programu QIR Azure Quantum Portal Przesyłanie QIR
Użyj plików FCIDUMP jako parametrów argumentu (zaawansowane) Visual Studio Code Przesyłanie problemu z chemią kwantową

Uwaga

Zestaw Microsoft Quantum Development Kit (klasyczny zestaw QDK) nie będzie już obsługiwany po 30 czerwca 2024 r. Jeśli jesteś istniejącym deweloperem zestawu QDK, zalecamy przejście do nowego zestawu Azure Quantum Development Kit (Modern QDK), aby kontynuować opracowywanie rozwiązań kwantowych. Aby uzyskać więcej informacji, zobacz Migrowanie kodu Q# do nowoczesnego zestawu QDK.

Wymagania wstępne dotyczące programu VS Code

Porada

Nie musisz mieć konta platformy Azure do uruchamiania lokalnego narzędzia do szacowania zasobów.

Twórca nowy plik języka Q#

  1. Otwórz Visual Studio Code i wybierz pozycję Plik > nowy plik tekstowy, aby utworzyć nowy plik.
  2. Zapisz plik jako ShorRE.qs. Ten plik będzie zawierać kod języka Q# dla programu.

Twórca algorytmu kwantowego

Skopiuj następujący kod do ShorRE.qs pliku:

namespace Shors {
    open Microsoft.Quantum.Arrays;
    open Microsoft.Quantum.Canon;
    open Microsoft.Quantum.Convert;
    open Microsoft.Quantum.Diagnostics;
    open Microsoft.Quantum.Intrinsic;
    open Microsoft.Quantum.Math;
    open Microsoft.Quantum.Measurement;
    open Microsoft.Quantum.Unstable.Arithmetic;
    open Microsoft.Quantum.ResourceEstimation;

    @EntryPoint()
    operation RunProgram() : Unit {
        let bitsize = 31;

        // When chooseing parameters for `EstimateFrequency`, make sure that
        // generator and modules are not co-prime
        let _ = EstimateFrequency(11, 2^bitsize - 1, bitsize);
    }

    // In this sample we concentrate on costing the `EstimateFrequency`
    // operation, which is the core quantum operation in Shors algorithm, and
    // we omit the classical pre- and post-processing.

    /// # Summary
    /// Estimates the frequency of a generator
    /// in the residue ring Z mod `modulus`.
    ///
    /// # Input
    /// ## generator
    /// The unsigned integer multiplicative order (period)
    /// of which is being estimated. Must be co-prime to `modulus`.
    /// ## modulus
    /// The modulus which defines the residue ring Z mod `modulus`
    /// in which the multiplicative order of `generator` is being estimated.
    /// ## bitsize
    /// Number of bits needed to represent the modulus.
    ///
    /// # Output
    /// The numerator k of dyadic fraction k/2^bitsPrecision
    /// approximating s/r.
    operation EstimateFrequency(
        generator : Int,
        modulus : Int,
        bitsize : Int
    )
    : Int {
        mutable frequencyEstimate = 0;
        let bitsPrecision =  2 * bitsize + 1;

        // Allocate qubits for the superposition of eigenstates of
        // the oracle that is used in period finding.
        use eigenstateRegister = Qubit[bitsize];

        // Initialize eigenstateRegister to 1, which is a superposition of
        // the eigenstates we are estimating the phases of.
        // We first interpret the register as encoding an unsigned integer
        // in little endian encoding.
        ApplyXorInPlace(1, eigenstateRegister);
        let oracle = ApplyOrderFindingOracle(generator, modulus, _, _);

        // Use phase estimation with a semiclassical Fourier transform to
        // estimate the frequency.
        use c = Qubit();
        for idx in bitsPrecision - 1..-1..0 {
            within {
                H(c);
            } apply {
                // `BeginEstimateCaching` and `EndEstimateCaching` are the operations
                // exposed by Azure Quantum Resource Estimator. These will instruct
                // resource counting such that the if-block will be executed
                // only once, its resources will be cached, and appended in
                // every other iteration.
                if BeginEstimateCaching("ControlledOracle", SingleVariant()) {
                    Controlled oracle([c], (1 <<< idx, eigenstateRegister));
                    EndEstimateCaching();
                }
                R1Frac(frequencyEstimate, bitsPrecision - 1 - idx, c);
            }
            if MResetZ(c) == One {
                set frequencyEstimate += 1 <<< (bitsPrecision - 1 - idx);
            }
        }

        // Return all the qubits used for oracles eigenstate back to 0 state
        // using Microsoft.Quantum.Intrinsic.ResetAll.
        ResetAll(eigenstateRegister);

        return frequencyEstimate;
    }

    /// # Summary
    /// Interprets `target` as encoding unsigned little-endian integer k
    /// and performs transformation |k⟩ ↦ |gᵖ⋅k mod N ⟩ where
    /// p is `power`, g is `generator` and N is `modulus`.
    ///
    /// # Input
    /// ## generator
    /// The unsigned integer multiplicative order ( period )
    /// of which is being estimated. Must be co-prime to `modulus`.
    /// ## modulus
    /// The modulus which defines the residue ring Z mod `modulus`
    /// in which the multiplicative order of `generator` is being estimated.
    /// ## power
    /// Power of `generator` by which `target` is multiplied.
    /// ## target
    /// Register interpreted as little endian encoded which is multiplied by
    /// given power of the generator. The multiplication is performed modulo
    /// `modulus`.
    internal operation ApplyOrderFindingOracle(
        generator : Int, modulus : Int, power : Int, target : Qubit[]
    )
    : Unit
    is Adj + Ctl {
        // The oracle we use for order finding implements |x⟩ ↦ |x⋅a mod N⟩. We
        // also use `ExpModI` to compute a by which x must be multiplied. Also
        // note that we interpret target as unsigned integer in little-endian
        // encoding.
        ModularMultiplyByConstant(modulus,
                                    ExpModI(generator, power, modulus),
                                    target);
    }

    /// # Summary
    /// Performs modular in-place multiplication by a classical constant.
    ///
    /// # Description
    /// Given the classical constants `c` and `modulus`, and an input
    /// quantum register  |𝑦⟩, this operation
    /// computes `(c*x) % modulus` into |𝑦⟩.
    ///
    /// # Input
    /// ## modulus
    /// Modulus to use for modular multiplication
    /// ## c
    /// Constant by which to multiply |𝑦⟩
    /// ## y
    /// Quantum register of target
    internal operation ModularMultiplyByConstant(modulus : Int, c : Int, y : Qubit[])
    : Unit is Adj + Ctl {
        use qs = Qubit[Length(y)];
        for (idx, yq) in Enumerated(y) {
            let shiftedC = (c <<< idx) % modulus;
            Controlled ModularAddConstant([yq], (modulus, shiftedC, qs));
        }
        ApplyToEachCA(SWAP, Zipped(y, qs));
        let invC = InverseModI(c, modulus);
        for (idx, yq) in Enumerated(y) {
            let shiftedC = (invC <<< idx) % modulus;
            Controlled ModularAddConstant([yq], (modulus, modulus - shiftedC, qs));
        }
    }

    /// # Summary
    /// Performs modular in-place addition of a classical constant into a
    /// quantum register.
    ///
    /// # Description
    /// Given the classical constants `c` and `modulus`, and an input
    /// quantum register  |𝑦⟩, this operation
    /// computes `(x+c) % modulus` into |𝑦⟩.
    ///
    /// # Input
    /// ## modulus
    /// Modulus to use for modular addition
    /// ## c
    /// Constant to add to |𝑦⟩
    /// ## y
    /// Quantum register of target
    internal operation ModularAddConstant(modulus : Int, c : Int, y : Qubit[])
    : Unit is Adj + Ctl {
        body (...) {
            Controlled ModularAddConstant([], (modulus, c, y));
        }
        controlled (ctrls, ...) {
            // We apply a custom strategy to control this operation instead of
            // letting the compiler create the controlled variant for us in which
            // the `Controlled` functor would be distributed over each operation
            // in the body.
            //
            // Here we can use some scratch memory to save ensure that at most one
            // control qubit is used for costly operations such as `AddConstant`
            // and `CompareGreaterThenOrEqualConstant`.
            if Length(ctrls) >= 2 {
                use control = Qubit();
                within {
                    Controlled X(ctrls, control);
                } apply {
                    Controlled ModularAddConstant([control], (modulus, c, y));
                }
            } else {
                use carry = Qubit();
                Controlled AddConstant(ctrls, (c, y + [carry]));
                Controlled Adjoint AddConstant(ctrls, (modulus, y + [carry]));
                Controlled AddConstant([carry], (modulus, y));
                Controlled CompareGreaterThanOrEqualConstant(ctrls, (c, y, carry));
            }
        }
    }

    /// # Summary
    /// Performs in-place addition of a constant into a quantum register.
    ///
    /// # Description
    /// Given a non-empty quantum register |𝑦⟩ of length 𝑛+1 and a positive
    /// constant 𝑐 < 2ⁿ, computes |𝑦 + c⟩ into |𝑦⟩.
    ///
    /// # Input
    /// ## c
    /// Constant number to add to |𝑦⟩.
    /// ## y
    /// Quantum register of second summand and target; must not be empty.
    internal operation AddConstant(c : Int, y : Qubit[]) : Unit is Adj + Ctl {
        // We are using this version instead of the library version that is based
        // on Fourier angles to show an advantage of sparse simulation in this sample.

        let n = Length(y);
        Fact(n > 0, "Bit width must be at least 1");

        Fact(c >= 0, "constant must not be negative");
        Fact(c < 2 ^ n, $"constant must be smaller than {2L ^ n}");

        if c != 0 {
            // If c has j trailing zeroes than the j least significant bits
            // of y won't be affected by the addition and can therefore be
            // ignored by applying the addition only to the other qubits and
            // shifting c accordingly.
            let j = NTrailingZeroes(c);
            use x = Qubit[n - j];
            within {
                ApplyXorInPlace(c >>> j, x);
            } apply {
                IncByLE(x, y[j...]);
            }
        }
    }

    /// # Summary
    /// Performs greater-than-or-equals comparison to a constant.
    ///
    /// # Description
    /// Toggles output qubit `target` if and only if input register `x`
    /// is greater than or equal to `c`.
    ///
    /// # Input
    /// ## c
    /// Constant value for comparison.
    /// ## x
    /// Quantum register to compare against.
    /// ## target
    /// Target qubit for comparison result.
    ///
    /// # Reference
    /// This construction is described in [Lemma 3, arXiv:2201.10200]
    internal operation CompareGreaterThanOrEqualConstant(c : Int, x : Qubit[], target : Qubit)
    : Unit is Adj+Ctl {
        let bitWidth = Length(x);

        if c == 0 {
            X(target);
        } elif c >= 2 ^ bitWidth {
            // do nothing
        } elif c == 2 ^ (bitWidth - 1) {
            ApplyLowTCNOT(Tail(x), target);
        } else {
            // normalize constant
            let l = NTrailingZeroes(c);

            let cNormalized = c >>> l;
            let xNormalized = x[l...];
            let bitWidthNormalized = Length(xNormalized);
            let gates = Rest(IntAsBoolArray(cNormalized, bitWidthNormalized));

            use qs = Qubit[bitWidthNormalized - 1];
            let cs1 = [Head(xNormalized)] + Most(qs);
            let cs2 = Rest(xNormalized);

            within {
                for i in IndexRange(gates) {
                    (gates[i] ? ApplyAnd | ApplyOr)(cs1[i], cs2[i], qs[i]);
                }
            } apply {
                ApplyLowTCNOT(Tail(qs), target);
            }
        }
    }

    /// # Summary
    /// Internal operation used in the implementation of GreaterThanOrEqualConstant.
    internal operation ApplyOr(control1 : Qubit, control2 : Qubit, target : Qubit) : Unit is Adj {
        within {
            ApplyToEachA(X, [control1, control2]);
        } apply {
            ApplyAnd(control1, control2, target);
            X(target);
        }
    }

    internal operation ApplyAnd(control1 : Qubit, control2 : Qubit, target : Qubit)
    : Unit is Adj {
        body (...) {
            CCNOT(control1, control2, target);
        }
        adjoint (...) {
            H(target);
            if (M(target) == One) {
                X(target);
                CZ(control1, control2);
            }
        }
    }


    /// # Summary
    /// Returns the number of trailing zeroes of a number
    ///
    /// ## Example
    /// ```qsharp
    /// let zeroes = NTrailingZeroes(21); // = NTrailingZeroes(0b1101) = 0
    /// let zeroes = NTrailingZeroes(20); // = NTrailingZeroes(0b1100) = 2
    /// ```
    internal function NTrailingZeroes(number : Int) : Int {
        mutable nZeroes = 0;
        mutable copy = number;
        while (copy % 2 == 0) {
            set nZeroes += 1;
            set copy /= 2;
        }
        return nZeroes;
    }

    /// # Summary
    /// An implementation for `CNOT` that when controlled using a single control uses
    /// a helper qubit and uses `ApplyAnd` to reduce the T-count to 4 instead of 7.
    internal operation ApplyLowTCNOT(a : Qubit, b : Qubit) : Unit is Adj+Ctl {
        body (...) {
            CNOT(a, b);
        }

        adjoint self;

        controlled (ctls, ...) {
            // In this application this operation is used in a way that
            // it is controlled by at most one qubit.
            Fact(Length(ctls) <= 1, "At most one control line allowed");

            if IsEmpty(ctls) {
                CNOT(a, b);
            } else {
                use q = Qubit();
                within {
                    ApplyAnd(Head(ctls), a, q);
                } apply {
                    CNOT(q, b);
                }
            }
        }

        controlled adjoint self;
    }
}

Uruchamianie narzędzia do szacowania zasobów

Narzędzie do szacowania zasobów oferuje sześć wstępnie zdefiniowanych parametrów kubitu, z których cztery mają zestawy instrukcji opartych na bramie i dwa z zestawem instrukcji Majorana. Oferuje również dwa kody korekcyjne błędów kwantowych i surface_codefloquet_code.

W tym przykładzie uruchomisz narzędzie do szacowania zasobów przy użyciu parametru kubitu qubit_gate_us_e3 i kodu korekty błędu kwantowego surface_code .

  1. Wybierz pozycję Widok —> Paleta poleceń i wpisz "resource", który powinien wyświetlić opcję Q#: Calculate Resource Estimates (Oblicz szacowanie zasobów ). Możesz również kliknąć pozycję Estimate (Szacowanie ) z listy poleceń poniżej @EntryPoint(). Wybierz tę opcję, aby otworzyć okno Narzędzia do szacowania zasobów.

    Zrzut ekranu przedstawiający sposób wybierania polecenia estimate z listy obiektywów kodu.

  2. Aby oszacować zasoby, możesz wybrać co najmniej jeden parametr kubitu + typy kodu korekty błędu . W tym przykładzie wybierz pozycję qubit_gate_us_e3 i kliknij przycisk OK.

    Zrzut ekranu przedstawiający sposób wybierania parametru kubitu z menu szacowania zasobów.

  3. Określ budżet błędu lub zaakceptuj wartość domyślną 0,001. W tym przykładzie pozostaw wartość domyślną i naciśnij klawisz Enter.

  4. Naciśnij klawisz Enter , aby zaakceptować domyślną nazwę wyniku na podstawie nazwy pliku, w tym przypadku ShorRE.

Wyświetlanie wyników

Narzędzie do szacowania zasobów udostępnia wiele oszacowań dla tego samego algorytmu, z których każdy pokazuje kompromisy między liczbą kubitów a środowiskiem uruchomieniowym. Zrozumienie kompromisu między środowiskiem uruchomieniowym a skalowaniem systemu jest jednym z ważniejszych aspektów szacowania zasobów.

Wynik szacowania zasobów jest wyświetlany w oknie Szacowanie języka Q# .

  1. Karta Wyniki zawiera podsumowanie szacowania zasobów. Kliknij ikonę obok pierwszego wiersza, aby wybrać kolumny, które chcesz wyświetlić. Możesz wybrać określoną nazwę przebiegu, typ oszacowania, typ kubitu, schemat qec, budżet błędów, kubity logiczne, głębokość logiczną, odległość kodu, stany T, fabryki T, ułamek fabryki T, środowisko uruchomieniowe, rQOPS i kubity fizyczne.

    Zrzut ekranu przedstawiający sposób wyświetlania menu w celu wybrania wybranych danych wyjściowych szacowania zasobów.

    W kolumnie Estimate type (Szacowanie typu ) tabeli wyników można zobaczyć liczbę optymalnych kombinacji {liczba kubitów, środowisko uruchomieniowe} dla algorytmu. Te kombinacje można zobaczyć na diagramie czasu kosmicznego.

  2. Diagram czasowy pokazuje kompromisy między liczbą fizycznych kubitów a środowiskiem uruchomieniowym algorytmu. W tym przypadku narzędzie do szacowania zasobów znajduje 13 różnych optymalnych kombinacji z wielu tysięcy możliwych. Możesz umieścić wskaźnik myszy na każdej {liczbie kubitów, środowisku uruchomieniowym}, aby wyświetlić szczegóły szacowania zasobów w tym momencie.

    Zrzut ekranu przedstawiający diagram czasu kosmicznego narzędzia do szacowania zasobów.

    Aby uzyskać więcej informacji, zobacz Diagram czasu przestrzeni.

    Uwaga

    Musisz kliknąć jeden punkt diagramu czasu kosmicznego, czyli {liczba kubitów, parę środowiska uruchomieniowego}, aby wyświetlić diagram przestrzeni i szczegóły szacowania zasobów odpowiadające temu punktowi.

  3. Na diagramie kosmicznym przedstawiono rozkład fizycznych kubitów używanych dla algorytmu i fabryk T odpowiadających parom {liczba kubitów, runtime}. Jeśli na przykład wybierzesz najbardziej lewy punkt na diagramie czasu przestrzeni, liczba fizycznych kubitów wymaganych do uruchomienia algorytmu jest 427726, 196686 z których są kubitami algorytmów i 231040, z których są kubitami fabryki T.

    Zrzut ekranu przedstawiający diagram przestrzeni narzędzia do szacowania zasobów.

  4. Na koniec na karcie Szacowanie zasobów zostanie wyświetlona pełna lista danych wyjściowych narzędzia do szacowania zasobów odpowiadająca parze {liczba kubitów, runtime} . Szczegóły kosztów można sprawdzić, zwijając grupy, które zawierają więcej informacji. Na przykład wybierz najbardziej lewy punkt na diagramie czasu przestrzeni i zwiń grupę Parametry kubitu logicznego .

    Parametr kubitu logicznego Wartość
    Schemat QEC surface_code
    Odległość kodu 21
    Kubity fizyczne 882
    Czas cyklu logicznego 13 milisecs
    Szybkość błędów kubitu logicznego 3.00E-13
    Refaktoryzacja przeprawy 0.03
    Próg korekty błędu 0,01
    Formuła czasu cyklu logicznego (4 * + twoQubitGateTime 2 * oneQubitMeasurementTime) * codeDistance
    Formuła kubitów fizycznych 2 * codeDistance * codeDistance

    Porada

    Kliknij pozycję Pokaż szczegółowe wiersze , aby wyświetlić opis poszczególnych danych wyjściowych raportu.

    Aby uzyskać więcej informacji, zobacz pełne dane raportu narzędzia do szacowania zasobów.

Zmienianie parametrów target

Koszt tego samego programu w języku Q# można oszacować przy użyciu innego typu kubitu, kodu korekty błędów i budżetu błędu. Otwórz okno Narzędzie do szacowania zasobów, wybierając pozycję Widok —> Paleta poleceń i wpisz Q#: Calculate Resource Estimates.

Wybierz dowolną inną konfigurację, na przykład parametr kubitu opartego na majoranie, qubit_maj_ns_e6. Zaakceptuj domyślną wartość budżetu błędu lub wprowadź nową, a następnie naciśnij klawisz Enter. Narzędzie do szacowania zasobów ponownie uruchamia szacowanie przy użyciu nowych target parametrów.

Aby uzyskać więcej informacji, zobacz Parametry docelowe narzędzia do szacowania zasobów.

Uruchamianie wielu konfiguracji parametrów

Narzędzie do szacowania zasobów usługi Azure Quantum może uruchamiać wiele konfiguracji parametrów target i porównywać wyniki szacowania zasobów.

  1. Wybierz pozycję Widok —> Paleta poleceń lub naciśnij klawisze Ctrl+Shift+P i wpisz Q#: Calculate Resource Estimates.

  2. Wybierz qubit_gate_us_e3, qubit_gate_us_e4, qubit_maj_ns_e4 + floquet_code i qubit_maj_ns_e6 + floquet_code, a następnie kliknij przycisk OK.

  3. Zaakceptuj domyślną wartość budżetu błędu 0.001 i naciśnij klawisz Enter.

  4. Naciśnij klawisz Enter , aby zaakceptować plik wejściowy, w tym przypadku ShorRE.qs.

  5. W przypadku wielu konfiguracji parametrów wyniki są wyświetlane w różnych wierszach na karcie Wyniki .

  6. Diagram czasu przestrzeni przedstawia wyniki wszystkich konfiguracji parametrów. Pierwsza kolumna tabeli wyników zawiera legendę dla każdej konfiguracji parametrów. Możesz zatrzymać wskaźnik myszy na każdym punkcie, aby zobaczyć szczegóły szacowania zasobów w tym momencie.

    Zrzut ekranu przedstawiający diagram czasu przestrzeni i tabelę wyników podczas uruchamiania wielu konfiguracji parametru w narzędziu do szacowania zasobów.

  7. Kliknij punkt {liczba kubitów, środowisko uruchomieniowe} diagramu czasu przestrzeni, aby wyświetlić odpowiedni diagram przestrzeni i dane raportu.

Wymagania wstępne dotyczące Jupyter Notebook w programie VS Code

Porada

Nie musisz mieć konta platformy Azure do uruchamiania lokalnego narzędzia do szacowania zasobów.

Twórca algorytm kwantowy

  1. W programie VS Code wybierz pozycję Wyświetl > paletę poleceń i wybierz pozycję Twórca: Nowy Jupyter Notebook.

  2. W prawym górnym rogu program VS Code wykryje i wyświetli wersję języka Python oraz wirtualne środowisko języka Python wybrane dla notesu. Jeśli masz wiele środowisk języka Python, może być konieczne wybranie jądra przy użyciu selektora jądra w prawym górnym rogu. Jeśli nie wykryto żadnego środowiska, zobacz Notesy Jupyter Notebook w programie VS Code , aby uzyskać informacje o instalacji.

  3. W pierwszej komórce notesu zaimportuj qsharp pakiet.

    import qsharp
    
  4. Dodaj nową komórkę i skopiuj następujący kod.

    %%qsharp
    open Microsoft.Quantum.Arrays;
    open Microsoft.Quantum.Canon;
    open Microsoft.Quantum.Convert;
    open Microsoft.Quantum.Diagnostics;
    open Microsoft.Quantum.Intrinsic;
    open Microsoft.Quantum.Math;
    open Microsoft.Quantum.Measurement;
    open Microsoft.Quantum.Unstable.Arithmetic;
    open Microsoft.Quantum.ResourceEstimation;
    
    operation RunProgram() : Unit {
        let bitsize = 31;
    
        // When choosing parameters for `EstimateFrequency`, make sure that
        // generator and modules are not co-prime
        let _ = EstimateFrequency(11, 2^bitsize - 1, bitsize);
    }
    
    
    // In this sample we concentrate on costing the `EstimateFrequency`
    // operation, which is the core quantum operation in Shors algorithm, and
    // we omit the classical pre- and post-processing.
    
    /// # Summary
    /// Estimates the frequency of a generator
    /// in the residue ring Z mod `modulus`.
    ///
    /// # Input
    /// ## generator
    /// The unsigned integer multiplicative order (period)
    /// of which is being estimated. Must be co-prime to `modulus`.
    /// ## modulus
    /// The modulus which defines the residue ring Z mod `modulus`
    /// in which the multiplicative order of `generator` is being estimated.
    /// ## bitsize
    /// Number of bits needed to represent the modulus.
    ///
    /// # Output
    /// The numerator k of dyadic fraction k/2^bitsPrecision
    /// approximating s/r.
    operation EstimateFrequency(
        generator : Int,
        modulus : Int,
        bitsize : Int
    )
    : Int {
        mutable frequencyEstimate = 0;
        let bitsPrecision =  2 * bitsize + 1;
    
        // Allocate qubits for the superposition of eigenstates of
        // the oracle that is used in period finding.
        use eigenstateRegister = Qubit[bitsize];
    
        // Initialize eigenstateRegister to 1, which is a superposition of
        // the eigenstates we are estimating the phases of.
        // We first interpret the register as encoding an unsigned integer
        // in little endian encoding.
        ApplyXorInPlace(1, eigenstateRegister);
        let oracle = ApplyOrderFindingOracle(generator, modulus, _, _);
    
        // Use phase estimation with a semiclassical Fourier transform to
        // estimate the frequency.
        use c = Qubit();
        for idx in bitsPrecision - 1..-1..0 {
            within {
                H(c);
            } apply {
                // `BeginEstimateCaching` and `EndEstimateCaching` are the operations
                // exposed by Azure Quantum Resource Estimator. These will instruct
                // resource counting such that the if-block will be executed
                // only once, its resources will be cached, and appended in
                // every other iteration.
                if BeginEstimateCaching("ControlledOracle", SingleVariant()) {
                    Controlled oracle([c], (1 <<< idx, eigenstateRegister));
                    EndEstimateCaching();
                }
                R1Frac(frequencyEstimate, bitsPrecision - 1 - idx, c);
            }
            if MResetZ(c) == One {
                set frequencyEstimate += 1 <<< (bitsPrecision - 1 - idx);
            }
        }
    
        // Return all the qubits used for oracle eigenstate back to 0 state
        // using Microsoft.Quantum.Intrinsic.ResetAll.
        ResetAll(eigenstateRegister);
    
        return frequencyEstimate;
    }
    
    /// # Summary
    /// Interprets `target` as encoding unsigned little-endian integer k
    /// and performs transformation |k⟩ ↦ |gᵖ⋅k mod N ⟩ where
    /// p is `power`, g is `generator` and N is `modulus`.
    ///
    /// # Input
    /// ## generator
    /// The unsigned integer multiplicative order ( period )
    /// of which is being estimated. Must be co-prime to `modulus`.
    /// ## modulus
    /// The modulus which defines the residue ring Z mod `modulus`
    /// in which the multiplicative order of `generator` is being estimated.
    /// ## power
    /// Power of `generator` by which `target` is multiplied.
    /// ## target
    /// Register interpreted as little endian encoded which is multiplied by
    /// given power of the generator. The multiplication is performed modulo
    /// `modulus`.
    internal operation ApplyOrderFindingOracle(
        generator : Int, modulus : Int, power : Int, target : Qubit[]
    )
    : Unit
    is Adj + Ctl {
        // The oracle we use for order finding implements |x⟩ ↦ |x⋅a mod N⟩. We
        // also use `ExpModI` to compute a by which x must be multiplied. Also
        // note that we interpret target as unsigned integer in little-endian
        // encoding.
        ModularMultiplyByConstant(modulus,
                                    ExpModI(generator, power, modulus),
                                    target);
    }
    
    /// # Summary
    /// Performs modular in-place multiplication by a classical constant.
    ///
    /// # Description
    /// Given the classical constants `c` and `modulus`, and an input
    /// quantum register |𝑦⟩, this operation
    /// computes `(c*x) % modulus` into |𝑦⟩.
    ///
    /// # Input
    /// ## modulus
    /// Modulus to use for modular multiplication
    /// ## c
    /// Constant by which to multiply |𝑦⟩
    /// ## y
    /// Quantum register of target
    internal operation ModularMultiplyByConstant(modulus : Int, c : Int, y : Qubit[])
    : Unit is Adj + Ctl {
        use qs = Qubit[Length(y)];
        for (idx, yq) in Enumerated(y) {
            let shiftedC = (c <<< idx) % modulus;
            Controlled ModularAddConstant([yq], (modulus, shiftedC, qs));
        }
        ApplyToEachCA(SWAP, Zipped(y, qs));
        let invC = InverseModI(c, modulus);
        for (idx, yq) in Enumerated(y) {
            let shiftedC = (invC <<< idx) % modulus;
            Controlled ModularAddConstant([yq], (modulus, modulus - shiftedC, qs));
        }
    }
    
    /// # Summary
    /// Performs modular in-place addition of a classical constant into a
    /// quantum register.
    ///
    /// # Description
    /// Given the classical constants `c` and `modulus`, and an input
    /// quantum register  |𝑦⟩, this operation
    /// computes `(x+c) % modulus` into |𝑦⟩.
    ///
    /// # Input
    /// ## modulus
    /// Modulus to use for modular addition
    /// ## c
    /// Constant to add to |𝑦⟩
    /// ## y
    /// Quantum register of target
    internal operation ModularAddConstant(modulus : Int, c : Int, y : Qubit[])
    : Unit is Adj + Ctl {
        body (...) {
            Controlled ModularAddConstant([], (modulus, c, y));
        }
        controlled (ctrls, ...) {
            // We apply a custom strategy to control this operation instead of
            // letting the compiler create the controlled variant for us in which
            // the `Controlled` functor would be distributed over each operation
            // in the body.
            //
            // Here we can use some scratch memory to save ensure that at most one
            // control qubit is used for costly operations such as `AddConstant`
            // and `CompareGreaterThenOrEqualConstant`.
            if Length(ctrls) >= 2 {
                use control = Qubit();
                within {
                    Controlled X(ctrls, control);
                } apply {
                    Controlled ModularAddConstant([control], (modulus, c, y));
                }
            } else {
                use carry = Qubit();
                Controlled AddConstant(ctrls, (c, y + [carry]));
                Controlled Adjoint AddConstant(ctrls, (modulus, y + [carry]));
                Controlled AddConstant([carry], (modulus, y));
                Controlled CompareGreaterThanOrEqualConstant(ctrls, (c, y, carry));
            }
        }
    }
    
    /// # Summary
    /// Performs in-place addition of a constant into a quantum register.
    ///
    /// # Description
    /// Given a non-empty quantum register |𝑦⟩ of length 𝑛+1 and a positive
    /// constant 𝑐 < 2ⁿ, computes |𝑦 + c⟩ into |𝑦⟩.
    ///
    /// # Input
    /// ## c
    /// Constant number to add to |𝑦⟩.
    /// ## y
    /// Quantum register of second summand and target; must not be empty.
    internal operation AddConstant(c : Int, y : Qubit[]) : Unit is Adj + Ctl {
        // We are using this version instead of the library version that is based
        // on Fourier angles to show an advantage of sparse simulation in this sample.
    
        let n = Length(y);
        Fact(n > 0, "Bit width must be at least 1");
    
        Fact(c >= 0, "constant must not be negative");
        Fact(c < 2 ^ n, $"constant must be smaller than {2L ^ n}");
    
        if c != 0 {
            // If c has j trailing zeroes than the j least significant bits
            // of y will not be affected by the addition and can therefore be
            // ignored by applying the addition only to the other qubits and
            // shifting c accordingly.
            let j = NTrailingZeroes(c);
            use x = Qubit[n - j];
            within {
                ApplyXorInPlace(c >>> j, x);
            } apply {
                IncByLE(x, y[j...]);
            }
        }
    }
    
    /// # Summary
    /// Performs greater-than-or-equals comparison to a constant.
    ///
    /// # Description
    /// Toggles output qubit `target` if and only if input register `x`
    /// is greater than or equal to `c`.
    ///
    /// # Input
    /// ## c
    /// Constant value for comparison.
    /// ## x
    /// Quantum register to compare against.
    /// ## target
    /// Target qubit for comparison result.
    ///
    /// # Reference
    /// This construction is described in [Lemma 3, arXiv:2201.10200]
    internal operation CompareGreaterThanOrEqualConstant(c : Int, x : Qubit[], target : Qubit)
    : Unit is Adj+Ctl {
        let bitWidth = Length(x);
    
        if c == 0 {
            X(target);
        } elif c >= 2 ^ bitWidth {
            // do nothing
        } elif c == 2 ^ (bitWidth - 1) {
            ApplyLowTCNOT(Tail(x), target);
        } else {
            // normalize constant
            let l = NTrailingZeroes(c);
    
            let cNormalized = c >>> l;
            let xNormalized = x[l...];
            let bitWidthNormalized = Length(xNormalized);
            let gates = Rest(IntAsBoolArray(cNormalized, bitWidthNormalized));
    
            use qs = Qubit[bitWidthNormalized - 1];
            let cs1 = [Head(xNormalized)] + Most(qs);
            let cs2 = Rest(xNormalized);
    
            within {
                for i in IndexRange(gates) {
                    (gates[i] ? ApplyAnd | ApplyOr)(cs1[i], cs2[i], qs[i]);
                }
            } apply {
                ApplyLowTCNOT(Tail(qs), target);
            }
        }
    }
    
    /// # Summary
    /// Internal operation used in the implementation of GreaterThanOrEqualConstant.
    internal operation ApplyOr(control1 : Qubit, control2 : Qubit, target : Qubit) : Unit is Adj {
        within {
            ApplyToEachA(X, [control1, control2]);
        } apply {
            ApplyAnd(control1, control2, target);
            X(target);
        }
    }
    
    internal operation ApplyAnd(control1 : Qubit, control2 : Qubit, target : Qubit)
    : Unit is Adj {
        body (...) {
            CCNOT(control1, control2, target);
        }
        adjoint (...) {
            H(target);
            if (M(target) == One) {
                X(target);
                CZ(control1, control2);
            }
        }
    }
    
    
    /// # Summary
    /// Returns the number of trailing zeroes of a number
    ///
    /// ## Example
    /// ```qsharp
    /// let zeroes = NTrailingZeroes(21); // = NTrailingZeroes(0b1101) = 0
    /// let zeroes = NTrailingZeroes(20); // = NTrailingZeroes(0b1100) = 2
    /// ```
    internal function NTrailingZeroes(number : Int) : Int {
        mutable nZeroes = 0;
        mutable copy = number;
        while (copy % 2 == 0) {
            set nZeroes += 1;
            set copy /= 2;
        }
        return nZeroes;
    }
    
    /// # Summary
    /// An implementation for `CNOT` that when controlled using a single control uses
    /// a helper qubit and uses `ApplyAnd` to reduce the T-count to 4 instead of 7.
    internal operation ApplyLowTCNOT(a : Qubit, b : Qubit) : Unit is Adj+Ctl {
        body (...) {
            CNOT(a, b);
        }
    
        adjoint self;
    
        controlled (ctls, ...) {
            // In this application this operation is used in a way that
            // it is controlled by at most one qubit.
            Fact(Length(ctls) <= 1, "At most one control line allowed");
    
            if IsEmpty(ctls) {
                CNOT(a, b);
            } else {
                use q = Qubit();
                within {
                    ApplyAnd(Head(ctls), a, q);
                } apply {
                    CNOT(q, b);
                }
            }
        }
    
        controlled adjoint self;
    }
    

Szacowanie algorytmu kwantowego

Teraz szacujesz zasoby fizyczne dla RunProgram operacji przy użyciu domyślnych założeń. Dodaj nową komórkę i skopiuj następujący kod.

result = qsharp.estimate("RunProgram()")
result

Funkcja qsharp.estimate tworzy obiekt wynikowy, który może służyć do wyświetlania tabeli z ogólnymi liczbami zasobów fizycznych. Szczegóły kosztów można sprawdzić, zwijając grupy, które zawierają więcej informacji. Aby uzyskać więcej informacji, zobacz pełne dane raportu narzędzia do szacowania zasobów.

Na przykład zwiń grupę Parametrów kubitu logicznego , aby zobaczyć, że odległość kodu wynosi 21, a liczba kubitów fizycznych wynosi 882.

Parametr kubitu logicznego Wartość
Schemat QEC surface_code
Odległość kodu 21
Kubity fizyczne 882
Czas cyklu logicznego 8 milisek
Szybkość błędów kubitu logicznego 3.00E-13
Refaktor przeprawy 0.03
Próg korekty błędu 0,01
Formuła czasu cyklu logicznego (4 * twoQubitGateTime + 2 * oneQubitMeasurementTime) * codeDistance
Formuła kubitów fizycznych 2 * codeDistance * codeDistance

Porada

Aby uzyskać bardziej kompaktowa wersję tabeli wyjściowej, można użyć polecenia result.summary.

Diagram spacji

Rozkład fizycznych kubitów używanych dla algorytmu i fabryk T jest czynnikiem, który może mieć wpływ na projektowanie algorytmu. Za pomocą qsharp-widgets pakietu można wizualizować tę dystrybucję, aby lepiej zrozumieć szacowane wymagania dotyczące miejsca dla algorytmu.

from qsharp-widgets import SpaceChart, EstimateDetails
SpaceChart(result)

W tym przykładzie liczba fizycznych kubitów wymaganych do uruchomienia algorytmu jest 829766, z których 196686 są kubitami algorytmów i 633080, z których są kubitami fabrycznymi T.

Zrzut ekranu przedstawiający diagram przestrzeni narzędzia do szacowania zasobów.

Zmienianie wartości domyślnych i szacowanie algorytmu

Podczas przesyłania żądania oszacowania zasobów dla programu można określić niektóre parametry opcjonalne. jobParams Użyj pola, aby uzyskać dostęp do wszystkich target parametrów, które można przekazać do wykonania zadania i sprawdzić, które wartości domyślne zostały przyjęte:

result['jobParams']
{'errorBudget': 0.001,
 'qecScheme': {'crossingPrefactor': 0.03,
  'errorCorrectionThreshold': 0.01,
  'logicalCycleTime': '(4 * twoQubitGateTime + 2 * oneQubitMeasurementTime) * codeDistance',
  'name': 'surface_code',
  'physicalQubitsPerLogicalQubit': '2 * codeDistance * codeDistance'},
 'qubitParams': {'instructionSet': 'GateBased',
  'name': 'qubit_gate_ns_e3',
  'oneQubitGateErrorRate': 0.001,
  'oneQubitGateTime': '50 ns',
  'oneQubitMeasurementErrorRate': 0.001,
  'oneQubitMeasurementTime': '100 ns',
  'tGateErrorRate': 0.001,
  'tGateTime': '50 ns',
  'twoQubitGateErrorRate': 0.001,
  'twoQubitGateTime': '50 ns'}}

Widać, że narzędzie do szacowania zasobów przyjmuje qubit_gate_ns_e3 model kubitu, surface_code kod korekty błędów i budżet błędu 0,001 jako wartości domyślne szacowania.

Są to target parametry, które można dostosować:

  • errorBudget - ogólny dozwolony budżet błędów dla algorytmu
  • qecScheme — schemat korekty błędu kwantowego (QEC)
  • qubitParams - parametry kubitu fizycznego
  • constraints - ograniczenia na poziomie składnika
  • distillationUnitSpecifications - specyfikacje algorytmów destylowania fabryk T
  • estimateType - pojedyncza lub granica

Aby uzyskać więcej informacji, zobacz Parametry docelowe narzędzia do szacowania zasobów.

Zmienianie modelu kubitu

Koszt dla tego samego algorytmu można oszacować przy użyciu parametru kubitu opartego na majoranie , qubitParams"qubit_maj_ns_e6".

result_maj = qsharp.estimate("RunProgram()", params={
                "qubitParams": {
                    "name": "qubit_maj_ns_e6"
                }})
EstimateDetails(result_maj)

Zmienianie schematu korekty błędów kwantowych

Możesz ponownie uruchomić zadanie szacowania zasobów dla tego samego przykładu dla parametrów kubitu opartego na majoranie za pomocą schematu QEC floqued. qecScheme

result_maj = qsharp.estimate("RunProgram()", params={
                "qubitParams": {
                    "name": "qubit_maj_ns_e6"
                },
                "qecScheme": {
                    "name": "floquet_code"
                }})
EstimateDetails(result_maj)

Zmiana budżetu błędu

Następnie uruchom ponownie ten sam obwód kwantowy o errorBudget wartości 10%.

result_maj = qsharp.estimate("RunProgram()", params={
                "qubitParams": {
                    "name": "qubit_maj_ns_e6"
                },
                "qecScheme": {
                    "name": "floquet_code"
                },
                "errorBudget": 0.1})
EstimateDetails(result_maj)

Przetwarzanie wsadowe za pomocą narzędzia do szacowania zasobów

Narzędzie do szacowania zasobów usługi Azure Quantum umożliwia uruchamianie wielu konfiguracji parametrów target i porównywanie wyników. Jest to przydatne, gdy chcesz porównać koszt różnych modeli kubitów, schematów QEC lub budżetów błędów.

  1. Szacowanie wsadowe można wykonać, przekazując listę parametrów target do params parametru qsharp.estimate funkcji. Na przykład uruchom ten sam algorytm z parametrami domyślnymi i parametrami kubitu opartymi na majorze z schematem QEC floqued.

    result_batch = qsharp.estimate("RunProgram()", params=
                    [{}, # Default parameters
                    {
                        "qubitParams": {
                            "name": "qubit_maj_ns_e6"
                        },
                        "qecScheme": {
                            "name": "floquet_code"
                        }
                    }])
    result_batch.summary_data_frame(labels=["Gate-based ns, 10⁻³", "Majorana ns, 10⁻⁶"])
    
    Model Kubity logiczne Głębokość logiczna Stany T Odległość kodu Fabryki T Ułamek fabryki T Kubity fizyczne rQOPS Środowisko uruchomieniowe fizyczne
    Ns oparte na bramie, 10⁻³ 223 3,64 mln 4,70 mln 21 19 76.30 % 829,77 tys. 26,55 mln 31 s
    Majorana ns, 10⁻⁶ 223 3,64 mln 4,70 mln 5 19 63.02 % 79,60 tys. 148,67 mln 5 s
  2. Można również utworzyć listę parametrów szacowania przy użyciu EstimatorParams klasy .

    from qsharp.estimator import EstimatorParams, QubitParams, QECScheme, LogicalCounts
    
    labels = ["Gate-based µs, 10⁻³", "Gate-based µs, 10⁻⁴", "Gate-based ns, 10⁻³", "Gate-based ns, 10⁻⁴", "Majorana ns, 10⁻⁴", "Majorana ns, 10⁻⁶"]
    
    params = EstimatorParams(num_items=6)
    params.error_budget = 0.333
    params.items[0].qubit_params.name = QubitParams.GATE_US_E3
    params.items[1].qubit_params.name = QubitParams.GATE_US_E4
    params.items[2].qubit_params.name = QubitParams.GATE_NS_E3
    params.items[3].qubit_params.name = QubitParams.GATE_NS_E4
    params.items[4].qubit_params.name = QubitParams.MAJ_NS_E4
    params.items[4].qec_scheme.name = QECScheme.FLOQUET_CODE
    params.items[5].qubit_params.name = QubitParams.MAJ_NS_E6
    params.items[5].qec_scheme.name = QECScheme.FLOQUET_CODE
    
    qsharp.estimate("RunProgram()", params=params).summary_data_frame(labels=labels)
    
    Model Kubity logiczne Głębokość logiczna Stany T Odległość kodu Fabryki T Ułamek fabryki T Kubity fizyczne rQOPS Środowisko uruchomieniowe fizyczne
    μs oparte na bramie, 10⁻³ 223 3,64 mln 4,70 mln 17 13 40.54 % 216,77 tys. 21,86 tys. 10 godzin
    μs oparte na bramie, 10⁻⁴ 223 3,64 mln 4,70 mln 9 14 43.17 % 63,57 tys. 41.30 tys. 5 godzin
    Ns oparte na bramie, 10⁻³ 223 3,64 mln 4,70 mln 17 16 69.08 % 416,89 tys. 32,79 mln 25 s
    Ns oparte na bramie, 10⁻⁴ 223 3,64 mln 4,70 mln 9 14 43.17 % 63,57 tys. 61,94 mln 13 s
    Majorana ns, 10⁻⁴ 223 3,64 mln 4,70 mln 9 19 82.75 % 501.48 tys. 82,59 mln 10 s
    Majorana ns, 10⁻⁶ 223 3,64 mln 4,70 mln 5 13 31.47 % 42,96 tys. 148,67 mln 5 s

Szacowanie granic Pareto

Podczas szacowania zasobów algorytmu należy wziąć pod uwagę kompromis między liczbą fizycznych kubitów a środowiskiem uruchomieniowym algorytmu. Można rozważyć alokację jak największej liczby kubitów fizycznych, aby zmniejszyć środowisko uruchomieniowe algorytmu. Jednak liczba kubitów fizycznych jest ograniczona przez liczbę fizycznych kubitów dostępnych na sprzęcie kwantowym.

Szacowanie granic Pareto zapewnia wiele szacunków dla tego samego algorytmu, z których każda ma kompromis między liczbą kubitów a środowiskiem uruchomieniowym.

  1. Aby uruchomić narzędzie do szacowania zasobów przy użyciu szacowania granic Pareto, należy określić "estimateType"target parametr jako "frontier". Na przykład uruchom ten sam algorytm z parametrami kubitu opartymi na majoranie z kodem powierzchniowym przy użyciu szacowania granic Pareto.

    result = qsharp.estimate("RunProgram()", params=
                                {"qubitParams": { "name": "qubit_maj_ns_e4" },
                                "qecScheme": { "name": "surface_code" },
                                "estimateType": "frontier", # frontier estimation
                                }
                            )
    
  2. Za pomocą EstimatesOverview funkcji można wyświetlić tabelę z ogólnymi liczbami zasobów fizycznych. Kliknij ikonę obok pierwszego wiersza, aby wybrać kolumny, które chcesz wyświetlić. Możesz wybrać elementy z nazwy uruchomienia, typu szacowania, typu kubitu, schematu qec, budżetu błędu, kubitów logicznych, głębokości logicznej, odległości kodu, stanów T, fabryk T, ułamka fabryki T, czasu wykonywania, rQOPS i kubitów fizycznych.

    from qsharp_widgets import EstimatesOverview
    EstimatesOverview(result)
    

W kolumnie Szacowanie typu tabeli wyników można zobaczyć liczbę różnych kombinacji {liczba kubitów, środowisko uruchomieniowe} dla algorytmu. W tym przypadku narzędzie do szacowania zasobów znajduje 22 różne optymalne kombinacje z wielu tysięcy możliwych.

Diagram czasu przestrzeni

Funkcja EstimatesOverview wyświetla również diagram czasowy narzędzia do szacowania zasobów.

Diagram czasu przestrzeni przedstawia liczbę fizycznych kubitów i środowisko uruchomieniowe algorytmu dla każdej pary {liczba kubitów, runtime}. Możesz umieścić wskaźnik myszy na każdym punkcie, aby zobaczyć szczegóły szacowania zasobów w tym momencie.

Zrzut ekranu przedstawiający diagram czasu przestrzeni z oszacowaniem granic narzędzia do szacowania zasobów.

Dzielenie na partie przy użyciu szacowania granic Pareto

  1. Aby oszacować i porównać wiele konfiguracji parametrów target z szacowaniem granic, dodaj "estimateType": "frontier", do parametrów.

    result = qsharp.estimate(
        "RunProgram()",
        [
            {
            "qubitParams": { "name": "qubit_maj_ns_e4" },
            "qecScheme": { "name": "surface_code" },
            "estimateType": "frontier", # Pareto frontier estimation
            },
            {
            "qubitParams": { "name": "qubit_maj_ns_e6" },
            "qecScheme": { "name": "floquet_code" },
            "estimateType": "frontier", # Pareto frontier estimation
            },
        ]
    )
    
    EstimatesOverview(result, colors=["#1f77b4", "#ff7f0e"], runNames=["e4 Surface Code", "e6 Floquet Code"])
    

    Zrzut ekranu przedstawiający diagram czasu kosmicznego narzędzia do szacowania zasobów podczas korzystania z szacowania granic Pareto i wielu konfiguracji parametrów.

    Uwaga

    Za pomocą funkcji można definiować kolory i nazwy przebiegów dla diagramu czasu kubitu EstimatesOverview .

  2. Podczas uruchamiania wielu konfiguracji parametrów target przy użyciu szacowania granic Pareto można zobaczyć oszacowania zasobów dla określonego punktu diagramu czasu przestrzeni, czyli dla każdej pary {liczba kubitów, runtime}. Na przykład poniższy kod przedstawia szacowane użycie szczegółów dla drugiego uruchomienia (estimate index=0) i czwartego (point index=3) najkrótszego środowiska uruchomieniowego.

    EstimateDetails(result[1], 4)
    
  3. Można również zobaczyć diagram przestrzeni dla określonego punktu diagramu czasu przestrzeni. Na przykład poniższy kod przedstawia diagram przestrzeni dla pierwszego uruchomienia kombinacji (estimate index=0) i trzeciego najkrótszego środowiska uruchomieniowego (point index=2).

    SpaceChart(result[0], 2)
    

Wymagania wstępne dotyczące zestawu Qiskit

Włączanie narzędzia do szacowania target zasobów usługi Azure Quantum w obszarze roboczym

Narzędzie do szacowania zasobów jest dostawcą target obliczeń kwantowych firmy Microsoft. Jeśli obszar roboczy został utworzony od czasu wydania narzędzia do szacowania zasobów, dostawca microsoft Quantum Computing został automatycznie dodany do obszaru roboczego.

Jeśli używasz istniejącego obszaru roboczego usługi Azure Quantum:

  1. Otwórz obszar roboczy w Azure Portal.
  2. Na panelu po lewej stronie w obszarze Operacje wybierz pozycję Dostawcy.
  3. Wybierz pozycję + Dodaj dostawcę.
  4. Wybierz pozycję + Dodaj dla usługi Microsoft Quantum Computing.
  5. Wybierz pozycję Learn & Programowanie i wybierz pozycję Dodaj.

Twórca nowy notes w obszarze roboczym

  1. Zaloguj się do Azure Portal i wybierz swój obszar roboczy usługi Azure Quantum.
  2. W obszarze Operacje wybierz pozycję Notesy
  3. Kliknij pozycję Moje notesy i kliknij pozycję Dodaj nowy
  4. W obszarze Typ jądra wybierz pozycję IPython.
  5. Wpisz nazwę pliku, a następnie kliknij przycisk Twórca pliku.

Po otwarciu nowego notesu automatycznie tworzy kod dla pierwszej komórki na podstawie informacji o subskrypcji i obszarze roboczym.

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

Uwaga

O ile nie określono inaczej, należy uruchomić każdą komórkę w kolejności tworzenia, aby uniknąć problemów z kompilacją.

Kliknij trójkątną ikonę "odtwórz" po lewej stronie komórki, aby uruchomić kod.

Ładowanie wymaganych importów

Najpierw należy zaimportować dodatkowe moduły z modułów azure-quantum i qiskit.

Kliknij pozycję + Kod , aby dodać nową komórkę, a następnie dodaj i uruchom następujący kod:

from azure.quantum.qiskit import AzureQuantumProvider
from qiskit import QuantumCircuit, transpile
from qiskit.circuit.library import RGQFTMultiplier

Nawiązywanie połączenia z usługą Azure Quantum

Następnie utwórz obiekt AzureQuantumProvider przy użyciu workspace obiektu z poprzedniej komórki w celu nawiązania połączenia z obszarem roboczym usługi Azure Quantum. Utworzysz wystąpienie zaplecza i ustawisz narzędzie do szacowania zasobów jako .target

provider = AzureQuantumProvider(workspace)
backend = provider.get_backend('microsoft.estimator')

Twórca algorytmu kwantowego

W tym przykładzie utworzysz obwód kwantowy dla mnożnika na podstawie konstrukcji przedstawionej w ruiz-Perez i Garcia-Escartin (arXiv:1411.5949), który używa przekształcenia Quantum Fourier do implementowania arytmetyki.

Rozmiar mnożnika można dostosować, zmieniając zmienną bitwidth . Generowanie obwodu jest opakowane w funkcję, którą można wywołać przy użyciu bitwidth wartości mnożnika. Operacja będzie zawierać dwa rejestry wejściowe, każdy rozmiar określonego bitwidthrejestru i jeden rejestr wyjściowy, który jest dwa razy większy od określonego bitwidth. Funkcja wyświetli również niektóre liczby zasobów logicznych dla mnożnika wyodrębnionego bezpośrednio z obwodu kwantowego.

def create_algorithm(bitwidth):
    print(f"[INFO] Create a QFT-based multiplier with bitwidth {bitwidth}")
    
    # Print a warning for large bitwidths that will require some time to generate and
    # transpile the circuit.
    if bitwidth > 18:
        print(f"[WARN] It will take more than one minute generate a quantum circuit with a bitwidth larger than 18")

    circ = RGQFTMultiplier(num_state_qubits=bitwidth, num_result_qubits=2 * bitwidth)

    # One could further reduce the resource estimates by increasing the optimization_level,
    # however, this will also increase the runtime to construct the algorithm.  Note, that
    # it does not affect the runtime for resource estimation.
    print(f"[INFO] Decompose circuit into intrinsic quantum operations")

    circ = transpile(circ, basis_gates=SUPPORTED_INSTRUCTIONS, optimization_level=0)

    # print some statistics
    print(f"[INFO]   qubit count: {circ.num_qubits}")
    print("[INFO]   gate counts")
    for gate, count in circ.count_ops().items():
        print(f"[INFO]   - {gate}: {count}")

    return circ

Uwaga

Zadania szacowania zasobów fizycznych można przesyłać dla algorytmów, które nie mają stanów T, ale mają co najmniej jedną miarę.

Szacowanie algorytmu kwantowego

Twórca wystąpienie algorytmu create_algorithm przy użyciu funkcji . Rozmiar mnożnika można dostosować, zmieniając zmienną bitwidth .

bitwidth = 4

circ = create_algorithm(bitwidth)

Szacuj zasoby fizyczne dla tej operacji przy użyciu domyślnych założeń. Obwód można przesłać do zaplecza narzędzia do szacowania zasobów przy użyciu run metody , a następnie uruchomić polecenie job.result() , aby poczekać na ukończenie zadania i zwrócić wyniki.

job = backend.run(circ)
result = job.result()
result

Spowoduje to utworzenie tabeli przedstawiającej ogólną liczbę zasobów fizycznych. Szczegóły kosztów można sprawdzić, zwijając grupy, które zawierają więcej informacji.

Porada

Aby uzyskać bardziej kompaktową wersję tabeli wyjściowej, można użyć polecenia result.summary.

Jeśli na przykład zwiniesz grupę Parametrów kubitu logicznego , możesz łatwiej zobaczyć, że odległość kodu korekty błędu wynosi 15.

Parametr kubitu logicznego Wartość
Schemat QEC surface_code
Odległość kodu 15
Kubity fizyczne 450
Czas cyklu logicznego 6us
Szybkość błędów kubitu logicznego 3.00E-10
Refaktoryzacja przeprawy 0.03
Próg korekty błędu 0,01
Formuła czasu cyklu logicznego (4 * + twoQubitGateTime 2 * oneQubitMeasurementTime) * codeDistance
Formuła kubitów fizycznych 2 * codeDistance * codeDistance

W grupie Parametry kubitu fizycznego można zobaczyć fizyczne właściwości kubitu, które zostały przyjęte dla tego oszacowania. Na przykład czas wykonywania pomiaru pojedynczego kubitu i bramki z jednym kubitem przyjmuje się odpowiednio 100 ns i 50 ns.

Porada

Możesz również uzyskać dostęp do danych wyjściowych narzędzia do szacowania zasobów jako słownika języka Python przy użyciu metody result.data().

Aby uzyskać więcej informacji, zobacz pełną listę danych wyjściowych narzędzia do szacowania zasobów.

Diagramy przestrzeni

Rozkład fizycznych kubitów używanych dla algorytmu i fabryk T jest czynnikiem, który może mieć wpływ na projektowanie algorytmu. Możesz zwizualizować tę dystrybucję, aby lepiej zrozumieć szacowane wymagania dotyczące miejsca dla algorytmu.

result.diagram.space

Diagram kołowy przedstawiający rozkład całkowitych kubitów fizycznych między kubitami algorytmu i kubitami fabrycznymi T. Istnieje tabela z podziałem liczby kopii fabrycznych T i liczby kubitów fizycznych na fabrykę T.

Na diagramie kosmicznym przedstawiono proporcję kubitów algorytmu i kubitów fabrycznych T. Należy pamiętać, że liczba kopii fabrycznych T, 28, przyczynia się do liczby fizycznych kubitów fabryk T jako $\text{fabryk T} \cdot \text{fizyczny kubit na fabrykę T}= 28 \cdot 18,000 = 504,000$.

Aby uzyskać więcej informacji, zobacz Szacowanie fizyczne fabryki T.

Zmienianie wartości domyślnych i szacowanie algorytmu

Podczas przesyłania żądania szacowania zasobów dla programu można określić niektóre parametry opcjonalne. jobParams Użyj pola , aby uzyskać dostęp do wszystkich wartości, które można przekazać do wykonania zadania i sprawdzić, które wartości domyślne zostały przyjęte:

result.data()["jobParams"]
{'errorBudget': 0.001,
 'qecScheme': {'crossingPrefactor': 0.03,
  'errorCorrectionThreshold': 0.01,
  'logicalCycleTime': '(4 * twoQubitGateTime + 2 * oneQubitMeasurementTime) * codeDistance',
  'name': 'surface_code',
  'physicalQubitsPerLogicalQubit': '2 * codeDistance * codeDistance'},
 'qubitParams': {'instructionSet': 'GateBased',
  'name': 'qubit_gate_ns_e3',
  'oneQubitGateErrorRate': 0.001,
  'oneQubitGateTime': '50 ns',
  'oneQubitMeasurementErrorRate': 0.001,
  'oneQubitMeasurementTime': '100 ns',
  'tGateErrorRate': 0.001,
  'tGateTime': '50 ns',
  'twoQubitGateErrorRate': 0.001,
  'twoQubitGateTime': '50 ns'}}

Są to target parametry, które można dostosować:

Aby uzyskać więcej informacji, zobacz Target parameters for the Resource Estimator ( Parametry docelowe dla narzędzia do szacowania zasobów).

Zmienianie modelu kubitu

Następnie szacuj koszt dla tego samego algorytmu przy użyciu parametru kubitu opartego na majoranie qubit_maj_ns_e6

job = backend.run(circ,
    qubitParams={
        "name": "qubit_maj_ns_e6"
    })
result = job.result()
result

Można programowo sprawdzić liczby fizyczne. Możesz na przykład zapoznać się ze szczegółami fabryki T, która została utworzona w celu wykonania algorytmu.

result.data()["tfactory"]
{'eccDistancePerRound': [1, 1, 5],
 'logicalErrorRate': 1.6833177305222897e-10,
 'moduleNamePerRound': ['15-to-1 space efficient physical',
  '15-to-1 RM prep physical',
  '15-to-1 RM prep logical'],
 'numInputTstates': 20520,
 'numModulesPerRound': [1368, 20, 1],
 'numRounds': 3,
 'numTstates': 1,
 'physicalQubits': 16416,
 'physicalQubitsPerRound': [12, 31, 1550],
 'runtime': 116900.0,
 'runtimePerRound': [4500.0, 2400.0, 110000.0]}

Uwaga

Domyślnie środowisko uruchomieniowe jest wyświetlane w nanosekundach.

Te dane umożliwiają przedstawienie pewnych wyjaśnień dotyczących sposobu produkcji wymaganych stanów T przez fabryki T.

data = result.data()
tfactory = data["tfactory"]
breakdown = data["physicalCounts"]["breakdown"]
producedTstates = breakdown["numTfactories"] * breakdown["numTfactoryRuns"] * tfactory["numTstates"]

print(f"""A single T factory produces {tfactory["logicalErrorRate"]:.2e} T states with an error rate of (required T state error rate is {breakdown["requiredLogicalTstateErrorRate"]:.2e}).""")
print(f"""{breakdown["numTfactories"]} copie(s) of a T factory are executed {breakdown["numTfactoryRuns"]} time(s) to produce {producedTstates} T states ({breakdown["numTstates"]} are required by the algorithm).""")
print(f"""A single T factory is composed of {tfactory["numRounds"]} rounds of distillation:""")
for round in range(tfactory["numRounds"]):
    print(f"""- {tfactory["numModulesPerRound"][round]} {tfactory["moduleNamePerRound"][round]} unit(s)""")
A single T factory produces 1.68e-10 T states with an error rate of (required T state error rate is 2.77e-08).
23 copies of a T factory are executed 523 time(s) to produce 12029 T states (12017 are required by the algorithm).
A single T factory is composed of 3 rounds of distillation:
- 1368 15-to-1 space efficient physical unit(s)
- 20 15-to-1 RM prep physical unit(s)
- 1 15-to-1 RM prep logical unit(s)

Zmienianie schematu korekty błędów kwantowych

Teraz uruchom ponownie zadanie szacowania zasobów dla tego samego przykładu dla parametrów kubitu opartego na majoranie z floqued schematem QEC. qecScheme

job = backend.run(circ,
    qubitParams={
        "name": "qubit_maj_ns_e6"
    },
    qecScheme={
        "name": "floquet_code"
    })
result_maj_floquet = job.result()
result_maj_floquet

Zmienianie budżetu błędów

Uruchommy ponownie ten sam obwód kwantowy o errorBudget wartości 10%.

job = backend.run(circ,
    qubitParams={
        "name": "qubit_maj_ns_e6"
    },
    qecScheme={
        "name": "floquet_code"
    },
    errorBudget=0.1)
result_maj_floquet_e1 = job.result()
result_maj_floquet_e1

Uwaga

Jeśli wystąpi jakikolwiek problem podczas pracy z narzędziem do szacowania zasobów, zapoznaj się ze stroną Rozwiązywanie problemów lub skontaktuj się z .AzureQuantumInfo@microsoft.com

Następne kroki