Condividi tramite


Diversi modi per eseguire Lo strumento di stima delle risorse

Questo articolo illustra come usare Lo strumento di stima delle risorse di Azure Quantum. Lo strumento di stima delle risorse è disponibile sia in VS Code che online in portale di Azure.

La tabella seguente illustra i diversi modi per eseguire Resource Estimator.

Scenario utente Piattaforma Esercitazione
Stimare le risorse di un programma Q# Visual Studio Code Selezionare Q# in VS Code nella parte superiore della pagina
Stimare le risorse di un programma Q# (avanzato) Jupyter Notebook in Visual Studio Code Selezionare Q# in Jupyter Notebook nella parte superiore della pagina
Stimare le risorse di un programma Qiskit Portale di Azure Quantum Selezionare Qiskit in portale di Azure nella parte superiore della pagina
Stimare le risorse di un programma QIR Portale di Azure Quantum Inviare QIR
Usare i file FCIDUMP come parametri di argomento (avanzati) Visual Studio Code Inviare un problema di chimica quantistica

Nota

Microsoft Quantum Development Kit (QDK classico) non sarà più supportato dopo il 30 giugno 2024. Se si è uno sviluppatore QDK esistente, è consigliabile passare al nuovo Azure Quantum Development Kit (Modern QDK) per continuare a sviluppare soluzioni quantistiche. Per altre informazioni, vedere Eseguire la migrazione del codice Q# al QDK moderno.

Prerequisiti per VS Code

Suggerimento

Non è necessario disporre di un account Azure per eseguire lo strumento di stima delle risorse locale.

Creare un nuovo file Q#

  1. Aprire Visual Studio Code e selezionare File > Nuovo file di testo per creare un nuovo file.
  2. Salvare il file come ShorRE.qs. Questo file conterrà il codice Q# per il programma.

Creare l'algoritmo quantistico

Copiare il codice seguente nel ShorRE.qs file :

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

Eseguire lo strumento di stima delle risorse

Resource Estimator offre sei parametri qubit predefiniti, quattro dei quali hanno set di istruzioni basati su gate e due con un set di istruzioni Majorana. Offre anche due codici di correzione degli errori quantistici e surface_codefloquet_code.

In questo esempio si esegue l'oggetto Resource Estimator usando il qubit_gate_us_e3 parametro qubit e il surface_code codice di correzione degli errori quantistici.

  1. Selezionare Visualizza -> Riquadro comandi e digitare "risorsa" che dovrebbe visualizzare l'opzione Q#: Calculate Resource Estimates (Calcola stime risorse). È anche possibile fare clic su Stima nell'elenco dei comandi seguenti @EntryPoint(). Selezionare questa opzione per aprire la finestra Stima risorse.

    Screenshot che mostra come selezionare il comando di stima dall'elenco di obiettivo del codice.

  2. È possibile selezionare uno o più tipi di codice Qubit + Correzione errori per stimare le risorse. Per questo esempio, selezionare qubit_gate_us_e3 e fare clic su OK.

    Screenshot che mostra come selezionare il parametro qubit dal menu di stima delle risorse.

  3. Specificare il budget degli errori o accettare il valore predefinito 0,001. Per questo esempio, lasciare il valore predefinito e premere INVIO.

  4. Premere INVIO per accettare il nome del risultato predefinito in base al nome file, in questo caso ShorRE.

Visualizzare i risultati

Lo strumento di stima delle risorse fornisce più stime per lo stesso algoritmo, ognuna delle quali mostra i compromessi tra il numero di qubit e il runtime. Comprendere il compromesso tra runtime e scalabilità di sistema è uno degli aspetti più importanti della stima delle risorse.

Il risultato della stima delle risorse viene visualizzato nella finestra Stima Q#.

  1. Nella scheda Risultati viene visualizzato un riepilogo della stima delle risorse. Fare clic sull'icona accanto alla prima riga per selezionare le colonne da visualizzare. È possibile scegliere tra nome di esecuzione, tipo di stima, tipo di qubit, schema qec, budget degli errori, qubit logici, profondità logica, distanza del codice, stati T, T factory, frazione di T factory, runtime, rQOPS e qubit fisici.

    Screenshot che mostra come visualizzare il menu per selezionare gli output di stima delle risorse desiderati.

    Nella colonna Tipo di stima della tabella dei risultati è possibile visualizzare il numero di combinazioni ottimali di {numero di qubit, runtime} per l'algoritmo. Queste combinazioni possono essere visualizzate nel diagramma dello spazio-tempo.

  2. Il diagramma spazio-tempo mostra i compromessi tra il numero di qubit fisici e il runtime dell'algoritmo. In questo caso, lo strumento di stima delle risorse trova 13 diverse combinazioni ottimali su molte migliaia di possibili. È possibile passare il puntatore del mouse su ogni {numero di qubit, runtime} per visualizzare i dettagli della stima delle risorse in quel punto.

    Screenshot che mostra il diagramma spazio-tempo dell'oggetto Resource Estimator.

    Per altre informazioni, vedere Diagramma spazio-tempo.

    Nota

    È necessario fare clic su un punto del diagramma spaziale, ovvero una coppia {numero di qubit, runtime} per visualizzare il diagramma spaziale e i dettagli della stima delle risorse corrispondente a tale punto.

  3. Il diagramma spaziale mostra la distribuzione dei qubit fisici usati per l'algoritmo e le factory T, corrispondenti a una coppia {numero di qubit, runtime}. Ad esempio, se si seleziona il punto più a sinistra nel diagramma dello spazio-tempo, il numero di qubit fisici necessari per eseguire l'algoritmo è 427726, 196686 di cui sono qubit di algoritmo e 231040 di cui sono qubit di T factory.

    Screenshot che mostra il diagramma dello spazio dello strumento di stima delle risorse.

  4. Infine, nella scheda Stime risorse viene visualizzato l'elenco completo dei dati di output per l'oggetto Resource Estimator corrispondente a una coppia di {numero di qubit, runtime} . È possibile controllare i dettagli dei costi comprimendo i gruppi, che includono altre informazioni. Ad esempio, selezionare il punto più a sinistra nel diagramma spazio-tempo e comprimere il gruppo di parametri qubit logici .

    Parametro qubit logico Valore
    Schema Correzione degli errori quantistici surface_code
    Distanza di codice 21
    Qubit fisici 882
    Tempo ciclo logico 13 millisecs
    Frequenza di errore del qubit logico 3.00E-13
    Attraversamento del prefactoring 0.03
    Soglia di correzione degli errori 0,01
    Formula tempo del ciclo logico (4 * twoQubitGateTime + 2 * oneQubitMeasurementTime) * codeDistance
    Formula qubit fisici 2 * codeDistance * codeDistance

    Suggerimento

    Fare clic su Mostra righe dettagliate per visualizzare la descrizione di ogni output dei dati del report.

    Per altre informazioni, vedere i dati completi del report di Resource Estimator.

Modificare i target parametri

È possibile stimare il costo per lo stesso programma Q# usando altri tipi di qubit, codice di correzione degli errori e budget degli errori. Aprire la finestra Stima risorse selezionando Visualizza -> Riquadro comandi e digitare Q#: Calculate Resource Estimates.

Selezionare qualsiasi altra configurazione, ad esempio il parametro qubit basato su Majorana, qubit_maj_ns_e6. Accettare il valore predefinito del budget degli errori o immetterne uno nuovo e premere INVIO. Lo strumento di stima delle risorse esegue nuovamente la stima con i nuovi target parametri.

Per altre informazioni, vedere Parametri di destinazione per Lo strumento di stima delle risorse.

Eseguire più configurazioni di parametri

Azure Quantum Resource Estimator può eseguire più configurazioni di target parametri e confrontare i risultati della stima delle risorse.

  1. Selezionare Visualizza -> Riquadro comandi oppure premere CTRL+MAIUSC+P e digitare Q#: Calculate Resource Estimates.

  2. Selezionare qubit_gate_us_e3, qubit_gate_us_e4, qubit_maj_ns_e4 + floquet_code e qubit_maj_ns_e6 + floquet_code e fare clic su OK.

  3. Accettare il valore predefinito del budget di errore 0,001 e premere INVIO.

  4. Premere INVIO per accettare il file di input, in questo caso ShorRE.qs.

  5. Nel caso di più configurazioni di parametri, i risultati vengono visualizzati in righe diverse nella scheda Risultati .

  6. Il diagramma Spazio-tempo mostra i risultati per tutte le configurazioni dei parametri. La prima colonna della tabella dei risultati visualizza la legenda per ogni configurazione dei parametri. È possibile passare il puntatore del mouse su ogni punto per visualizzare i dettagli della stima delle risorse in quel punto.

    Screenshot che mostra il diagramma spazio-tempo e la tabella dei risultati durante l'esecuzione di più configurazioni di parametro in Resource Estimator.

  7. Fare clic su un punto {number of qubits, runtime} (Numero di qubit, runtime) del diagramma spazio-tempo per visualizzare i dati corrispondenti del diagramma spaziale e del report.

Prerequisiti per Jupyter Notebook in VS Code

Suggerimento

Non è necessario disporre di un account Azure per eseguire lo strumento di stima delle risorse locale.

Creare l'algoritmo quantistico

  1. In VS Code, selezionare Visualizza > Riquadro comandi e selezionare Crea: Nuovo Jupyter Notebook.

  2. In alto a destra VS Code rileverà e visualizzerà la versione di Python e l'ambiente Python virtuale selezionato per il notebook. Se si dispone di più ambienti Python, potrebbe essere necessario selezionare un kernel usando la selezione kernel in alto a destra. Se non è stato rilevato alcun ambiente, vedere Jupyter Notebooks in VS Code per informazioni sull'installazione.

  3. Nella prima cella del notebook importare il pacchetto qsharp.

    import qsharp
    
  4. Aggiungere una nuova cella e copiare il codice seguente.

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

Stimare l'algoritmo quantistico

Stimare ora le risorse fisiche per l'operazione RunProgram usando i presupposti predefiniti. Aggiungere una nuova cella e copiare il codice seguente.

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

La funzione qsharp.estimate crea un oggetto risultato, che può essere usato per visualizzare una tabella con il conto complessivo delle risorse fisiche. È possibile controllare i dettagli dei costi comprimendo i gruppi, che includono altre informazioni. Per altre informazioni, vedere i dati completi del report di Resource Estimator.

Ad esempio, comprimere il gruppo di Parametri qubit logici per vedere che la distanza del codice è 21 e il numero di qubit fisici è 882.

Parametro qubit logico Valore
Schema Correzione degli errori quantistici surface_code
Distanza di codice 21
Qubit fisici 882
Tempo ciclo logico 8 millisecs
Frequenza di errore del qubit logico 3.00E-13
Attraversamento del prefactoring 0.03
Soglia di correzione degli errori 0,01
Formula tempo del ciclo logico (4 * twoQubitGateTime + 2 * oneQubitMeasurementTime) * codeDistance
Formula qubit fisici 2 * codeDistance * codeDistance

Suggerimento

Per una versione più compatta della tabella di output, è possibile usare result.summary.

Diagramma spaziale

La distribuzione dei qubit fisici usati per l'algoritmo e le factory T è un fattore che può influire sulla progettazione dell'algoritmo. È possibile usare il pacchetto qsharp-widgets per visualizzare questa distribuzione per comprendere meglio i requisiti di spazio stimati per l'algoritmo.

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

In questo esempio, il numero di qubit fisici necessari per eseguire l'algoritmo è 829766, di cui 196686 sono qubit di algoritmo e 633080 sono qubit di T factory.

Screenshot che mostra il diagramma dello spazio dello strumento di stima delle risorse.

Modificare i valori predefiniti e stimare l'algoritmo

Quando si invia una richiesta di stima delle risorse per il programma, è possibile specificare alcuni parametri facoltativi. Usare il jobParams campo per accedere a tutti i target parametri che possono essere passati all'esecuzione del processo e verificare quali valori predefiniti sono stati presupposti:

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

È possibile notare che l'oggetto Strumento di stima delle risorse accetta il modello qubit qubit_gate_ns_e3, il codice di correzione degli errori surface_code e il budget degli errori 0,001 come valori predefiniti per la stima.

Questi sono i target parametri che è possibile personalizzare:

  • errorBudget: il budget complessivo degli errori consentito per l'algoritmo
  • qecScheme: schema di correzione degli errori quantistici (QEC)
  • qubitParams: parametri qubit fisici
  • constraints: i vincoli a livello di componente
  • distillationUnitSpecifications: le specifiche per gli algoritmi di crittografia delle factory T
  • estimateType: singola o frontiera

Per altre informazioni, vedere Parametri di destinazione per Lo strumento di stima delle risorse.

Modificare il modello qubit

È possibile stimare il costo per lo stesso algoritmo usando il parametro qubit basato su Majorana, qubitParams, "qubit_maj_ns_e6".

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

Modificare lo schema di correzione degli errori quantistici

È possibile rieseguire il processo di stima delle risorse per lo stesso esempio nei parametri qubit basati su Majorana con uno schema QEC floqued, qecScheme.

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

Modificare il budget degli errori

Eseguire quindi di nuovo lo stesso circuito quantistico con un valore errorBudget pari al 10%.

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

Invio in batch con lo strumento di stima delle risorse

Azure Quantum Resource Estimator consente di eseguire più configurazioni di target parametri e confrontare i risultati. Ciò è utile quando si vuole confrontare il costo di modelli qubit diversi, schemi QEC o budget di errore.

  1. È possibile eseguire una stima batch passando un elenco di target parametri al params parametro della qsharp.estimate funzione. Ad esempio, eseguire lo stesso algoritmo con i parametri predefiniti e i parametri qubit basati su Majorana con uno schema QEC floquet.

    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⁻⁶"])
    
    Modello Qubit logici Profondità logica T states Distanza di codice T factory Frazione T factory Qubit fisici rQOPS Runtime fisico
    Basato su gate ns 10⁻³ 223 3,64 M 4.70M 21 19 76.30 % 829.77k 26,55 M 31 sec
    Majorana ns 10⁻⁶ 223 3,64 M 4.70M 5 19 63.02 % 79.60k 148.67M 5 sec
  2. È anche possibile costruire un elenco di parametri di stima usando la EstimatorParams classe .

    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)
    
    Modello Qubit logici Profondità logica T states Distanza di codice T factory Frazione T factory Qubit fisici rQOPS Runtime fisico
    Basato su gate µs 10⁻³ 223 3,64M 4.70M 17 13 40.54 % 216.77k 21,86 k 10 ore
    Basato su gate µs 10⁻⁴ 223 3,64 M 4.70M 9 14 43.17 % 63,57 k 41.30k 5 ore
    Basato su gate ns 10⁻³ 223 3,64M 4.70M 17 16 69.08 % 416.89k 32,79M 25 sec
    Basato su gate ns 10⁻⁴ 223 3,64M 4.70M 9 14 43.17 % 63,57 k 61,94M 13 sec
    Majorana ns 10⁻⁴ 223 3,64M 4.70M 9 19 82.75 % 501.48k 82.59M 10 sec
    Majorana ns 10⁻⁶ 223 3,64M 4.70M 5 13 31.47 % 42.96k 148.67M 5 sec

Esecuzione della stima della frontiera Pareto

Quando si stimano le risorse di un algoritmo, è importante considerare il compromesso tra il numero di qubit fisici e il runtime dell'algoritmo. È possibile considerare l'allocazione del maggior numero possibile di qubit fisici per ridurre il runtime dell'algoritmo. Tuttavia, il numero di qubit fisici è limitato dal numero di qubit fisici disponibili nell'hardware quantistico.

La stima della frontiera Pareto fornisce più stime per lo stesso algoritmo, ognuna con un compromesso tra il numero di qubit e il runtime.

  1. Per eseguire Lo strumento di stima delle risorse usando la stima della frontiera Pareto, è necessario specificare il "estimateType"target parametro come "frontier". Ad esempio, eseguire lo stesso algoritmo con i parametri qubit basati su Majorana con un codice di superficie usando la stima della frontiera Pareto.

    result = qsharp.estimate("RunProgram()", params=
                                {"qubitParams": { "name": "qubit_maj_ns_e4" },
                                "qecScheme": { "name": "surface_code" },
                                "estimateType": "frontier", # frontier estimation
                                }
                            )
    
  2. È possibile usare la EstimatesOverview funzione per visualizzare una tabella con i conteggi complessivi delle risorse fisiche. Fare clic sull'icona accanto alla prima riga per selezionare le colonne da visualizzare. È possibile scegliere tra nome di esecuzione, tipo di stima, tipo di qubit, schema qec, budget degli errori, qubit logici, profondità logica, distanza del codice, stati T, T factory, frazione di T factory, runtime, rQOPS e qubit fisici.

    from qsharp_widgets import EstimatesOverview
    EstimatesOverview(result)
    

Nella colonna Tipo di stima della tabella dei risultati è possibile visualizzare il numero di combinazioni diverse di {numero di qubit, runtime} per l'algoritmo. In questo caso, Lo strumento di stima delle risorse trova 22 combinazioni ottimali diverse tra molte migliaia di possibili.

Diagramma spazio-tempo

La EstimatesOverview funzione visualizza anche il diagramma spazio-tempo di Stima risorse.

Il diagramma dello spazio-tempo mostra il numero di qubit fisici e il runtime dell'algoritmo per ogni coppia {numero di qubit, runtime}. È possibile passare il puntatore del mouse su ogni punto per visualizzare i dettagli della stima delle risorse in quel punto.

Screenshot che mostra il diagramma dello spazio-tempo con la stima della frontiera dello strumento di stima delle risorse.

Invio in batch con stima della frontiera Pareto

  1. Per stimare e confrontare più configurazioni di target parametri con la stima della frontiera, aggiungere "estimateType": "frontier", ai parametri .

    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 che mostra il diagramma dello spazio-tempo dello strumento di stima delle risorse quando si usa la stima della frontiera Pareto e più configurazioni di parametri.

    Nota

    È possibile definire i colori ed eseguire i nomi per il diagramma di qubit-time usando la EstimatesOverview funzione .

  2. Quando si eseguono più configurazioni di target parametri usando la stima della frontiera Pareto, è possibile visualizzare le stime delle risorse per un punto specifico del diagramma dello spazio-tempo, vale a dire per ogni coppia {numero di qubit, runtime}. Ad esempio, il codice seguente mostra l'utilizzo dei dettagli della stima per la seconda esecuzione (stima index=0) e il quarto runtime (indice punto=3) più breve.

    EstimateDetails(result[1], 4)
    
  3. È anche possibile visualizzare il diagramma spaziale per un punto specifico del diagramma dello spazio-tempo. Ad esempio, il codice seguente mostra il diagramma spaziale per la prima esecuzione di combinazioni (stima index=0) e il terzo runtime più breve (indice punto=2).

    SpaceChart(result[0], 2)
    

Prerequisiti per Qiskit

  • Un account Azure con una sottoscrizione attiva. Se non si ha un account Azure, registrarsi gratuitamente e iscriversi per ottenere una sottoscrizione con pagamento in base al consumo.
  • Un'area di lavoro di Azure Quantum. Per altre informazioni, vedere Creare un'area di lavoro di Azure Quantum.

Abilitare Lo strumento target di stima delle risorse di Azure Quantum nell'area di lavoro

Lo strumento di stima delle risorse è un target provider di calcolo Quantistico Microsoft. Se è stata creata un'area di lavoro dopo il rilascio di Resource Estimator, il provider microsoft Quantum Computing è stato aggiunto automaticamente all'area di lavoro.

Se si usa un'area di lavoro di Azure Quantum esistente :

  1. Aprire l'area di lavoro nel portale di Azure.
  2. Nel pannello sinistro, in Operazioni, selezionare Provider.
  3. Selezionare + Aggiungi un provider.
  4. Selezionare + Aggiungi per Microsoft Quantum Computing.
  5. Selezionare Learn & Develop (Scopri e sviluppa ) e selezionare Add (Aggiungi).

Creare un nuovo notebook nell'area di lavoro

  1. Accedere al portale di Azure e selezionare l'area di lavoro di Azure Quantum.
  2. In Operazioni selezionare Notebook
  3. Fare clic su My notebooks (Notebook personali) e fare clic su Add New (Aggiungi nuovo)
  4. In Kernel Type (Tipo di kernel) selezionare IPython.
  5. Digitare un nome per il file e fare clic su Crea file.

Quando si apre un nuovo notebook, il codice per la prima cella viene creato automaticamente, in base alle informazioni relative alla sottoscrizione e all'area di lavoro.

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

Nota

Se non diversamente specificato, è necessario eseguire ogni cella in ordine durante la creazione per evitare problemi di compilazione.

Fare clic sull'icona triangolare "riproduci" a sinistra della cella per eseguire il codice.

Caricare le importazioni necessarie

Prima di tutto, è necessario importare moduli aggiuntivi da azure-quantum e qiskit.

Fare clic su + Codice per aggiungere una nuova cella, quindi aggiungere ed eseguire il codice seguente:

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

Connettersi al servizio Azure Quantum

Creare quindi un oggetto AzureQuantumProvider usando l'oggetto della cella precedente per connettersi all'area workspace di lavoro di Azure Quantum. Si crea un'istanza back-end e si imposta Resource Estimator come target.

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

Creare l'algoritmo quantistico

In questo esempio viene creato un circuito quantistico per un moltiplicatore in base alla costruzione presentata in Conversion-Perez e Garcia-Escartin (arXiv:1411.5949) che usa la trasformazione Quantum Fourier per implementare l'aritmetica.

È possibile modificare le dimensioni del moltiplicatore modificando la bitwidth variabile. La generazione del circuito viene sottoposta a wrapping in una funzione che può essere chiamata con il bitwidth valore del moltiplicatore. L'operazione avrà due registri di input, ognuna delle dimensioni dell'oggetto specificato bitwidthe un registro di output che corrisponde al doppio delle dimensioni dell'oggetto specificato bitwidth. La funzione stampa anche alcuni conteggi delle risorse logiche per il moltiplicatore estratto direttamente dal circuito quantistico.

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

Nota

È possibile inviare processi di stima delle risorse fisiche per algoritmi che non hanno stati T, ma che hanno almeno una misura.

Stimare l'algoritmo quantistico

Creare un'istanza dell'algoritmo usando la create_algorithm funzione . È possibile modificare le dimensioni del moltiplicatore modificando la bitwidth variabile.

bitwidth = 4

circ = create_algorithm(bitwidth)

Stimare le risorse fisiche per questa operazione usando i presupposti predefiniti. È possibile inviare il circuito al back-end di Stima risorse usando il run metodo e quindi eseguire job.result() per attendere il completamento del processo e restituire i risultati.

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

Viene creata una tabella che mostra i conteggi complessivi delle risorse fisiche. È possibile controllare i dettagli dei costi comprimendo i gruppi, che includono altre informazioni.

Suggerimento

Per una versione più compatta della tabella di output, è possibile usare result.summary.

Ad esempio, se si comprime il gruppo di parametri qubit logici , è possibile vedere più facilmente che la distanza del codice di correzione degli errori è 15.

Parametro qubit logico Valore
Schema Correzione degli errori quantistici surface_code
Distanza di codice 15
Qubit fisici 450
Durata ciclo logico 6us
Frequenza di errore del qubit logico 3.00E-10
Prefactoring di incrocio 0.03
Soglia di correzione degli errori 0,01
Formula tempo del ciclo logico (4 * twoQubitGateTime + 2 * oneQubitMeasurementTime) * codeDistance
Formula qubit fisici 2 * codeDistance * codeDistance

Nel gruppo Parametri qubit fisici è possibile visualizzare le proprietà qubit fisiche che sono state considerate per questa stima. Ad esempio, il tempo per eseguire una misurazione a qubit singolo e un controllo a qubit singolo vengono considerati rispettivamente 100 ns e 50 ns.

Suggerimento

È anche possibile accedere all'output di Resource Estimator come dizionario Python usando il metodo result.data().

Per altre informazioni, vedere l'elenco completo dei dati di output per Lo strumento di stima delle risorse.

Diagrammi spazi

La distribuzione dei qubit fisici usati per l'algoritmo e le factory T è un fattore che può influire sulla progettazione dell'algoritmo. È possibile visualizzare questa distribuzione per comprendere meglio i requisiti di spazio stimati per l'algoritmo.

result.diagram.space

Diagramma a torta che mostra la distribuzione dei qubit fisici totali tra qubit di algoritmo e qubit di T factory. È disponibile una tabella con la suddivisione del numero di copie factory T e il numero di qubit fisici per ogni factory T.

Il diagramma spaziale mostra la proporzione di qubit di algoritmo e qubit di T factory. Si noti che il numero di copie factory T, 28, contribuisce al numero di qubit fisici per le factory T come $\text{T factory} \cdot \text{qubit fisico per T factory}= 28 \cdot 18.000 = 504.000$.

Per altre informazioni, vedere Stima fisica della factory T.

Modificare i valori predefiniti e stimare l'algoritmo

Quando si invia una richiesta di stima delle risorse per il programma, è possibile specificare alcuni parametri facoltativi. Usare il jobParams campo per accedere a tutti i valori che possono essere passati all'esecuzione del processo e verificare quali valori predefiniti sono stati considerati:

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

Questi sono i target parametri che è possibile personalizzare:

Per altre informazioni, vedere Parametri di destinazione per Lo strumento di stima delle risorse.

Modificare il modello qubit

Successivamente, stimare il costo per lo stesso algoritmo usando il parametro qubit basato su Majorana qubit_maj_ns_e6

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

È possibile esaminare i conteggi fisici a livello di codice. Ad esempio, è possibile esplorare i dettagli sulla factory T creata per eseguire l'algoritmo.

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

Nota

Per impostazione predefinita, il runtime viene visualizzato in nanosecondi.

È possibile usare questi dati per produrre alcune spiegazioni del modo in cui le factory T producono gli stati T necessari.

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)

Modificare lo schema di correzione degli errori quantistici

Eseguire di nuovo il processo di stima delle risorse per lo stesso esempio sui parametri qubit basati su Majorana con uno schema QEC floqued, qecScheme.

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

Modificare il budget degli errori

Eseguire di nuovo lo stesso circuito quantistico con un valore errorBudget pari al 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

Nota

Se si verifica un problema durante l'uso dello strumento di stima delle risorse, consultare la pagina Risoluzione dei problemi o contattare AzureQuantumInfo@microsoft.com.

Passaggi successivi