Freigeben über


Verschiedene Möglichkeiten zum Ausführen der Ressourcenstimator

In diesem Artikel erfahren Sie, wie Sie mit dem Azure Quantum Resource Estimator arbeiten. Der Ressourcen-Estimator ist sowohl in VS Code als auch online in Azure-Portal verfügbar.

In der folgenden Tabelle sind die verschiedenen Methoden zum Ausführen der Ressourcenschätzung aufgeführt.

Benutzerszenario Plattform Tutorial
Schätzen der Ressourcen eines Q#-Programms Visual Studio Code Wählen Sie "Q#" im VS-Code oben auf der Seite aus.
Schätzen der Ressourcen eines Q#-Programms (erweitert) Jupyter-Notizbuch in Visual Studio Code Wählen Sie "Q#" im Jupyter-Notizbuch oben auf der Seite aus.
Schätzen der Ressourcen eines Qiskit-Programms Azure Quantum-Portal Wählen Sie Qiskit in Azure-Portal oben auf der Seite aus.
Schätzen der Ressourcen eines QIR-Programms Azure Quantum-Portal Qir übermitteln
Verwenden von FCIDUMP-Dateien als Argumentparameter (erweitert) Visual Studio Code Übermitteln eines Quantenchemieproblems

Hinweis

Das Microsoft Quantum Development Kit (Classic QDK) wird nach dem 30. Juni 2024 nicht mehr unterstützt. Wenn Sie ein vorhandener QDK-Entwickler sind, empfehlen wir Ihnen, zum neuen Azure Quantum Development Kit (Modern QDK) zu wechseln, um die Entwicklung von Quantenlösungen fortzusetzen. Weitere Informationen finden Sie unter Migrieren ihres Q#-Codes zum modernen QDK.

Voraussetzungen für VS-Code

Tipp

Sie müssen nicht über ein Azure-Konto verfügen, um den lokalen Ressourcen-Estimator auszuführen.

Erstellen einer neuen Q#-Datei

  1. Öffnen Sie Visual Studio Code und wählen Sie Datei > Neue Textdatei aus, um eine neue Datei zu erstellen.
  2. Speichern Sie die Datei unter dem Namen ShorRE.qs. Diese Datei enthält den Q#-Code für Ihr Programm.

Erstellen des Quantenalgorithmus

Kopieren Sie den folgenden Code in die Datei ShorRE.qs:

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 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 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;
    }
}

Ausführen von Resource Estimator

Der Ressourcen-Estimator bietet sechs vordefinierte Qubit-Parameter, von denen vier gatebasierte Anweisungssätze und zwei über einen Majorana-Anweisungssatz verfügen. Es bietet auch zwei Quantenfehlerkorrekturcodes und surface_codefloquet_code.

In diesem Beispiel führen Sie Resource Estimator mithilfe des qubit_gate_us_e3-Quantenparameters und des Codes surface_code zur Korrektur von Quantenfehlern aus.

  1. Wählen Sie "Ansicht " - Befehlspalette" aus, und geben Sie "Ressource" ein, die die Option "Q#: Ressourcenschätzungen berechnen" anzeigen soll.> Sie können auch in der Liste der folgenden @EntryPoint()Befehle auf "Schätzen" klicken. Wählen Sie diese Option aus, um das Fenster „Resource Estimator“ zu öffnen.

    Screenshot, der zeigt, wie der Befehl

  2. Sie können einen oder mehrere Typen für Qubitparameter und Codes zur Fehlerkorrektur auswählen, um die Ressourcen zu schätzen. Wählen Sie in diesem Beispiel qubit_gate_us_e3 aus, und klicken Sie auf OK.

    Screenshot, der zeigt, wie der Qubit-Parameter aus dem Menü

  3. Geben Sie das Fehlerbudget an, oder übernehmen Sie den Standardwert 0,001. Behalten Sie in diesem Beispiel den Standardwert bei, und drücken Sie die EINGABETASTE.

  4. Drücken Sie die EINGABETASTE , um den Standardergebnisnamen basierend auf dem Dateinamen zu akzeptieren, in diesem Fall ShorRE.

Ergebnisse anzeigen

Der Ressourcen-Estimator stellt mehrere Schätzungen für denselben Algorithmus bereit, wobei jeweils Kompromisse zwischen der Anzahl der Qubits und der Laufzeit angezeigt werden. Das Verständnis des Kompromisses zwischen Laufzeit und Systemmaßstab ist einer der wichtigeren Aspekte der Ressourcenschätzung.

Das Ergebnis der Ressourcenschätzung wird im Fenster Q#-Schätzung angezeigt.

  1. Auf der Registerkarte Ergebnisse wird eine Zusammenfassung der Ressourcenschätzung angezeigt. Klicken Sie auf das Symbol neben der ersten Zeile, um die Spalten auszuwählen, die Sie anzeigen möchten. Sie können unter Ausführungsname, Schätzungstyp, Qubittyp, qec-Schema, Fehlerbudget, logische Qubits, logische Tiefe, Codeabstand, T-Zustände, T-Fabriken, T-Factorybruch, Laufzeit, rQOPS und physische Qubits auswählen.

    Screenshot, der zeigt, wie das Menü angezeigt wird, um die Ausgaben ihrer Wahl für die Ressourcenschätzung auszuwählen.

    In der Spalte "Schätzungstyp " der Ergebnistabelle können Sie die Anzahl der optimalen Kombinationen von {Anzahl von Qubits, Runtime} für Ihren Algorithmus sehen. Diese Kombinationen werden im Raumzeitdiagramm angezeigt.

  2. Das Raumzeitdiagramm zeigt die Kompromisse zwischen der Anzahl der physischen Qubits und der Laufzeit des Algorithmus. In diesem Fall findet der Ressourcen-Estimator 13 verschiedene optimale Kombinationen aus vielen tausend möglichen Kombinationen. Sie können auf jede {Anzahl von Qubits, Laufzeit}-Punkt zeigen, um die Details der Ressourcenschätzung zu diesem Zeitpunkt anzuzeigen.

    Screenshot des Raumzeitdiagramms der Ressourcenschätzung.

    Weitere Informationen finden Sie unter Raumzeitdiagramm.

    Hinweis

    Sie müssen auf einen Punkt des Raumzeitdiagramms klicken, bei dem es sich um ein {Anzahl von Qubits, Laufzeit}-Paar handelt, um das Raumdiagramm und die Details der Ressourcenschätzung anzuzeigen, die diesem Punkt entspricht.

  3. Das Raumdiagramm zeigt die Verteilung physischer Qubits, die für den Algorithmus und die T-Fabriken verwendet werden, die einem {Anzahl von Qubits, Runtime}-Paar entsprechen. Wenn Sie z. B. den äußerst linken Punkt im Raumzeitdiagramm auswählen, sind die zum Ausführen des Algorithmus erforderlichen physischen Qubits 427726, 196686 von denen Algorithmus-Qubits und 231040 T-Factory-Qubits sind.

    Screenshot des Raumdiagramms der Ressourcenschätzung.

  4. Schließlich zeigt die Registerkarte "Ressourcenschätzungen " die vollständige Liste der Ausgabedaten für den Ressourcen-Estimator an, der einem {Anzahl von Qubits, Laufzeit} -Paar entspricht. Sie können die Kostendetails einsehen, indem Sie die Gruppen, die mehr Informationen enthalten, zuklappen. Wählen Sie z. B. den äußerst linken Punkt im Raumzeitdiagramm aus, und reduzieren Sie die Gruppe " Logische Qubit-Parameter ".

    Logischer Qubitparameter Wert
    QEC-Schema surface_code
    Codeabstand 21
    Physische Qubits 882
    Logische Zykluszeit 13 Millisekunden
    Logische Qubit-Fehlerrate 3.00E-13
    Crossing Prefactor 0,03
    Schwellenwert für die Fehlerkorrektur 0.01
    Formel für die logische Zykluszeit (4 × twoQubitGateTime × 2 × oneQubitMeasurementTime) × codeDistance
    Formel für physische Qubits 2 × codeDistance * codeDistance

    Tipp

    Klicken Sie auf Detailzeilen anzeigen, um die Beschreibung der einzelnen Ausgaben der Berichtdaten anzuzeigen.

    Weitere Informationen finden Sie in den vollständigen Berichtsdaten des Ressourcenstimators.

Ändern der target Parameter

Sie können die Kosten für dasselbe Q#-Programm mit einem anderen Qubit-Typ, Fehlerkorrekturcode und Fehlerbudget schätzen. Öffnen Sie das Fenster "Ressource schätzen", indem Sie "Ansicht -> Befehlspalette" auswählen und "Eingabe" eingebenQ#: Calculate Resource Estimates.

Wählen Sie eine beliebige andere Konfiguration aus, z. B. den Parameter Majorana-basiertes Qubit, qubit_maj_ns_e6. Übernehmen Sie den Standardfehlerbudgetwert, oder geben Sie einen neuen ein, und drücken Sie die EINGABETASTE. Der Ressourcenschätzungs-Schätzwert wird mit den neuen target Parametern erneut ausgeführt.

Weitere Informationen finden Sie unter "Zielparameter " für den Ressourcenstimator.

Ausführen mehrerer Konfigurationen von Parametern

Der Azure Quantum Resource Estimator kann mehrere Konfigurationen von target Parametern ausführen und die Ergebnisse der Ressourcenschätzung vergleichen.

  1. Wählen Sie "Ansicht"> aus– Befehlspalette, oder drücken Sie STRG+UMSCHALT+P, und geben Sie einQ#: Calculate Resource Estimates.

  2. Wählen Sie qubit_gate_us_e3, qubit_gate_us_e4, qubit_maj_ns_e4 + floquet_code aus, und qubit_maj_ns_e6 + floquet_code, und klicken Sie auf OK.

  3. Übernehmen Sie den Standardfehlerbudgetwert 0,001, und drücken Sie die EINGABETASTE.

  4. Drücken Sie die EINGABETASTE , um die Eingabedatei zu akzeptieren, in diesem Fall ShorRE.qs.

  5. Bei mehreren Konfigurationen von Parametern werden die Ergebnisse in verschiedenen Zeilen auf der Registerkarte "Ergebnisse " angezeigt.

  6. Das Raumzeitdiagramm zeigt die Ergebnisse für alle Konfigurationen von Parametern. In der ersten Spalte der Ergebnistabelle wird die Legende für jede Konfiguration von Parametern angezeigt. Sie können auf jeden Punkt zeigen, um die Details der Ressourcenschätzung zu diesem Zeitpunkt anzuzeigen.

    Screenshot des Raumzeitdiagramms und der Ergebnistabelle beim Ausführen mehrerer Konfigurationen von Parametern im Ressourcenstimator.

  7. Klicken Sie auf eine {Anzahl von Qubits, Laufzeit}-Punkt des Raumzeitdiagramms, um das entsprechende Raumdiagramm und Berichtsdaten anzuzeigen.

Voraussetzungen für das Jupyter-Notizbuch im VS-Code

Tipp

Sie müssen nicht über ein Azure-Konto verfügen, um den lokalen Ressourcen-Estimator auszuführen.

Erstellen des Quantenalgorithmus

  1. Wählen Sie in VS Code die Optionen Ansicht > Befehlspalette und dann Erstellen aus: Verwenden von Jupyter Notebook.

  2. In der oberen rechten Ecke erkennt und zeigt VS Code die Version von Python und die virtuelle Python-Umgebung an, die für das Notizbuch ausgewählt wurde. Wenn Sie über mehrere Python-Umgebungen verfügen, müssen Sie möglicherweise einen Kernel mit der Kernelauswahl oben rechts auswählen. Wenn keine Umgebung erkannt wurde, finden Sie Informationen zum Einrichten unter Jupyter-Notizbücher in VS Code .

  3. Importieren Sie das qsharp-Paket in der ersten Zelle des Notebooks.

    import qsharp
    
  4. Fügen Sie eine neue Zelle hinzu und kopieren Sie den folgenden Code.

    %%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;
    }
    

Schätzen des Quantenalgorithmus

Schätzen Sie nun die physischen Ressourcen für den RunProgram-Vorgang unter Verwendung der Standardannahmen. Fügen Sie eine neue Zelle hinzu und kopieren Sie den folgenden Code.

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

Die qsharp.estimate-Funktion erstellt ein Ergebnisobjekt, mit dem eine Tabelle mit der Gesamtanzahl der physischen Ressourcen angezeigt werden kann. Sie können die Kostendetails einsehen, indem Sie die Gruppen, die mehr Informationen enthalten, zuklappen. Weitere Informationen finden Sie in den vollständigen Berichtsdaten des Ressourcenstimators.

Reduzieren Sie beispielsweise die Gruppe der logischen Qubit-Parameter, um festzustellen, ob der Codeabstand 21 und die Anzahl der physischen Qubits 882 ist.

Logischer Qubit-Parameter Wert
QEC-Schema surface_code
Codeabstand 21
Physische Qubits 882
Logische Zykluszeit 8 Millisekunden
Logische Qubit-Fehlerrate 3.00E-13
Crossing Prefactor 0,03
Schwellenwert für die Fehlerkorrektur 0.01
Formel für die logische Zykluszeit (4 × twoQubitGateTime × 2 × oneQubitMeasurementTime) × codeDistance
Formel für physische Qubits 2 × codeDistance * codeDistance

Tipp

Für eine kompaktere Version der Ausgabetabelle können Sie result.summary verwenden.

Raumdiagramm

Die Verteilung physischer Qubits, die für den Algorithmus und die T-Fabriken verwendet werden, ist ein Faktor, der sich auf das Design Ihres Algorithmus auswirken kann. Sie können das qsharp-widgets-Paket verwenden, um diese Verteilung zu visualisieren, um die geschätzten Platzanforderungen für den Algorithmus besser zu verstehen.

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

In diesem Beispiel ist die Anzahl der zum Ausführen des Algorithmus erforderlichen physischen Qubits 829766, von denen 196686 Algorithmus-Qubits und 633080 T-Factory-Qubits sind.

Screenshot des Raumdiagramms der Ressourcenschätzung.

Ändern der Standardwerte und Schätzen des Algorithmus

Beim Übermitteln einer Ressourcenschätzungsanforderung für Ihr Programm können Sie einige optionale Parameter angeben. Verwenden Sie das jobParams Feld, um auf alle Parameter zuzugreifen, die target an die Auftragsausführung übergeben werden können, und sehen Sie, welche Standardwerte angenommen wurden:

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'}}

Sie können sehen, dass der Ressourcen-Estimator das qubit_gate_ns_e3- Qubit-Modell, den surface_code-Fehlerkorrekturcode und das Fehlerbudget 0,001 als Standardwerte für die Schätzung verwendet.

Dies sind die target Parameter, die angepasst werden können:

  • errorBudget - das zulässige Gesamtfehlerbudget für den Algorithmus
  • qecScheme - das QEC-Schema (Quantum Error Correction, Quantenfehlerkorrektur)
  • qubitParams - die physischen Qubit-Parameter
  • constraints – die Einschränkungen auf Komponentenebene
  • distillationUnitSpecifications - die Spezifikationen für die Destillationsalgorithmen der T-Fabriken
  • estimateType - einfach oder grenzüberschreitend

Weitere Informationen finden Sie unter "Zielparameter " für den Ressourcenstimator.

Qubit-Modell ändern

Sie können die Kosten für denselben Algorithmus mithilfe des Majorana-basierten Qubit-Parameters, qubitParams, "qubit_maj_ns_e6" schätzen.

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

Ändern des Quantenfehlerkorrekturschemas

Sie können die Ressourcenabschätzung für dasselbe Beispiel mit den Majorana-basierten QEC-Parametern und einem Floqued-QEC-Schema, qecScheme, wiederholen.

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

Fehlerbudget ändern

Führen Sie als Nächstes den gleichen Quantenkreis mit einem errorBudget-Wert von 10 % erneut aus.

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

Batchverarbeitung mit dem Ressourcen-Estimator

Mit dem Azure Quantum Resource Estimator können Sie mehrere Parameterkonfigurationen target ausführen und die Ergebnisse vergleichen. Dies ist nützlich, wenn Sie die Kosten verschiedener Qubit-Modelle, QEC-Schemas oder Fehlerbudgets vergleichen möchten.

  1. Sie können eine Batchschätzung durchführen, indem Sie eine Liste von target Parametern an den params Parameter der qsharp.estimate Funktion übergeben. Führen Sie beispielsweise denselben Algorithmus mit den Standardparametern und den Majorana-basierten Qubit-Parametern mit einem Floquet-QEC-Schema aus.

    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⁻⁶"])
    
    Modell Logische Qubits Logische Tiefe T-Zustände Codeabstand T-Factorys T-Factoryanteil Physische Qubits rQOPS Physische Ausführungszeit
    Gatterbasiert ns, 10⁻³ 223 3,64 M 4,70 M 21 19 76.30 % 829,77k 26,55 M 31 Sek.
    Majorana ns, 10⁻⁶ 223 3,64 M 4,70 M 5 19 63.02 % 79,60k 148,67 M 5 s
  2. Sie können auch eine Liste von Schätzungsparametern mithilfe der EstimatorParams Klasse erstellen.

    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)
    
    Modell Logische Qubits Logische Tiefe T-Zustände Codeabstand T-Factorys T-Factoryanteil Physische Qubits rQOPS Physische Ausführungszeit
    Gatterbasiert µs, 10⁻³ 223 3,64M 4,70 M 17 13 40,54 % 216,77 k 21,86 k 10 Stunden
    Gatterbasiert µs, 10⁻⁴ 223 3,64 M 4,70 M 9 14 43,17 % 63,57 k 41,30 k 5 Stunden
    Gatterbasiert ns, 10⁻³ 223 3,64M 4,70 M 17 16 69,08 % 416,89 k 32,79 M 25 Sek.
    Gatterbasiert ns, 10⁻⁴ 223 3,64M 4,70 M 9 14 43,17 % 63,57 k 61,94 M 13 Sek.
    Majorana ns, 10⁻⁴ 223 3,64M 4,70 M 9 19 82,75 % 501,48 k 82,59 M 10 Sek.
    Majorana ns, 10⁻⁶ 223 3,64M 4,70 M 5 13 31,47 % 42,96 k 148,67 M 5 s

Berechnung der Pareto-Grenze

Bei der Schätzung der Ressourcen eines Algorithmus ist es wichtig, den Kompromiss zwischen der Anzahl der physischen Qubits und der Laufzeit des Algorithmus zu berücksichtigen. Sie könnten die Zuordnung von so vielen physischen Qubits wie möglich in Betracht ziehen, um die Laufzeit des Algorithmus zu reduzieren. Die Anzahl der physischen Qubits ist jedoch durch die Anzahl der physischen Qubits begrenzt, die in der Quantenhardware verfügbar sind.

Die Pareto-Grenzschätzung liefert mehrere Schätzungen für denselben Algorithmus, jeweils mit einem Kompromiss zwischen der Anzahl der Qubits und der Laufzeit.

  1. Um den Ressourcen-Estimator mithilfe der Pareto-Grenzschätzung auszuführen, müssen Sie den "estimateType"target Parameter als "frontier"angeben. Führen Sie z. B. den gleichen Algorithmus mit den Majorana-basierten Qubit-Parametern mit einem Oberflächencode aus, indem Sie die Grenze von Pareto verwenden.

    result = qsharp.estimate("RunProgram()", params=
                                {"qubitParams": { "name": "qubit_maj_ns_e4" },
                                "qecScheme": { "name": "surface_code" },
                                "estimateType": "frontier", # frontier estimation
                                }
                            )
    
  2. Sie können die EstimatesOverview Funktion verwenden, um eine Tabelle mit der Gesamtanzahl der physischen Ressourcen anzuzeigen. Klicken Sie auf das Symbol neben der ersten Zeile, um die Spalten auszuwählen, die Sie anzeigen möchten. Sie können unter Ausführungsname, Schätzungstyp, Qubittyp, qec-Schema, Fehlerbudget, logische Qubits, logische Tiefe, Codeabstand, T-Zustände, T-Fabriken, T-Factorybruch, Laufzeit, rQOPS und physische Qubits auswählen.

    from qsharp_widgets import EstimatesOverview
    EstimatesOverview(result)
    

In der Spalte "Geschätzter Typ " der Ergebnistabelle können Sie die Anzahl der verschiedenen Kombinationen von {Anzahl von Qubits, Runtime} für Ihren Algorithmus sehen. In diesem Fall findet der Ressourcen-Estimator 22 verschiedene optimale Kombinationen aus vielen tausend möglichen Kombinationen.

Raumzeitdiagramm

Die EstimatesOverview Funktion zeigt auch das Raumzeitdiagramm des Ressourcenstimators an.

Das Raumzeitdiagramm zeigt die Anzahl der physischen Qubits und die Laufzeit des Algorithmus für jedes {Anzahl von Qubits, Runtime}-Paaren. Sie können auf jeden Punkt zeigen, um die Details der Ressourcenschätzung zu diesem Zeitpunkt anzuzeigen.

Screenshot des Raumzeitdiagramms mit Grenzschätzung des Ressourcen-Estimators.

Batchverarbeitung mit Pareto-Grenzschätzung

  1. Fügen Sie die Parameter hinzu, um mehrere Konfigurationen von target Parametern mit der Grenzschätzung zu schätzen und zu "estimateType": "frontier", vergleichen.

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

    Screenshot des Raumzeitdiagramms des Ressourcenstimators bei Verwendung der Pareto-Grenzschätzung und mehrerer Konfigurationen von Parametern.

    Hinweis

    Sie können Farben definieren und Namen für das Qubit-Zeitdiagramm mithilfe der EstimatesOverview Funktion ausführen.

  2. Wenn Sie mehrere Konfigurationen von target Parametern mithilfe der Pareto-Grenzschätzung ausführen, können Sie die Ressourcenschätzungen für einen bestimmten Punkt des Raumzeitdiagramms anzeigen, d. h. für jede {Anzahl von Qubits, Laufzeit}-Paaren. Der folgende Code zeigt z. B. die Geschätzte Detailnutzung für die zweite Ausführung (Schätzungsindex=0) und die vierte (Punktindex=3) kürzeste Laufzeit.

    EstimateDetails(result[1], 4)
    
  3. Sie können auch das Raumdiagramm für einen bestimmten Punkt des Raumzeitdiagramms anzeigen. Der folgende Code zeigt z. B. das Leerzeichendiagramm für die erste Ausführung von Kombinationen (Schätzungsindex=0) und die dritte kürzeste Laufzeit (Punktindex=2).

    SpaceChart(result[0], 2)
    

Voraussetzungen für Qiskit

  • Ein Azure-Konto mit einem aktiven Abonnement. Wenn Sie nicht über ein Azure-Konto verfügen, registrieren Sie sich kostenlos, und registrieren Sie sich für ein Kostenpflichtiges Abonnement.
  • Azure Quantum-Arbeitsbereich Weitere Informationen finden Sie unter Erstellen eines Azure Quantum-Arbeitsbereichs.

Aktivieren der Azure Quantum Resource Estimator target in Ihrem Arbeitsbereich

Der Resource Estimator ist ein target Microsoft Quantum Computing-Anbieter. Wenn Sie seit der Veröffentlichung des Resource Estimator einen Arbeitsbereich erstellt haben, wurde der Microsoft Quantum Computing-Anbieter automatisch zu Ihrem Arbeitsbereich hinzugefügt.

Wenn Sie einen vorhandenen Azure Quantum-Arbeitsbereich verwenden:

  1. Öffnen Sie Ihren Arbeitsbereich im Azure-Portal.
  2. Wählen Sie im linken Bereich unter Vorgänge die Option Anbieter aus.
  3. Wählen Sie + Anbieter hinzufügen aus.
  4. Wählen Sie + Hinzufügen für Microsoft Quantum Computing aus.
  5. Wählen Sie "Lernen und Entwickeln" und dann "Hinzufügen" aus.

Erstellen eines neuen Notebooks in Ihrem Arbeitsbereich

  1. Melden Sie sich beim Azure-Portal an, und wählen Sie Ihren Azure Quantum-Arbeitsbereich aus.
  2. Wählen Sie unter "Vorgänge" die Option "Notizbücher" aus .
  3. Klicken Sie auf "Meine Notizbücher ", und klicken Sie auf " Neu hinzufügen".
  4. Wählen Sie unter Kerneltyp die Option IPython aus.
  5. Geben Sie einen Namen für die Datei ein, und klicken Sie auf " Datei erstellen".

Wenn Ihr neues Notebook geöffnet wird, wird der Code für die erste Zelle automatisch auf der Grundlage Ihrer Abonnement- und Arbeitsbereichsinformationen erstellt.

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

Hinweis

Sofern nicht anders angegeben, müssen Sie die einzelnen Zellen in der Reihenfolge ihrer Erstellung ausführen, um Kompilierungsprobleme zu vermeiden.

Klicken Sie links neben der Zelle auf das dreieckige Wiedergabesymbol, um den Code auszuführen.

Laden der erforderlichen Importe

Zunächst müssen Sie ein zusätzliches Modul aus Azure-Quantum und qiskit.

Klicken Sie auf + Code, um eine neue Zelle hinzuzufügen, fügen Sie dann den folgenden Code hinzu, und führen Sie ihn aus:

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

Herstellen einer Verbindung mit dem Azure Quantum-Dienst

Erstellen Sie als Nächstes ein AzureQuantumProvider-Objekt mithilfe des workspace Objekts aus der vorherigen Zelle, um eine Verbindung mit Ihrem Azure Quantum-Arbeitsbereich herzustellen. Sie erstellen eine Back-End-Instanz und legen den Ressourcenstimator als Ihre target.

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

Erstellen des Quantenalgorithmus

In diesem Beispiel erstellen Sie einen Quantenkreis für einen Multiplikator basierend auf der Konstruktion in Ruiz-Perez und Garc-Escartin (arXiv:1411.5949), die die Quantum Fourier Transform verwendet, um Arithmetik zu implementieren.

Sie können die Größe des Multiplikators anpassen, indem Sie die bitwidth Variable ändern. Die Schaltkreisgenerierung wird in eine Funktion umschlossen, die mit dem bitwidth Wert des Multiplikators aufgerufen werden kann. Der Vorgang verfügt über zwei Eingaberegister, jede Größe des angegebenen bitwidthWerts und ein Ausgaberegister, das doppelt so groß ist wie die angegebene bitwidth. Die Funktion druckt auch einige logische Ressourcenanzahlen für den Multiplizierer, der direkt aus dem Quantenkreis extrahiert wird.

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

Hinweis

Sie können physische Ressourcenschätzungsaufträge für Algorithmen übermitteln, die keine T-Zustände aufweisen, aber mindestens eine Messung aufweisen.

Schätzen des Quantenalgorithmus

Erstellen Sie eine Instanz Ihres Algorithmus mithilfe der create_algorithm Funktion. Sie können die Größe des Multiplikators anpassen, indem Sie die bitwidth Variable ändern.

bitwidth = 4

circ = create_algorithm(bitwidth)

Schätzen Sie die physischen Ressourcen für diesen Vorgang mithilfe der Standardannahmen. Sie können den Schaltkreis mithilfe der run Methode an das Resource Estimator-Back-End übermitteln und dann ausführen job.result() , bis der Auftrag abgeschlossen ist, und die Ergebnisse zurückgeben.

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

Dadurch wird eine Tabelle erstellt, in der die Gesamtanzahl der physischen Ressourcen angezeigt wird. Sie können die Kostendetails einsehen, indem Sie die Gruppen, die mehr Informationen enthalten, zuklappen.

Tipp

Für eine kompaktere Version der Ausgabetabelle können Sie result.summary verwenden.

Wenn Sie beispielsweise die Gruppe "Logische Qubit-Parameter " reduzieren, können Sie leichter erkennen, dass der Entfernung des Fehlerkorrekturcodes 15 ist.

Logischer Qubitparameter Wert
QEC-Schema surface_code
Codeabstand 15
Physische Qubits 450
Logische Zykluszeit 6us
Fehlerrate logischer Qubits 3.00E-10
Vorfaktor der Kreuzung 0,03
Schwellenwert für die Fehlerkorrektur 0.01
Formel für die logische Zykluszeit (4 × twoQubitGateTime × 2 × oneQubitMeasurementTime) × codeDistance
Formel für physische Qubits 2 × codeDistance * codeDistance

In der Gruppe "Physische Qubitparameter " können Sie die physikalischen Qubiteigenschaften sehen, die für diese Schätzung angenommen wurden. So beträgt beispielsweise die Zeit für die Durchführung der Messung eines einzelnen Qubits und eines einzelnen Qubitgatters 100 ns bzw. 50 ns.

Tipp

Sie können auch auf die Ausgabe des Resource Estimator als Python-Wörterbuch mithilfe der Result.data()- Methode zugreifen.

Weitere Informationen finden Sie in der vollständigen Liste der Ausgabedaten für den Ressourcen-Estimator.

Raumdiagramme

Die Verteilung physischer Qubits, die für den Algorithmus und die T-Fabriken verwendet werden, ist ein Faktor, der sich auf das Design Ihres Algorithmus auswirken kann. Sie können diese Verteilung visualisieren, um die geschätzten Platzanforderungen für den Algorithmus besser zu verstehen.

result.diagram.space

Kreisdiagramm mit der Verteilung der gesamten physischen Qubits zwischen Algorithmus-Qubits und T-Factory-Qubits. Es gibt eine Tabelle mit der Aufschlüsselung der Anzahl der T-Werkskopien und der Anzahl der physischen Qubits pro T-Fabrik.

Das Raumdiagramm zeigt den Anteil von Algorithmus-Qubits und T-Factory-Qubits. Beachten Sie, dass die Anzahl der T Factorykopien, 28, zur Anzahl der physischen Qubits für T-Fabriken als $\text{T factories} \cdot \text{physical qubit per T factory}= 28 \cdot 18.000 = 504.000$ beiträgt.

Weitere Informationen finden Sie unter T factory physical estimation.

Ändern der Standardwerte und Schätzen des Algorithmus

Beim Übermitteln einer Ressourcenschätzungsanforderung für Ihr Programm können Sie einige optionale Parameter angeben. Verwenden Sie das jobParams Feld, um auf alle Werte zuzugreifen, die an die Auftragsausführung übergeben werden können, und sehen Sie, welche Standardwerte angenommen wurden:

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'}}

Dies sind die target Parameter, die angepasst werden können:

  • errorBudget - das gesamt zulässige Fehlerbudget
  • qecScheme - das QEC-Schema (Quantum Error Correction, Quantenfehlerkorrektur)
  • qubitParams - die physischen Qubit-Parameter
  • constraints – die Einschränkungen auf Komponentenebene
  • distillationUnitSpecifications - die Spezifikationen für die Destillationsalgorithmen der T-Fabriken

Weitere Informationen finden Sie unter "Zielparameter " für den Ressourcenstimator.

Qubit-Modell ändern

Als Nächstes schätzen Sie die Kosten für denselben Algorithmus mithilfe des Majorana-basierten Qubit-Parameters. qubit_maj_ns_e6

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

Sie können die physischen Zählungen programmgesteuert prüfen. Beispielsweise können Sie Details zur T-Factory untersuchen, die erstellt wurde, um den Algorithmus auszuführen.

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]}

Hinweis

Standardmäßig wird die Laufzeit in Nanosekunden angezeigt.

Sie können diese Daten verwenden, um einige Erläuterungen darüber zu erzeugen, wie die T-Fabriken die erforderlichen T-Zustände erzeugen.

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)

Ändern des Quantenfehlerkorrekturschemas

Führen Sie nun den Ressourcenschätzungsauftrag für dasselbe Beispiel für die Majorana-basierten Qubit-Parameter mit einem floqued QEC-Schema erneut aus. qecScheme

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

Fehlerbudget ändern

Lassen Sie uns den gleichen Quantenkreis mit einem errorBudget Wert von 10 % erneut ausführen.

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

Hinweis

Wenn beim Arbeiten mit der Ressourcenschätzung probleme auftreten, schauen Sie sich die Seite "Problembehandlung" an, oder wenden Sie sich an den Kontakt AzureQuantumInfo@microsoft.com.

Nächste Schritte