Pular para o conteúdo principal
Este projeto está em fase beta. Encontrou um bug? Abra uma issue no GitHub. Tem uma pergunta ou ideia? Pergunte no Discussions — adoraríamos receber seu feedback!
Como está a qualidade da tradução nesta página?

Construa um experimento de aprendizado

Este tutorial demonstra como construir um experimento de aprendizado padrão do zero e usá-lo para aprender um modelo a partir de dados ruidosos simulados.

  1. Defina um conjunto de portas em um anel de qubits

  2. Escolha um modelo de Pauli-Lindblad 2-local

  3. Construa o experimento de aprendizado

  4. Gere os circuitos

  5. Configure a simulação local

  6. Execute o programa

  7. Analise os dados

Execução em hardware real

Os circuitos abaixo são simulados localmente, então este passo a passo não precisa de credenciais do IBM Quantum®. Para executá-lo em um dispositivo real:

  • Passo 1: substitua FakeMarrakesh por um Backend real.

  • Passo 5: passe esse Backend como o modo do executor e remova as opções do simulador.

1. Defina um conjunto de portas em um anel de qubits​

Construa um QiskitGateSet a partir de um Target de Backend e de um subconjunto de seus qubits — aqui, um anel de doze qubits do FakeMarrakesh — e então adicione uma porta a ele: uma camada de seis portas CZ que cobre esse anel. Por padrão, o conjunto de portas é inicializado com uma porta de preparação P e uma porta de medição M.

from qiskit_ibm_runtime.fake_provider import FakeMarrakesh

from qiskit_noise_learning.gate_sets import QiskitGateSet

backend = FakeMarrakesh()

qubit_subset = [*range(25, 30), *range(37, 39), *range(45, 50)]
cz_pairs = [(25, 26), (27, 28), (29, 38), (37, 45), (46, 47), (48, 49)]

gate_set = QiskitGateSet(backend.num_qubits, target=backend.target, qubit_subset=qubit_subset)

with gate_set.build_new_gate("cz_gate", latex_str=r"\mathrm{CZ}") as builder:
for pair in cz_pairs:
builder.circuit.cz(*pair)

list(gate_set)
['M', 'P', 'cz_gate']
Execução em hardware real
from qiskit_ibm_runtime import QiskitRuntimeService

backend = QiskitRuntimeService().backend("ibm_marrakesh")

Chame draw() para ver a porta na topologia do dispositivo: o anel de qubits no conjunto de portas e quais pares deles a porta emaranha.

gate_set.draw()

2. Escolha um modelo de Pauli-Lindblad 2-local​

Decida quais geradores de Pauli-Lindblad o ruído pode ter com um PauliLindbladModel. Construa esse conjunto de geradores a partir da conectividade do conjunto de portas com k_local() — aqui, todo Pauli 2-local em pares de qubits conectados da porta unitária e Paulis de um único qubit para preparação e medição.

from qiskit_noise_learning.models import PauliLindbladModel

model = PauliLindbladModel.k_local(gate_set, gate_k={"cz_gate": 2, "M": 1, "P": 1})

Exiba a contagem de parâmetros desconhecidos do modelo de ruído de cada porta.

{name: len(generators) for name, generators in model.generators.items()}
{'M': 12, 'P': 12, 'cz_gate': 144}

3. Construa o experimento de aprendizado​

Monte um Experiment compondo etapas de construção. Cada etapa lê o que as etapas anteriores escreveram, então a ordem importa:

from qiskit_noise_learning.experiment_builder import (
BindFragmentDepths,
CompleteSequences,
EvenDepthVanillaPaths,
Experiment,
GenerateInstructionSequences,
IdentifyRelations,
MergeInstructionSequences,
SPAMPaths,
VanillaInstructionSequences,
)

experiment_builder = (
# Add standard vanilla learning paths and instruction sequences, and identify relations
EvenDepthVanillaPaths()
+ VanillaInstructionSequences()
+ IdentifyRelations()
# Add paths for learning SPAM, then generate and merge instruction sequences for measuring them
+ SPAMPaths()
+ GenerateInstructionSequences()
+ MergeInstructionSequences()
# Finalize by completing the instruction sequences and setting experiment depths
+ CompleteSequences()
+ BindFragmentDepths([2, 16, 32, 64])
)

experiment = experiment_builder.run(
Experiment(fidelity_model=model, shots=20, randomizations=50)
)

print(f"Number of paths: {len(experiment.paths)}")
print(f"Number of instruction sequences: {len(experiment.instruction_sequences)}")
Number of paths: 156
Number of instruction sequences: 37

Observe o posto da matriz de design:

print(f"Design matrix rank: {experiment.design_matrix.rank}")
Design matrix rank: 120

4. Gere os circuitos​

Compile o experimento com um ExecutorCircuitGenerator, gerando um QuantumProgram — um circuito modelo parametrizado por profundidade de fragmento.

from qiskit_noise_learning.circuit_generator import ExecutorCircuitGenerator

circuit_generator = ExecutorCircuitGenerator(gate_set)
quantum_program = circuit_generator.generate(experiment)

print(f"Number of template circuits: {len(quantum_program.items)}")
Number of template circuits: 5

Desenhe um circuito modelo para inspecioná-lo.

quantum_program.items[0].circuit.draw("mpl", idle_wires=False, fold=False)

../_images/4088c7e98baeb5b56925c6c1f276159582c5f7040fbba76de438b919210bcc5f.png

5. Configure a simulação local​

Para definir o modelo de ruído da simulação, usamos os geradores que já estão no modelo, com taxas aleatórias, para construir um PauliLindbladMap.

import numpy as np
from qiskit.quantum_info import PauliLindbladMap

rng = np.random.default_rng(1234)

true_maps = {
name: PauliLindbladMap.from_terms(
[
PauliLindbladMap.GeneratorTerm(rate, generator)
for generator, rate in zip(generators, rng.uniform(2e-4, 1.5e-3, len(generators)))
]
)
for name, generators in model.generators.items()
if name != "P"
}
{name: noise_map.num_qubits for name, noise_map in true_maps.items()}
{'M': 156, 'cz_gate': 156}

Esses mapas têm a largura do dispositivo, já que o modelo é expresso na indexação de qubits de backend, mas o simulador quer que cada mapa tenha a largura da porta à qual se aplica, com índices de Pauli percorrendo os qubits da porta em ordem física crescente. Estreite-os com keep_qubits(), que traça para fora tudo o que está fora do anel:

noise_dict = {
name: noise_map.keep_qubits(qubit_subset) for name, noise_map in true_maps.items()
}
{name: noise_map.num_qubits for name, noise_map in noise_dict.items()}
{'M': 12, 'cz_gate': 12}

O simulador nomeia o ruído de uma camada pela instrução de caixa a partir da qual a camada foi construída, que to_instruction() retorna para qualquer porta do conjunto de portas. Associe cada caixa ao seu mapa, use o método de simulação de estabilizadores e defina seed_simulator para tornar os dados simulados reproduzíveis:

from qiskit_aer import AerSimulator
from qiskit_ibm_runtime import Executor

executor = Executor(
mode=AerSimulator(method="stabilizer"),
options={
"simulator": {
"layer_noise_model": [
(gate_set[name].to_instruction(), noise_map)
for name, noise_map in noise_dict.items()
],
"seed_simulator": 42,
}
},
)
Execução em hardware real
executor = Executor(mode=backend)

6. Execute o programa​

Execute o programa e depois formate os resultados com collect(), para produzir um Fit — o contêiner do qual as etapas de análise leem e no qual escrevem.

Esta etapa é a mesma, esteja o executor apontando para um simulador ou para um dispositivo real.

job = executor.run(quantum_program)
fit = circuit_generator.collect(job.result())

7. Analise os dados​

Construa um pipeline de análise em torno do ajuste do modelo por mínimos quadrados não negativos.

from qiskit_noise_learning.analysis import (
ComputeObservables, # computes observables from raw data
CurveFitObservables, # performs exponential fitting
LeastSquaresSolve, # solves model with (nonnegative) least squares
)

analyzer = ComputeObservables() + CurveFitObservables() + LeastSquaresSolve()

fit = analyzer.run(fit)
/opt/hostedtoolcache/Python/3.15.1/x64/lib/python3.15/site-packages/qiskit_noise_learning/analysis/model_solve.py:250: UserWarning: 7 of 156 row(s) of the linear system have a non-positive or non-finite uncertainty, so they carry no usable statistical weight. Positions in LinearSystemData.row_labels: 144, 145, 148, 149, 151, 152, 154.
system = LinearSystemData.from_fit(fit)

Plote as médias medidas dos observáveis contra suas exponenciais ajustadas, um subgráfico por par CZ, para ver se os decaimentos que estão sendo ajustados são mesmo exponenciais.

fit.plot_qubit_pair_decays(
pairs=cz_pairs,
observable_type="means",
exponential_fit=True,
)

Troque exponential_fit por model_prediction para comparar os mesmos dados com os decaimentos implicados pelas taxas obtidas na solução de mínimos quadrados não negativos.

fit.plot_qubit_pair_decays(
pairs=cz_pairs,
observable_type="means",
model_prediction=True,
)