Diferentes maneiras de executar o Avaliador de Recursos

Neste artigo, você aprenderá a trabalhar com o Avaliador de Recursos do Azure Quantum. O Avaliador de Recursos está disponível no VS Code e online em portal do Azure.

A tabela a seguir mostra as diferentes maneiras de executar o Avaliador de Recursos.

Cenário do usuário Plataforma Tutorial
Estimar os recursos de um programa Q# Visual Studio Code Selecione Q# no VS Code na parte superior da página
Estimar os recursos de um programa Q# (avançado) Jupyter Notebook em Visual Studio Code Selecione Q# em Jupyter Notebook na parte superior da página
Estimar os recursos de um programa qiskit Portal do Azure Quantum Selecione Qiskit em portal do Azure na parte superior da página
Estimar os recursos de um programa QIR Portal do Azure Quantum Enviar QIR
Usar arquivos FCIDUMP como parâmetros de argumento (avançado) Visual Studio Code Enviar um problema de química quântica

Observação

O Microsoft Quantum Development Kit (QDK Clássico) não terá mais suporte após 30 de junho de 2024. Se você for um desenvolvedor de QDK existente, recomendamos fazer a transição para o novo QDK (Kit de Desenvolvimento Quântico do Azure) para continuar desenvolvendo soluções quânticas. Para obter mais informações, consulte Migrar seu código Q# para o QDK moderno.

Pré-requisitos para o VS Code

Dica

Você não precisa ter uma conta do Azure para executar o Avaliador de Recursos local.

Criar um novo arquivo Q#

  1. Abra Visual Studio Code e selecione Arquivo > Novo Arquivo de Texto para criar um novo arquivo.
  2. Salve o arquivo como ShorRE.qs. Esse arquivo conterá o código Q# do programa.

Criar o algoritmo quântico

Copie o seguinte código no arquivo 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;
    }
}

Executar o Avaliador de Recursos

O Avaliador de Recursos oferece seis parâmetros qubit predefinidos, quatro dos quais têm conjuntos de instruções baseados em portão e dois que têm um conjunto de instruções Majorana. Ele também oferece dois códigos surface_code de correção de erro quântico, e floquet_code.

Neste exemplo, você executa o Avaliador de Recursos usando o qubit_gate_us_e3 parâmetro qubit e o código de correção de surface_code erro quântico.

  1. Selecione Exibir –> Paleta de Comandos ou pressione Ctrl+Shift+P e digite "resource", que deve abrir a opção Q#: Calcular Estimativas de Recursos . Selecione esta opção para abrir a janela Avaliador de Recursos.
  2. Você pode selecionar um ou mais tipos de código de parâmetro Qubit + Correção de Erro para os quais estimar os recursos. Para este exemplo, selecione qubit_gate_us_e3 e clique em OK.
  3. Especifique o orçamento de erro ou aceite o valor padrão 0,001. Para este exemplo, deixe o valor padrão e pressione Enter.
  4. Pressione Enter para aceitar o nome do resultado padrão com base no nome do arquivo, nesse caso, ShorRE.

Exibir os resultados

O Avaliador de Recursos fornece várias estimativas para o mesmo algoritmo, cada uma mostrando compensações entre o número de qubits e o runtime. Entender a compensação entre o runtime e a escala do sistema é um dos aspectos mais importantes da estimativa de recursos.

O resultado da estimativa de recurso é exibido na janela Estimativa do Q# .

  1. A guia Resultados exibe um resumo da estimativa de recurso. Clique no ícone ao lado da primeira linha para selecionar as colunas que você deseja exibir. Você pode selecionar entre nome de execução, tipo de estimativa, tipo qubit, esquema qec, orçamento de erro, qubits lógicos, profundidade lógica, distância de código, estados T, fábricas T, fração de fábrica T, runtime, rQOPS e qubits físicos.

    Captura de tela mostrando como exibir o menu para selecionar as saídas de estimativa de recursos de sua escolha.

    Na coluna Tipo de estimativa da tabela de resultados, você pode ver o número de combinações ideais de {número de qubits, runtime} para seu algoritmo. Essas combinações podem ser vistas no diagrama de espaço-tempo.

  2. O diagrama espaço-tempo mostra as compensações entre o número de qubits físicos e o runtime do algoritmo. Nesse caso, o Avaliador de Recursos localiza 13 combinações ideais diferentes de muitos milhares de possíveis. Você pode passar o mouse sobre cada {número de qubits, runtime} para ver os detalhes da estimativa de recurso nesse ponto.

    Captura de tela mostrando o diagrama de espaço-tempo do Avaliador de Recursos.

    Para obter mais informações, consulte Diagrama de espaço-tempo.

    Observação

    Você precisa clicar em um ponto do diagrama de espaço-tempo, que é um par {number of qubits, runtime}, para ver o diagrama de espaço e os detalhes da estimativa de recurso correspondente a esse ponto.

  3. O diagrama Espaço mostra a distribuição de qubits físicos usados para o algoritmo e as fábricas T, correspondendo a um par {number of qubits, runtime}. Por exemplo, se você selecionar o ponto mais à esquerda no diagrama de espaço-tempo, o número de qubits físicos necessários para executar o algoritmo será 427726, 196686 dos quais são qubits de algoritmo e 231040 dos quais são qubits de fábrica T.

    Captura de tela mostrando o diagrama de espaço do Avaliador de Recursos.

  4. Por fim, a guia Estimativas de Recursos exibe a lista completa de dados de saída para o Avaliador de Recursos correspondente a um par {number of qubits, runtime} . Você pode inspecionar os detalhes de custos recolhendo os grupos que têm mais informações. Por exemplo, selecione o ponto mais à esquerda no diagrama de espaço-tempo e recolha o grupo Parâmetros lógicos do qubit .

    Parâmetro qubit lógico Valor
    Esquema de QEC surface_code
    Distância de código 21
    Qubits físicos 882
    Tempo de ciclo lógico 13 milissegundos
    Taxa de erro do qubit lógico 3.00E-13
    Pré-fabricado de cruzamento 0.03
    Limite de correção de erro 0,01
    Fórmula de tempo do ciclo lógico (4 * twoQubitGateTime + 2 * oneQubitMeasurementTime) * codeDistance
    Fórmula de qubits físicos 2 * codeDistance * codeDistance

    Dica

    Clique em Mostrar linhas detalhadas para exibir a descrição de cada saída dos dados do relatório.

    Para obter mais informações, consulte os dados de relatório completos do Avaliador de Recursos.

Alterar os target parâmetros

Você pode estimar o custo para o mesmo programa Q# usando outro tipo de qubit, código de correção de erro e orçamento de erro. Abra a janela Avaliador de Recursos selecionando Exibir –> Paleta de Comandos e digite Q#: Calculate Resource Estimates.

Selecione qualquer outra configuração, por exemplo, o parâmetro qubit baseado em Majorana, qubit_maj_ns_e6. Aceite o valor de orçamento de erro padrão ou insira um novo e pressione Enter. O Avaliador de Recursos executa novamente a estimativa com os novos target parâmetros.

Para obter mais informações, consulte Parâmetros de destino para o Avaliador de Recursos.

Executar várias configurações de parâmetros

O Avaliador de Recursos do Azure Quantum pode executar várias configurações de target parâmetros e comparar os resultados da estimativa de recursos.

  1. Selecione Exibir –> Paleta de Comandos ou pressione Ctrl+Shift+P e digite Q#: Calculate Resource Estimates.

  2. Selecione qubit_gate_us_e3, qubit_gate_us_e4, qubit_maj_ns_e4 + floquet_code e qubit_maj_ns_e6 + floquet_code e clique em OK.

  3. Aceite o valor de orçamento de erro padrão 0,001 e pressione Enter.

  4. Pressione Enter para aceitar o arquivo de entrada, nesse caso, ShorRE.qs.

  5. No caso de várias configurações de parâmetros, os resultados são exibidos em linhas diferentes na guia Resultados .

  6. O diagrama espaço-tempo mostra os resultados de todas as configurações de parâmetros. A primeira coluna da tabela de resultados exibe a legenda de cada configuração de parâmetros. Você pode passar o mouse sobre cada ponto para ver os detalhes da estimativa de recurso nesse ponto.

    Captura de tela mostrando o diagrama de tempo de espaço e a tabela de resultados ao executar várias configurações de parâmetro no Avaliador de Recursos.

  7. Clique em um ponto {number of qubits, runtime} do diagrama de espaço-tempo para exibir o diagrama de espaço correspondente e os dados do relatório.

Pré-requisitos para Jupyter Notebook no VS Code

Dica

Você não precisa ter uma conta do Azure para executar o Estimador de Recursos local.

Criar o algoritmo quântico

  1. No VS Code, selecione Exibir > paleta de comandos e selecione Criar: Novo Jupyter Notebook.

  2. No canto superior direito, o VS Code detectará e exibirá a versão do Python e o ambiente virtual do Python que foi selecionado para o notebook. Se você tiver vários ambientes do Python, talvez seja necessário selecionar um kernel usando o seletor de kernel no canto superior direito. Se nenhum ambiente foi detectado, consulte Jupyter Notebooks no VS Code para obter informações de instalação.

  3. Na primeira célula do notebook, importe o qsharp pacote.

    import qsharp
    
  4. Adicione uma nova célula e copie o código a seguir.

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

Estimar o algoritmo quântico

Agora, você estima os recursos físicos para a RunProgram operação usando as suposições padrão. Adicione uma nova célula e copie o código a seguir.

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

A qsharp.estimate função cria um objeto de resultado, que pode ser usado para exibir uma tabela com as contagens gerais de recursos físicos. Você pode inspecionar os detalhes de custos recolhendo os grupos que têm mais informações. Para obter mais informações, consulte os dados de relatório completos do Avaliador de Recursos.

Por exemplo, recolha o grupo Parâmetros de qubit lógico para ver que a distância do código é 21 e o número de qubits físicos é 882.

Parâmetro de qubit lógico Valor
Esquema de QEC surface_code
Distância de código 21
Qubits físicos 882
Tempo de ciclo lógico 8 milissegundos
Taxa de erro de qubit lógico 3.00E-13
Pré-refatoração de cruzamento 0.03
Limite de correção de erro 0,01
Fórmula de tempo do ciclo lógico (4 * twoQubitGateTime + 2 * oneQubitMeasurementTime) * codeDistance
Fórmula de qubits físicos 2 * codeDistance * codeDistance

Dica

Para uma versão mais compacta da tabela de saída, você pode usar result.summary.

Diagrama de espaço

A distribuição de qubits físicos usados para o algoritmo e as fábricas T é um fator que pode afetar o design do algoritmo. Você pode usar o qsharp-widgets pacote para visualizar essa distribuição para entender melhor os requisitos de espaço estimados para o algoritmo.

from qsharp_widgets import SpaceChart, EstimateDetails
SpaceChart(result)

Neste exemplo, o número de qubits físicos necessários para executar o algoritmo são 829766, 196686 dos quais são qubits de algoritmo e 633080 dos quais são qubits de fábrica T.

Captura de tela mostrando o diagrama de espaço do Avaliador de Recursos.

Alterar os valores padrão e estimar o algoritmo

Ao enviar uma solicitação de estimativa de recursos para seu programa, você pode especificar alguns parâmetros opcionais. Use o jobParams campo para acessar todos os target parâmetros que podem ser passados para a execução do trabalho e ver quais valores padrão foram assumidos:

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

Você pode ver que o Avaliador de Recursos usa o qubit_gate_ns_e3 modelo qubit, o surface_code código de correção de erro e o orçamento de erro 0,001 como valores padrão para a estimativa.

Estes são os target parâmetros que podem ser personalizados:

  • errorBudget – o orçamento geral de erros permitido para o algoritmo
  • qecScheme – o esquema de correção de erros quânticos (QEC)
  • qubitParams – os parâmetros de qubit físico
  • constraints – As restrições no nível do componente
  • distillationUnitSpecifications – as especificações para algoritmos de destilação de fábricas T
  • estimateType - única ou fronteira

Para obter mais informações, consulte Parâmetros de destino para o Avaliador de Recursos.

Alterar o modelo de qubit

Você pode estimar o custo para o mesmo algoritmo usando o parâmetro qubit baseado em Majorana, qubitParams, "qubit_maj_ns_e6".

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

Alterar o esquema de correção de erros quânticos

Você pode executar novamente o trabalho de estimativa de recursos para o mesmo exemplo nos parâmetros qubit baseados em Majorana com um esquema QEC floqued, qecScheme.

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

Alterar o orçamento de erros

Em seguida, execute novamente o mesmo circuito quântico com um errorBudget de 10%.

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

Envio em lote com o Avaliador de Recursos

O Avaliador de Recursos do Azure Quantum permite que você execute várias configurações de target parâmetros e compare os resultados. Isso é útil quando você deseja comparar o custo de diferentes modelos de qubit, esquemas QEC ou orçamentos de erro.

  1. Você pode executar uma estimativa de lote passando uma lista de target parâmetros para o params parâmetro da qsharp.estimate função. Por exemplo, execute o mesmo algoritmo com os parâmetros padrão e os parâmetros qubit baseados em Majorana com um 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⁻⁶"])
    
    Modelar Qubits lógicos Profundidade lógica Estados T Distância de código Factories T Fração de factory T Qubits físicos rQOPS Runtime físico
    ns baseado em porta, 10⁻³ 223 3,64M 4,70M 21 19 76.30 % 829,77k 26,55M 31 segundos
    ns Majorana, 10⁻⁶ 223 3,64M 4,70M 5 19 63.02 % 79,60k 148,67M 5 segundos
  2. Você também pode construir uma lista de parâmetros de estimativa usando a 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)
    
    Modelar Qubits lógicos Profundidade lógica Estados T Distância de código Factories T Fração de factory T Qubits físicos rQOPS Runtime físico
    µs baseado em porta, 10⁻³ 223 3,64M 4,70M 17 13 40.54 % 216,77k 21,86k 10 horas
    µs baseado em porta, 10⁻⁴ 223 3,64M 4,70M 9 14 43.17 % 63,57k 41,30k 5 horas
    ns baseado em porta, 10⁻³ 223 3,64M 4,70M 17 16 69.08 % 416,89k 32,79M 25 segundos
    ns baseado em porta, 10⁻⁴ 223 3,64M 4,70M 9 14 43.17 % 63,57k 61,94M 13 segundos
    ns Majorana, 10⁻⁴ 223 3,64M 4,70M 9 19 82.75 % 501,48k 82,59M 10 segundos
    ns Majorana, 10⁻⁶ 223 3,64M 4,70M 5 13 31.47 % 42,96k 148,67M 5 segundos

Como executar a estimativa de fronteira do Pareto

Ao estimar os recursos de um algoritmo, é importante considerar a compensação entre o número de qubits físicos e o runtime do algoritmo. Você pode considerar a alocação de quantos qubits físicos possível para reduzir o runtime do algoritmo. No entanto, o número de qubits físicos é limitado pelo número de qubits físicos disponíveis no hardware quântico.

A estimativa de fronteira pareto fornece várias estimativas para o mesmo algoritmo, cada uma com uma compensação entre o número de qubits e o runtime.

  1. Para executar o Avaliador de Recursos usando a estimativa de fronteira do Pareto, você precisa especificar o "estimateType"target parâmetro como "frontier". Por exemplo, execute o mesmo algoritmo com os parâmetros qubit baseados em Majorana com um código de superfície usando a estimativa de fronteira pareto.

    result = qsharp.estimate("RunProgram()", params=
                                {"qubitParams": { "name": "qubit_maj_ns_e4" },
                                "qecScheme": { "name": "surface_code" },
                                "estimateType": "frontier", # frontier estimation
                                }
                            )
    
  2. Você pode usar a EstimatesOverview função para exibir uma tabela com as contagens gerais de recursos físicos. Clique no ícone ao lado da primeira linha para selecionar as colunas que você deseja exibir. Você pode selecionar entre nome de execução, tipo de estimativa, tipo qubit, esquema qec, orçamento de erro, qubits lógicos, profundidade lógica, distância de código, estados T, fábricas T, fração de fábrica T, runtime, rQOPS e qubits físicos.

    from qsharp_widgets import EstimatesOverview
    EstimatesOverview(result)
    

Na coluna Tipo de estimativa da tabela de resultados, você pode ver o número de combinações diferentes de {número de qubits, runtime} para seu algoritmo. Nesse caso, o Avaliador de Recursos localiza 22 combinações ideais diferentes de muitos milhares de possíveis.

Diagrama de espaço-tempo

A EstimatesOverview função também exibe o diagrama de espaço-tempo do Avaliador de Recursos.

O diagrama de tempo de espaço mostra o número de qubits físicos e o runtime do algoritmo para cada {número de qubits, par de runtime}. Você pode passar o mouse sobre cada ponto para ver os detalhes da estimativa de recurso nesse ponto.

Captura de tela mostrando o diagrama de espaço-tempo com estimativa de fronteira do Avaliador de Recursos.

Estimativa da fronteira do Envio em lote com Pareto

  1. Para estimar e comparar várias configurações de target parâmetros com a estimativa de fronteira, adicione "estimateType": "frontier", aos 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 tela mostrando o diagrama de espaço-tempo do Avaliador de Recursos ao usar a estimativa de fronteira do Pareto e várias configurações de parâmetros.

    Observação

    Você pode definir cores e executar nomes para o diagrama qubit-time usando a EstimatesOverview função .

  2. Ao executar várias configurações de target parâmetros usando a estimativa de fronteira pareto, você pode ver as estimativas de recursos para um ponto específico do diagrama de espaço-tempo, ou seja, para cada par {número de qubits, runtime}. Por exemplo, o código a seguir mostra o uso de detalhes da estimativa para a segunda execução (estimate index=0) e o quarto (índice de ponto=3) o runtime mais curto.

    EstimateDetails(result[1], 4)
    
  3. Você também pode ver o diagrama de espaço para um ponto específico do diagrama de espaço-tempo. Por exemplo, o código a seguir mostra o diagrama de espaço para a primeira execução de combinações (estimar index=0) e o terceiro runtime mais curto (índice de ponto=2).

    SpaceChart(result[0], 2)
    

Pré-requisitos para Qiskit

Habilitar o Avaliador target de Recursos do Azure Quantum em seu workspace

O Avaliador de Recursos é um target do provedor de Computação Quântica da Microsoft. Se você criou um workspace desde o lançamento do Avaliador de Recursos, o provedor de Computação Quântica da Microsoft foi adicionado automaticamente ao seu workspace.

Se você estiver usando um workspace do Azure Quantum existente :

  1. Abra seu workspace na portal do Azure.
  2. No painel esquerdo, em Operações, selecione Fornecedores.
  3. Selecione + Adicionar fornecedor.
  4. Selecione + Adicionar para Computação quântica da Microsoft.
  5. Selecione Aprender & Desenvolver e selecione Adicionar.

Criar um notebook em seu workspace

  1. Entre no portal do Azure e selecione seu workspace do Azure Quantum.
  2. Em Operações, selecione Notebooks
  3. Clique em Meus blocos de anotações e clique em Adicionar Novo
  4. Em Tipo de Kernel, selecione IPython.
  5. Digite um nome para o arquivo e clique em Criar arquivo.

Quando o novo notebook é aberto, ele cria automaticamente o código da primeira célula com base na assinatura e nas informações do workspace.

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

Observação

Salvo indicação em contrário, você deve executar cada célula na ordem em que a cria para evitar problemas de compilação.

Clique no ícone triangular "reproduzir" à esquerda da célula para executar o código.

Carregar as importações necessárias

Primeiro, você precisará importar mais módulos do azure-quantum e qiskitdo .

Clique em + Código para adicionar uma nova célula e, em seguida, adicione e execute o seguinte código:

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

Conectar ao serviço do Azure Quantum

Em seguida, crie um objeto AzureQuantumProvider usando o workspace objeto da célula anterior para se conectar ao workspace do Azure Quantum. Você cria uma instância de back-end e define o Avaliador de Recursos como seu target.

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

Criar o algoritmo quântico

Neste exemplo, você cria um circuito quântico para um multiplicador com base na construção apresentada em Ruiz-Perez e Garcia-Escartin (arXiv:1411.5949) que usa a Transformação Do Fourier Quântico para implementar a aritmética.

Você pode ajustar o tamanho do multiplicador alterando a bitwidth variável . A geração de circuito é encapsulada em uma função que pode ser chamada com o bitwidth valor do multiplicador. A operação terá dois registros de entrada, cada um com o tamanho do especificado bitwidthe um registro de saída que tem o dobro do tamanho do especificado bitwidth. A função também imprimirá algumas contagens de recursos lógicos para o multiplicador extraído diretamente do circuito quâ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

Observação

Você pode enviar trabalhos de estimativa de recursos físicos para algoritmos que não têm estados T, mas que têm pelo menos uma medida.

Estimar o algoritmo quântico

Crie uma instância do algoritmo usando a create_algorithm função . Você pode ajustar o tamanho do multiplicador alterando a bitwidth variável .

bitwidth = 4

circ = create_algorithm(bitwidth)

Estime os recursos físicos para essa operação usando as suposições padrão. Você pode enviar o circuito para o back-end do Avaliador de Recursos usando o run método e, em seguida, executar job.result() para aguardar a conclusão do trabalho e retornar os resultados.

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

Isso cria uma tabela que mostra as contagens gerais de recursos físicos. Você pode inspecionar os detalhes de custos recolhendo os grupos que têm mais informações.

Dica

Para uma versão mais compacta da tabela de saída, você pode usar result.summary.

Por exemplo, se você recolher o grupo parâmetros de qubit lógico , poderá ver com mais facilidade que a distância do código de correção de erro é 15.

Parâmetro de qubit lógico Valor
Esquema de QEC surface_code
Distância de código 15
Qubits físicos 450
Tempo de ciclo lógico 6us
Taxa de erro de qubit lógico 3.00E-10
Pré-refatoração de cruzamento 0.03
Limite de correção de erro 0,01
Fórmula de tempo do ciclo lógico (4 * twoQubitGateTime + 2 * oneQubitMeasurementTime) * codeDistance
Fórmula de qubits físicos 2 * codeDistance * codeDistance

No grupo Parâmetros de qubit físico , você pode ver as propriedades físicas do qubit que foram assumidas para essa estimativa. Por exemplo, o tempo para realizar uma medição de qubit único e uma porta de qubit único é considerado 100 ns e 50 ns, respectivamente.

Dica

Você também pode acessar a saída do Avaliador de Recursos como um dicionário python usando o método result.data().

Para obter mais informações, consulte a lista completa de dados de saída para o Avaliador de Recursos.

Diagramas de espaço

A distribuição de qubits físicos usados para o algoritmo e as fábricas T é um fator que pode afetar o design do algoritmo. Você pode visualizar essa distribuição para entender melhor os requisitos de espaço estimados para o algoritmo.

result.diagram.space

Diagrama de pizza mostrando a distribuição do total de qubits físicos entre qubits de algoritmo e qubits de fábrica T. Há uma tabela com a divisão do número de cópias de fábrica T e o número de qubits físicos por fábrica T.

O diagrama de espaço mostra a proporção de qubits de algoritmo e qubits de fábrica T. Observe que o número de cópias de fábrica T, 28, contribui para o número de qubits físicos para fábricas T como $\text{T factories} \cdot \text{qubit físico por fábrica T}= 28 \cdot 18.000 = 504.000$.

Para obter mais informações, consulte Estimativa física da fábrica T.

Alterar os valores padrão e estimar o algoritmo

Ao enviar uma solicitação de estimativa de recursos para seu programa, você pode especificar alguns parâmetros opcionais. Use o jobParams campo para acessar todos os valores que podem ser passados para a execução do trabalho e ver quais valores padrão foram assumidos:

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

Estes são os target parâmetros que podem ser personalizados:

  • errorBudget – o orçamento geral de erros permitido
  • qecScheme – o esquema de correção de erros quânticos (QEC)
  • qubitParams – os parâmetros de qubit físico
  • constraints – As restrições no nível do componente
  • distillationUnitSpecifications – as especificações para algoritmos de destilação de fábricas T

Para obter mais informações, consulte Parâmetros de destino para o Avaliador de Recursos.

Alterar o modelo de qubit

Em seguida, estime o custo do mesmo algoritmo usando o parâmetro qubit baseado em Majorana qubit_maj_ns_e6

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

Você pode inspecionar as contagens físicas programaticamente. Por exemplo, você pode explorar detalhes sobre a fábrica T que foi criada para executar o 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]}

Observação

Por padrão, o runtime é mostrado em nanossegundos.

Você pode usar esses dados para produzir algumas explicações de como as fábricas T produzem os estados T necessários.

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)

Alterar o esquema de correção de erros quânticos

Agora, execute novamente o trabalho de estimativa de recursos para o mesmo exemplo nos parâmetros qubit baseados em Majorana com um 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

Alterar o orçamento de erros

Vamos executar novamente o mesmo circuito quântico com um errorBudget de 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

Observação

Se você tiver algum problema ao trabalhar com o Avaliador de Recursos, marcar a página Solução de problemas ou entre em contato com AzureQuantumInfo@microsoft.com.

Próximas etapas