Diferentes maneras de ejecutar el estimador de recursos

En este artículo, aprenderá a trabajar con el estimador de recursos de Azure Quantum. El estimador de recursos está disponible tanto en VS Code como en línea en Azure Portal.

En la tabla siguiente se muestran las distintas formas de ejecutar el estimador de recursos.

Escenario de usuario Plataforma Tutorial
Estimación de los recursos de un programa de Q# Visual Studio Code Seleccione Q# en VS Code en la parte superior de la página.
Estimación de los recursos de un programa de Q# (avanzado) Jupyter Notebook en Visual Studio Code Seleccione Q# en Jupyter Notebook en la parte superior de la página.
Estimación de los recursos de un programa Qiskit Portal de Azure Quantum Seleccione Qiskit en Azure Portal en la parte superior de la página.
Estimación de los recursos de un programa QIR Portal de Azure Quantum Enviar QIR
Usar archivos FCIDUMP como parámetros de argumento (avanzado) Visual Studio Code Envío de un problema de química cuántica

Nota

El Kit de desarrollo de Microsoft Quantum (QDK clásico) ya no se admitirá después del 30 de junio de 2024. Si es un desarrollador de QDK existente, se recomienda realizar la transición al nuevo kit de desarrollo de Azure Quantum (QDK moderno) para seguir desarrollando soluciones cuánticas. Para obtener más información, consulte Migración del código de Q# al QDK moderno.

Requisitos previos para VS Code

Sugerencia

No es necesario tener una cuenta de Azure para ejecutar el estimador de recursos local.

Create un nuevo archivo Q#

  1. Abra Visual Studio Code y seleccione Archivo > nuevo archivo de texto para crear un nuevo archivo.
  2. Guarde el archivo como ShorRE.qs. Este archivo contendrá el código Q# del programa.

Create el algoritmo cuántico

Copie el código siguiente en el archivo 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 chooseing parameters for `EstimateFrequency`, make sure that
        // generator and modules are not co-prime
        let _ = EstimateFrequency(11, 2^bitsize - 1, bitsize);
    }

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

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

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

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

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

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

        return frequencyEstimate;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

        adjoint self;

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

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

        controlled adjoint self;
    }
}

Ejecución del estimador de recursos

El estimador de recursos ofrece seis parámetros de cúbit predefinidos, cuatro de los cuales tienen conjuntos de instrucciones basados en puertas y dos que tienen un conjunto de instrucciones Majorana. También ofrece dos códigos de corrección de errores cuánticos y surface_codefloquet_code.

En este ejemplo, ejecutará el estimador de recursos mediante el qubit_gate_us_e3 parámetro qubit y el surface_code código de corrección de errores cuánticos.

  1. Seleccione Ver-> Paleta de comandos y escriba "recurso" que debería mostrar la opción Q#: Calculate Resource Estimates (Calcular estimaciones de recursos ). También puede hacer clic en Estimación en la lista de comandos siguientes @EntryPoint(). Seleccione esta opción para abrir la ventana Estimador de recursos.

    Captura de pantalla que muestra cómo seleccionar el comando de estimación en la lista de lentes de código.

  2. Puede seleccionar uno o varios parámetros qubit + tipos de código de corrección de errores para calcular los recursos de . En este ejemplo, seleccione qubit_gate_us_e3 y haga clic en Aceptar.

    Captura de pantalla que muestra cómo seleccionar el parámetro qubit en el menú de estimación de recursos.

  3. Especifique el presupuesto de errores o acepte el valor predeterminado 0.001. En este ejemplo, deje el valor predeterminado y presione Entrar.

  4. Presione Entrar para aceptar el nombre de resultado predeterminado en función del nombre de archivo, en este caso, ShorRE.

Visualización de los resultados

El estimador de recursos proporciona varias estimaciones para el mismo algoritmo, cada una de las cuales muestra inconvenientes entre el número de cúbits y el tiempo de ejecución. Comprender el equilibrio entre el entorno de ejecución y la escala del sistema es uno de los aspectos más importantes de la estimación de recursos.

El resultado de la estimación de recursos se muestra en la ventana Estimación de Q# .

  1. La pestaña Resultados muestra un resumen de la estimación de recursos. Haga clic en el icono situado junto a la primera fila para seleccionar las columnas que desea mostrar. Puede seleccionar entre el nombre de ejecución, el tipo de estimación, el tipo de cúbit, el esquema qec, el presupuesto de errores, los cúbits lógicos, la profundidad lógica, la distancia del código, los estados T, las factorías de T, la fracción de fábrica de T, el tiempo de ejecución, rQOPS y los cúbits físicos.

    Captura de pantalla que muestra cómo mostrar el menú para seleccionar las salidas de estimación de recursos de su elección.

    En la columna Tipo estimado de la tabla de resultados, puede ver el número de combinaciones óptimas de {número de cúbits, runtime} para el algoritmo. Estas combinaciones se pueden ver en el diagrama de tiempo espacial.

  2. El diagrama de tiempo espaciado muestra los inconvenientes entre el número de cúbits físicos y el tiempo de ejecución del algoritmo. En este caso, el estimador de recursos encuentra 13 combinaciones óptimas diferentes de muchas miles posibles. Puede mantener el puntero sobre cada {número de cúbits, tiempo de ejecución} para ver los detalles de la estimación de recursos en ese momento.

    Captura de pantalla que muestra el diagrama de tiempo espaciador del estimador de recursos.

    Para obtener más información, vea Diagrama de tiempo espaciador.

    Nota

    Debe hacer clic en un punto del diagrama de tiempo espaciado, es decir, un par {número de cúbits, tiempo de ejecución}, para ver el diagrama de espacio y los detalles de la estimación de recursos correspondiente a ese punto.

  3. En el diagrama space se muestra la distribución de cúbits físicos usados para el algoritmo y las factorías de T, correspondientes a un par {número de cúbits, runtime}. Por ejemplo, si selecciona el punto más a la izquierda en el diagrama de tiempo espacial, el número de cúbits físicos necesarios para ejecutar el algoritmo se 427726, 196686 de los cuales son cúbits de algoritmo y 231040 de los cuales son cúbits de fábrica de T.

    Captura de pantalla que muestra el diagrama de espacio del estimador de recursos.

  4. Por último, la pestaña Estimaciones de recursos muestra la lista completa de datos de salida del estimador de recursos correspondiente a un par {número de cúbits, runtime} . Se pueden inspeccionar los detalles de los costos contrayendo los grupos, que contienen más información. Por ejemplo, seleccione el punto situado más a la izquierda en el diagrama de tiempo espaciado y contraiga el grupo Parámetros cuánticos lógicos .

    Parámetro de cúbit lógico Valor
    Esquema de QEC surface_code
    Distancia del código 21
    Cúbits físicos 882
    Tiempo de ciclo lógico 13 milisecs
    Frecuencia de error de cúbit lógico 3.00E-13
    Cruce del prefactor 0,03
    Umbral de corrección de errores 0,01
    Fórmula de tiempo de ciclo lógico (4 * twoQubitGateTime + 2 * oneQubitMeasurementTime) * codeDistance
    Fórmula de cúbits físicos 2 * codeDistance * codeDistance

    Sugerencia

    Haga clic en Mostrar filas detalladas para mostrar la descripción de cada salida de los datos del informe.

    Para obtener más información, consulte los datos completos del informe del estimador de recursos.

Cambio de los target parámetros

Puede calcular el costo del mismo programa de Q# con otro tipo de cúbit, código de corrección de errores y presupuesto de errores. Abra la ventana Estimador de recursos seleccionando Ver -> Paleta de comandos y escriba Q#: Calculate Resource Estimates.

Seleccione cualquier otra configuración, por ejemplo, el parámetro de cúbit basado en Majorana, qubit_maj_ns_e6. Acepte el valor predeterminado del presupuesto de errores o escriba uno nuevo y presione Entrar. El estimador de recursos vuelve a ejecutar la estimación con los nuevos target parámetros.

Para obtener más información, consulte Parámetros de destino para el estimador de recursos.

Ejecución de varias configuraciones de parámetros

El estimador de recursos de Azure Quantum puede ejecutar varias configuraciones de parámetros y comparar los resultados de target la estimación de recursos.

  1. Seleccione Ver -> Paleta de comandos o presione Ctrl+Mayús+P y escriba Q#: Calculate Resource Estimates.

  2. Seleccione qubit_gate_us_e3, qubit_gate_us_e4, qubit_maj_ns_e4 + floquet_code y qubit_maj_ns_e6 + floquet_code y haga clic en Aceptar.

  3. Acepte el valor predeterminado del presupuesto de errores 0.001 y presione Entrar.

  4. Presione Entrar para aceptar el archivo de entrada, en este caso, ShorRE.qs.

  5. En el caso de varias configuraciones de parámetros, los resultados se muestran en filas diferentes en la pestaña Resultados .

  6. En el diagrama space-time se muestran los resultados de todas las configuraciones de parámetros. La primera columna de la tabla de resultados muestra la leyenda de cada configuración de parámetros. Puede mantener el puntero sobre cada punto para ver los detalles de la estimación de recursos en ese momento.

    Captura de pantalla que muestra el diagrama de tiempo espaciador y la tabla de resultados al ejecutar varias configuraciones de parámetro en el estimador de recursos.

  7. Haga clic en un {número de cúbits, tiempo de ejecución} del diagrama de tiempo de espacio para mostrar el diagrama de espacio y los datos del informe correspondientes.

Requisitos previos para Jupyter Notebook en VS Code

Sugerencia

No es necesario tener una cuenta de Azure para ejecutar el estimador de recursos local.

Create el algoritmo cuántico

  1. En VS Code, seleccione Ver > paleta de comandos y seleccione Create: Nuevo Jupyter Notebook.

  2. En la parte superior derecha, VS Code detectará y mostrará la versión de Python y el entorno de Python virtual que se seleccionó para el cuaderno. Si tiene varios entornos de Python, es posible que tenga que seleccionar un kernel mediante el selector de kernel en la parte superior derecha. Si no se detectó ningún entorno, consulte Jupyter Notebooks in VS Code (Cuadernos de Jupyter Notebook en VS Code ) para obtener información de configuración.

  3. En la primera celda del cuaderno, importe el qsharp paquete.

    import qsharp
    
  4. Agregue una nueva celda y copie el código siguiente.

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

Estimación del algoritmo cuántico

Ahora, calcula los recursos físicos de la RunProgram operación con las suposiciones predeterminadas. Agregue una nueva celda y copie el código siguiente.

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

La qsharp.estimate función crea un objeto de resultado, que se puede usar para mostrar una tabla con los recuentos generales de recursos físicos. Se pueden inspeccionar los detalles de los costos contrayendo los grupos, que contienen más información. Para obtener más información, consulte los datos completos del informe del estimador de recursos.

Por ejemplo, contraiga el grupo Parámetros cuánticos lógicos para ver que la distancia del código es 21 y el número de cúbits físicos es 882.

Parámetro de cúbit lógico Valor
Esquema de QEC surface_code
Distancia del código 21
Cúbits físicos 882
Tiempo del ciclo lógico 8 milisecs
Tasa de errores de cúbit lógico 3.00E-13
Cruce del prefactor 0,03
Umbral de corrección de errores 0,01
Fórmula de tiempo del ciclo lógico (4 * twoQubitGateTime + 2 * oneQubitMeasurementTime) * codeDistance
Fórmula de cúbits físicos 2 * codeDistance * codeDistance

Sugerencia

Para una versión más compacta de la tabla de salida, puede usar result.summary.

Diagrama de espacio

La distribución de cúbits físicos usados para el algoritmo y las factorías de T es un factor que puede afectar al diseño del algoritmo. Puede usar el qsharp-widgets paquete para visualizar esta distribución para comprender mejor los requisitos de espacio estimados para el algoritmo.

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

En este ejemplo, el número de cúbits físicos necesarios para ejecutar el algoritmo se 829766, 196686 de los cuales son cúbits de algoritmo y 633080 de los cuales son cúbits de fábrica de T.

Captura de pantalla que muestra el diagrama de espacio del estimador de recursos.

Cambiar los valores predeterminados y calcular el algoritmo

Al enviar una solicitud de estimación de recursos para el programa, puede especificar algunos parámetros opcionales. Use el jobParams campo para tener acceso a todos los target parámetros que se pueden pasar a la ejecución del trabajo y ver qué valores predeterminados se han asumido:

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

Puede ver que el estimador de recursos toma el qubit_gate_ns_e3 modelo de cúbits, el surface_code código de corrección de errores y el presupuesto de errores 0,001 como valores predeterminados para la estimación.

Estos son los target parámetros que se pueden personalizar:

  • errorBudget : el presupuesto general de errores permitido para el algoritmo.
  • qecScheme : el esquema de corrección de errores cuánticos (QEC)
  • qubitParams : los parámetros de cúbit físico
  • constraints: restricciones en el nivel de componente.
  • distillationUnitSpecifications : las especificaciones de los algoritmos de destilación de fábricas de T.
  • estimateType - una o frontera

Para obtener más información, consulte Parámetros de destino para el estimador de recursos.

Cambio del modelo de cúbits

Puede calcular el costo del mismo algoritmo mediante el parámetro de cúbit basado en Majorana, qubitParams, "qubit_maj_ns_e6".

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

Cambio del esquema de corrección de errores cuánticos

Puede volver a ejecutar el trabajo de estimación de recursos para el mismo ejemplo en los parámetros de cúbit basados en Majorana con un esquema QEC floqued, qecScheme.

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

Cambio del presupuesto de errores

A continuación, vuelva a ejecutar el mismo circuito cuántico con un errorBudget 10 %.

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

Procesamiento por lotes con el estimador de recursos

El estimador de recursos de Azure Quantum permite ejecutar varias configuraciones de target parámetros y comparar los resultados. Esto resulta útil cuando desea comparar el costo de diferentes modelos de cúbits, esquemas QEC o presupuestos de errores.

  1. Puede realizar una estimación por lotes pasando una lista de target parámetros al params parámetro de la qsharp.estimate función. Por ejemplo, ejecute el mismo algoritmo con los parámetros predeterminados y los parámetros de cúbit basados en Majorana con un esquema QEC floqued.

    result_batch = qsharp.estimate("RunProgram()", params=
                    [{}, # Default parameters
                    {
                        "qubitParams": {
                            "name": "qubit_maj_ns_e6"
                        },
                        "qecScheme": {
                            "name": "floquet_code"
                        }
                    }])
    result_batch.summary_data_frame(labels=["Gate-based ns, 10⁻³", "Majorana ns, 10⁻⁶"])
    
    Modelo Cúbits lógicos Profundidad lógica Estados T Distancia del código Generadores de T Fracción de generador de T Cúbits físicos rQOPS Tiempo de ejecución físico
    ns basados en puertas, 10⁻³ 223 3,64 M 4,70 M 21 19 76.30 % 829.77k 26,55 M 31 segundos
    ns basados en Majorana, 10⁻⁶ 223 3,64 M 4,70 M 5 19 63.02 % 79.60k 148.67M 5 segundos
  2. También puede construir una lista de parámetros de estimación mediante la EstimatorParams clase .

    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)
    
    Modelo Cúbits lógicos Profundidad lógica Estados T Distancia del código Generadores de T Fracción de generador de T Cúbits físicos rQOPS Tiempo de ejecución físico
    μs basados en puertas, 10⁻³ 223 3,64 M 4,70 M 17 13 40.54 % 216.77k 21.86k 10 horas
    μs basados en puertas, 10⁻⁴ 223 3,64 M 4,70 M 9 14 43.17 % 63.57k 41.30 000 5 horas
    ns basados en puertas, 10⁻³ 223 3,64 M 4,70 M 17 16 69.08 % 416.89k 32,79 M 25 segundos
    ns basados en puertas, 10⁻⁴ 223 3,64 M 4,70 M 9 14 43.17 % 63.57k 61,94 M 13 segundos
    ns basados en Majorana, 10⁻⁴ 223 3,64 M 4,70 M 9 19 82.75 % 501.48k 82,59 M 10 segundos
    ns basados en Majorana, 10⁻⁶ 223 3,64 M 4,70 M 5 13 31.47 % 42.96k 148.67M 5 segundos

Ejecución de la estimación de pareto frontier

Al calcular los recursos de un algoritmo, es importante tener en cuenta el equilibrio entre el número de cúbits físicos y el tiempo de ejecución del algoritmo. Podría considerar la asignación de tantos cúbits físicos como sea posible para reducir el tiempo de ejecución del algoritmo. Sin embargo, el número de cúbits físicos está limitado por el número de cúbits físicos disponibles en el hardware cuántico.

La estimación de la frontera de Pareto proporciona varias estimaciones para el mismo algoritmo, cada una con un equilibrio entre el número de cúbits y el tiempo de ejecución.

  1. Para ejecutar el estimador de recursos mediante la estimación de frontera de Pareto, debe especificar el "estimateType"target parámetro como "frontier". Por ejemplo, ejecute el mismo algoritmo con los parámetros de cúbit basados en Majorana con un código de superficie mediante la estimación de frontera de Pareto.

    result = qsharp.estimate("RunProgram()", params=
                                {"qubitParams": { "name": "qubit_maj_ns_e4" },
                                "qecScheme": { "name": "surface_code" },
                                "estimateType": "frontier", # frontier estimation
                                }
                            )
    
  2. Puede usar la EstimatesOverview función para mostrar una tabla con los recuentos generales de recursos físicos. Haga clic en el icono situado junto a la primera fila para seleccionar las columnas que desea mostrar. Puede seleccionar entre el nombre de ejecución, el tipo de estimación, el tipo de cúbit, el esquema qec, el presupuesto de errores, los cúbits lógicos, la profundidad lógica, la distancia de código, los estados de T, las factorías de T, la fracción de fábrica de T, el runtime, rQOPS y los cúbits físicos.

    from qsharp_widgets import EstimatesOverview
    EstimatesOverview(result)
    

En la columna Tipo estimado de la tabla de resultados, puede ver el número de combinaciones diferentes de {número de cúbits, runtime} para el algoritmo. En este caso, el estimador de recursos encuentra 22 combinaciones óptimas diferentes de muchos miles de posibles.

Diagrama de tiempo espaciador

La EstimatesOverview función también muestra el diagrama de tiempo espaciador del estimador de recursos.

El diagrama de tiempo espaciado muestra el número de cúbits físicos y el tiempo de ejecución del algoritmo para cada par {número de cúbits, tiempo de ejecución}. Puede mantener el puntero sobre cada punto para ver los detalles de la estimación de recursos en ese momento.

Captura de pantalla que muestra el diagrama de espacio-tiempo con la estimación de frontera del estimador de recursos.

Procesamiento por lotes con estimación de frontera de Pareto

  1. Para calcular y comparar varias configuraciones de target parámetros con estimación de frontera, agregue "estimateType": "frontier", a los parámetros.

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

    Captura de pantalla que muestra el diagrama de tiempo espaciador del estimador de recursos al usar la estimación de frontera de Pareto y varias configuraciones de parámetros.

    Nota

    Puede definir colores y nombres de ejecución para el diagrama de tiempo de cúbit mediante la EstimatesOverview función .

  2. Al ejecutar varias configuraciones de target parámetros mediante la estimación de frontera de Pareto, puede ver las estimaciones de recursos para un punto específico del diagrama de tiempo de espacio, es decir, para cada par {número de cúbits, tiempo de ejecución}. Por ejemplo, el código siguiente muestra el uso de los detalles de estimación para la segunda ejecución (estimate index=0) y el cuarto tiempo de ejecución más corto (point index=3).

    EstimateDetails(result[1], 4)
    
  3. También puede ver el diagrama de espacio para un punto específico del diagrama de tiempo espacial. Por ejemplo, el código siguiente muestra el diagrama de espacio para la primera ejecución de combinaciones (estimación de index=0) y el tercer tiempo de ejecución más corto (point index=2).

    SpaceChart(result[0], 2)
    

Requisitos previos para Qiskit

Habilitación del estimador target de recursos de Azure Quantum en el área de trabajo

El estimador de recursos es un target proveedor de computación cuántica de Microsoft. Si ha creado un área de trabajo desde el lanzamiento del estimador de recursos, el proveedor de computación cuántica de Microsoft se agregó automáticamente al área de trabajo.

Si usa un área de trabajo de Azure Quantum existente :

  1. Abra el área de trabajo en Azure Portal.
  2. En el panel de la izquierda, en Operaciones, seleccione Proveedores.
  3. Seleccione + Agregar un proveedor.
  4. Seleccione + Agregar en Computación cuántica de Microsoft.
  5. Seleccione Learn & Desarrollar y seleccione Agregar.

Creación de un cuaderno en el área de trabajo

  1. Inicie sesión en Azure Portal y seleccione el área de trabajo de Azure Quantum.
  2. En Operaciones, seleccione Cuadernos.
  3. Haga clic en Mis cuadernos y haga clic en Agregar nuevo.
  4. En Tipo de kernel, seleccione IPython.
  5. Escriba un nombre para el archivo y haga clic en Create archivo.

Cuando se abre el cuaderno nuevo, este crea automáticamente el código de la primera celda, en función de la información de la suscripción y del área de trabajo.

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

Nota

A menos que se indique lo contrario, debe ejecutar cada celda en orden a medida que se cree para evitar problemas de compilación.

Haga clic en el icono triangular "reproducir" situado a la izquierda de la celda para ejecutar el código.

Carga de las importaciones necesarias

En primer lugar, deberá importar módulos adicionales de azure-quantum y qiskit.

Haga clic en + Código para agregar una nueva celda y, a continuación, agregue y ejecute el código siguiente:

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

Conexión al servicio Azure Quantum

A continuación, cree un objeto AzureQuantumProvider mediante el workspace objeto de la celda anterior para conectarse al área de trabajo de Azure Quantum. Cree una instancia de back-end y establezca el estimador de recursos como target.

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

Create el algoritmo cuántico

En este ejemplo, se crea un circuito cuántico para un multiplicador basado en la construcción presentada en Ruiz-Perez y Garcia-Escartin (arXiv:1411.5949) que usa la transformación Quantum Fourier para implementar aritmética.

Puede ajustar el tamaño del multiplicador cambiando la bitwidth variable. La generación del circuito se ajusta en una función a la que se puede llamar con el bitwidth valor del multiplicador. La operación tendrá dos registros de entrada, cada uno del tamaño del especificado bitwidthy un registro de salida que sea el doble del tamaño del especificado bitwidth. La función también imprimirá algunos recuentos de recursos lógicos para el multiplicador extraído directamente del circuito cuántico.

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

Puede enviar trabajos de estimación de recursos físicos para algoritmos que no tengan estados T, pero que tengan al menos una medida.

Estimación del algoritmo cuántico

Create una instancia del algoritmo mediante la create_algorithm función . Puede ajustar el tamaño del multiplicador cambiando la bitwidth variable.

bitwidth = 4

circ = create_algorithm(bitwidth)

Calcule los recursos físicos de esta operación con las suposiciones predeterminadas. Puede enviar el circuito al back-end del estimador de recursos mediante el run método y, a continuación, ejecutar job.result() para esperar a que el trabajo se complete y devuelva los resultados.

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

Esto crea una tabla que muestra los recuentos generales de recursos físicos. Se pueden inspeccionar los detalles de los costos contrayendo los grupos, que contienen más información.

Sugerencia

Para una versión más compacta de la tabla de salida, puede usar result.summary.

Por ejemplo, si contrae el grupo Parámetros de cúbit lógico , puede ver más fácilmente que la distancia del código de corrección de errores es 15.

Parámetro de cúbit lógico Valor
Esquema de QEC surface_code
Distancia del código 15
Cúbits físicos 450
Tiempo del ciclo lógico 6us
Tasa de errores de cúbit lógico 3.00E-10
Cruce del prefactor 0,03
Umbral de corrección de errores 0,01
Fórmula de tiempo del ciclo lógico (4 * twoQubitGateTime + 2 * oneQubitMeasurementTime) * codeDistance
Fórmula de cúbits físicos 2 * codeDistance * codeDistance

En el grupo Parámetros de cúbit físico , puede ver las propiedades de cúbit físicos que se han asumido para esta estimación. Por ejemplo, el tiempo para realizar una medición de un solo cúbit y una puerta de un solo cúbit se supone que son 100 ns y 50 ns, respectivamente.

Sugerencia

También puede acceder a la salida del estimador de recursos como diccionario de Python mediante el método result.data().

Para obtener más información, consulte la lista completa de datos de salida del estimador de recursos.

Diagramas de espacio

La distribución de cúbits físicos usados para el algoritmo y las factorías de T es un factor que puede afectar al diseño del algoritmo. Puede visualizar esta distribución para comprender mejor los requisitos de espacio estimados para el algoritmo.

result.diagram.space

Diagrama circular que muestra la distribución de cúbits físicos totales entre cúbits de algoritmo y cúbits de fábrica de T. Hay una tabla con el desglose del número de copias de fábrica de T y el número de cúbits físicos por factoría de T.

El diagrama de espacio muestra la proporción de cúbits de algoritmo y cúbits de fábrica de T. Tenga en cuenta que el número de copias de fábrica de T, 28, contribuye al número de cúbits físicos para factorías de T como $\text{T factorys} \cdot \text{qubit físico por factoría de T}= 28 \cdot 18 000 = 504 000$.

Para más información, consulte Estimación física de fábrica de T.

Cambiar los valores predeterminados y calcular el algoritmo

Al enviar una solicitud de estimación de recursos para el programa, puede especificar algunos parámetros opcionales. Use el jobParams campo para tener acceso a todos los valores que se pueden pasar a la ejecución del trabajo y ver qué valores predeterminados se han asumido:

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

Estos son los target parámetros que se pueden personalizar:

  • errorBudget : el presupuesto de errores permitido general.
  • qecScheme : el esquema de corrección de errores cuánticos (QEC)
  • qubitParams : los parámetros de cúbit físico
  • constraints: restricciones en el nivel de componente.
  • distillationUnitSpecifications : las especificaciones de los algoritmos de destilación de fábricas de T

Para obtener más información, consulte Parámetros de destino para el estimador de recursos.

Cambio del modelo de cúbits

A continuación, calcule el costo del mismo algoritmo mediante el parámetro de cúbit basado en Majorana. qubit_maj_ns_e6

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

Puede inspeccionar los recuentos físicos mediante programación. Por ejemplo, puede explorar los detalles sobre la factoría de T que se creó para ejecutar el 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

De forma predeterminada, el tiempo de ejecución se muestra en nanosegundos.

Puede usar estos datos para generar algunas explicaciones de cómo las factorías de T producen los estados T necesarios.

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)

Cambio del esquema de corrección de errores cuánticos

Ahora, vuelva a ejecutar el trabajo de estimación de recursos para el mismo ejemplo en los parámetros de cúbit basados en Majorana con un esquema 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

Cambio del presupuesto de errores

Vamos a volver a ejecutar el mismo circuito cuántico con un errorBudget 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

Si tiene algún problema al trabajar con el estimador de recursos, consulte la página Solución de problemas o póngase en contacto con AzureQuantumInfo@microsoft.com.

Pasos siguientes