Núcleos quânticos com portas fracionárias
Estimativa de uso: menos de 30 segundos em um processador Heron r2 (OBSERVAÇÃO: esta é apenas uma estimativa. Seu tempo de execução pode variar)
Resultados do aprendizado
Ao concluir este tutorial, você deverá compreender:
- O que são portas fracionárias e como elas reduzem a profundidade e a duração dos circuitos em QPUs d IBM®
- As restrições associadas ao uso de portas fracionárias (em particular, a faixa de ângulos RZZ)
- Como construir um fluxo de trabalho de kernel quântico que utilize portas fracionárias com o Qiskit Runtime
- Como comparar métricas de execução em hardware (profundidade, duração, contagem de portas não locais, fidelidade) com e sem portas fracionárias
- Como usar apenas portas RX fracionárias, mantendo o fluxo de trabalho padrão do Qiskit
Pré-requisitos
Sugerimos que você esteja familiarizado com os seguintes tópicos antes de seguir com este tutorial:
- O fluxo de trabalho de padrões do Qiskit
- Guia sobre portas fracionárias
- O tutorial de treinamento de kernels quânticos e a aula sobre kernels quânticos do curso de aprendizado de máquina quântico
Segundo plano
Portas fracionárias em QPUs d IBM
Portas fracionárias são portas quânticas parametrizadas que permitem a execução direta de rotações de ângulos arbitrários (dentro de limites específicos), eliminando a necessidade de decompor essas rotações em várias portas básicas. Ao aproveitar as interações nativas entre os qubits físicos, é possível implementar certas operações unitárias de forma mais eficiente no hardware.
IBM As QPUs Quantum® Heron suportam as seguintes portas fracionárias:
- para
- para qualquer valor real
Essas portas podem reduzir significativamente a profundidade e a duração dos circuitos quânticos. Eles são particularmente vantajosos em aplicativos que dependem muito de e , como a simulação Hamiltoniana, o Algoritmo de Otimização Aproximada Quântica (QAOA) e os métodos de kernel quântico. Neste tutorial, vamos nos concentrar no kernel quântico como um exemplo prático.
Limitações
Atualmente, as portas fracionárias são um recurso experimental e apresentam algumas restrições:
- é limitado a ângulos no intervalo .
- Não há suporte para o uso de portas fracionárias em circuitos dinâmicos, giro de Pauli, cancelamento de erro probabilístico (PEC) e extrapolação de ruído zero (ZNE) (usando amplificação de erro probabilístico (PEA)).
As portas fracionárias exigem um fluxo de trabalho diferente em comparação com a abordagem padrão. Este tutorial explica como trabalhar com portas fracionárias por meio de um aplicativo prático.
Consulte o seguinte para obter mais detalhes sobre portas fracionárias.
Abordagens de fluxo de trabalho para a restrição do ângulo RZZ
O fluxo de trabalho para o uso de portas fracionárias geralmente segue o fluxo de trabalho dos padrões do Qiskit. A principal diferença é que todos os ângulos RZZ devem satisfazer a restrição . Existem duas abordagens para garantir que essa condição seja atendida, conforme discutiremos a seguir. Recomendamos a segunda abordagem e, neste tutorial, a demonstramos por meio de um exemplo inspirado no método do kernel quântico. Para entender melhor em que contextos os kernels quânticos podem ser úteis, recomendamos a leitura do artigo de Liu, Arunachalam e Temme (2021).
Você também pode seguir o tutorial de treinamento sobre kernels quânticos e a aula sobre kernels quânticos no curso de aprendizado de máquina quântico em IBM Quantum® Learning.
1. Gerar valores de parâmetros que satisfaçam a restrição do ângulo RZZ
Se você tiver certeza de que todos os ângulos RZZ estão dentro do intervalo válido, poderá seguir o fluxo de trabalho padrão dos padrões do Qiskit. Nesse caso, basta enviar os valores dos parâmetros como parte de um PUB. O fluxo de trabalho é realizado da seguinte forma.
pm = generate_preset_pass_manager(backend=backend, ...)
t_circuit = pm.run(circuit)
t_observable = observable.apply_layout(t_circuit.layout)
sampler.run([(t_circuit, parameter_values)])
estimator.run([(t_circuit, t_observable, parameter_values)])Se você tentar enviar um PUB que inclua uma porta RZZ com um ângulo fora do intervalo válido, receberá uma mensagem de erro como esta:
'The instruction rzz is supported only for angles in the range [0, pi/2], but an angle (20.0) outside of this range has been requested; via parameter value(s) γ[0]=10.0, substituted in parameter expression 2.0*γ[0].'
Para evitar esse erro, utilize a segunda abordagem descrita a seguir.
2. Atribua valores de parâmetros aos circuitos antes da transpilagem
O qiskit-ibm-runtime pacote oferece uma etapa de transpilador especializada chamada FoldRzzAngle.
Esta etapa transforma os circuitos quânticos de forma que todos os ângulos RZZ estejam em conformidade com a restrição de ângulo RZZ.
Se você fornecer o backend para generate_preset_pass_manager ou transpile, o Qiskit aplica FoldRzzAngle automaticamente aos circuitos quânticos.
Essa abordagem exige que você atribua valores aos parâmetros dos circuitos quânticos antes da transpilagem.
O fluxo de trabalho se desenrola da seguinte forma.
pm = generate_preset_pass_manager(backend=backend, ...)
b_circuit = circuit.assign_parameters(parameter_values)
t_circuit = pm.run(b_circuit)
t_observable = observable.apply_layout(t_circuit.layout)
sampler.run([(t_circuit,)])
estimator.run([(t_circuit, t_observable)])Observe que esse fluxo de trabalho acarreta um custo computacional maior do que a primeira abordagem, pois envolve atribuir valores de parâmetros aos circuitos quânticos e armazenar localmente os circuitos associados a esses parâmetros.
Esteja ciente de um problema conhecido descrito em qiskit-ibm-runtimev0.47.0, no qual portas RZZ com ângulos inválidos podem permanecer nos circuitos mesmo após a transpilacão, em certos cenários.
Acesse qiskit-ibm-runtime#2441 para acompanhar o andamento dessa questão. Recomendamos a seguinte solução alternativa até que o problema seja resolvido.
pm = generate_preset_pass_manager(backend=backend, ...)
pm.post_optimization = PassManager(
[
FoldRzzAngle(),
Optimize1qGatesDecomposition(target=backend.target),
RemoveIdentityEquivalent(target=backend.target),
]
)
... = pm.run(...)Requisitos
Antes de iniciar este tutorial, verifique se você tem os seguintes itens instalados:
- Qiskit SDK v2.0 ou posterior, com suporte para visualização
- Qiskit Runtime v0.41 ou posterior (
pip install qiskit-ibm-runtime) - Qiskit Aer v0.17 ou versão posterior (
pip install qiskit-aer) - Qiskit Basis Constructor (
pip install qiskit_basis_constructor)
Instalação
import matplotlib.pyplot as plt
import numpy as np
from qiskit import QuantumCircuit, generate_preset_pass_manager
from qiskit.circuit import ParameterVector
from qiskit.circuit.library import UGate, n_local, unitary_overlap
from qiskit.transpiler import Target, PassManager
from qiskit.transpiler.passes import (
Optimize1qGatesDecomposition,
RemoveIdentityEquivalent,
)
from qiskit_aer.primitives import SamplerV2 as AerSampler
from qiskit_basis_constructor import DEFAULT_EQUIVALENCE_LIBRARY
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2
from qiskit_ibm_runtime.transpiler.passes import FoldRzzAngleHabilite portas fracionárias e verifique portas básicas
Para usar portas fracionárias, você pode obter um backend compatível com elas, definindo a opção use_fractional_gates=True .
Se o backend for compatível com portas fracionárias, você verá rzz e rx listados entre suas portas de base.
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=133
) # backend should be a heron device or later
backend_name = backend.name
backend_c = service.backend(backend_name) # w/o fractional gates
backend_f = service.backend(
backend_name, use_fractional_gates=True
) # w/ fractional gates
print(f"Backend: {backend_name}")
print(f"No fractional gates: {backend_c.basis_gates}")
print(f"With fractional gates: {backend_f.basis_gates}")
if "rzz" not in backend_f.basis_gates:
print(f"Backend {backend_name} does not support fractional gates")Output:
Backend: ibm_marrakesh
No fractional gates: ['cz', 'id', 'rz', 'sx', 'x']
With fractional gates: ['cz', 'id', 'rx', 'rz', 'rzz', 'sx', 'x']
Exemplo de simulador em pequena escala
Nesta seção, vamos explicar as quatro etapas do fluxo de trabalho do Qiskit Patterns em um simulador, usando o circuito do kernel quântico como exemplo prático.
Etapa 1: Mapeie entradas clássicas para um problema quântico
Circuito de kernel quântico
Nesta seção, exploramos o circuito do kernel quântico usando portas RZZ para introduzir o fluxo de trabalho para portas fracionárias.
Começamos construindo um circuito quântico para computar entradas individuais da matriz do kernel. Isso é feito por meio da combinação de circuitos de mapas de recursos ZZ com uma sobreposição unitária. A função de kernel usa vetores no espaço mapeado de recursos e retorna seu produto interno como uma entrada da matriz de kernel: onde representa o estado quântico mapeado por recursos.
Construímos manualmente um circuito de mapa de características ZZ utilizando portas RZZ.
zz_feature_mapEmbora o Qiskit ofereça um recurso integrado, ele ainda não oferece suporte a portas RZZ, conforme indicado na página v2.4.1 do Qiskit ( consulte a issue ).
Em seguida, calculamos a função do kernel para entradas idênticas - por exemplo, . Em computadores quânticos ruidosos, esse valor pode ser menor que 1 devido ao ruído. Um resultado mais próximo de 1 indica menor ruído na execução. Neste tutorial, nos referimos a esse valor como a fidelidade, definida como
optimization_level = 2
shots = 2000
reps = 3
rng = np.random.default_rng(seed=123)def my_zz_feature_map(num_qubits: int, reps: int = 1) -> QuantumCircuit:
x = ParameterVector("x", num_qubits * reps)
qc = QuantumCircuit(num_qubits)
qc.h(range(num_qubits))
for k in range(reps):
K = k * num_qubits
for i in range(num_qubits):
qc.rz(x[i + K], i)
pairs = [(i, i + 1) for i in range(num_qubits - 1)]
for i, j in pairs[0::2] + pairs[1::2]:
qc.rzz((np.pi - x[i + K]) * (np.pi - x[j + K]), i, j)
return qc
def quantum_kernel(num_qubits: int, reps: int = 1) -> QuantumCircuit:
qc = my_zz_feature_map(num_qubits, reps=reps)
inner_product = unitary_overlap(qc, qc, "x", "y", insert_barrier=True)
inner_product.measure_all()
return inner_product
def random_parameters(inner_product: QuantumCircuit) -> np.ndarray:
return np.tile(rng.random(inner_product.num_parameters // 2), 2)
def fidelity(result) -> float:
ba = result.data.meas
return ba.get_int_counts().get(0, 0) / ba.num_shotsOs circuitos de kernel quântico e seus valores de parâmetros correspondentes são gerados para sistemas com 4 a 40 qubits, e suas fidelidades são avaliadas posteriormente.
qubits = list(range(4, 12, 2))
circuits = [quantum_kernel(i, reps=reps) for i in qubits]
params = [random_parameters(circ) for circ in circuits]O circuito de quatro qubits é visualizado abaixo.
circuits[0].draw("mpl", fold=-1)Output:
No fluxo de trabalho padrão dos padrões do Qiskit, os valores dos parâmetros são normalmente passados para o primitivo Sampler ou Estimator como parte de um PUB. No entanto, ao usar um backend que ofereça suporte a portas fracionárias, esses valores de parâmetros devem ser explicitamente atribuídos ao circuito quântico antes da transpilação.
b_qc = [
circ.assign_parameters(param) for circ, param in zip(circuits, params)
]
b_qc[0].draw("mpl", fold=-1)Output:
Etapa 2: Otimizar o problema para execução em hardware quântico
Em seguida, transpilamos o circuito usando o gerenciador de passagens seguindo o padrão Qiskit.
Ao fornecer um backend compatível com portas fracionárias para generate_preset_pass_manager, uma passagem especializada chamada FoldRzzAngle é automaticamente incluída.
Essa passagem modifica o circuito para atender às restrições de ângulo RZZ.
Como resultado, as portas RZZ com valores negativos na figura anterior são transformadas em valores positivos, e algumas portas X adicionais são adicionadas.
backend_f = service.backend(name=backend_name, use_fractional_gates=True)
# pm_f includes `FoldRzzAngle` pass
pm_f = generate_preset_pass_manager(
optimization_level=optimization_level, backend=backend_f
)
pm_f.post_optimization = PassManager(
[
FoldRzzAngle(),
Optimize1qGatesDecomposition(target=backend_f.target),
RemoveIdentityEquivalent(target=backend_f.target),
]
)t_qc_f = pm_f.run(b_qc)
print(t_qc_f[0].count_ops())
t_qc_f[0].draw("mpl", fold=-1)Output:
OrderedDict({'rz': 35, 'rzz': 18, 'x': 13, 'rx': 9, 'measure': 4, 'barrier': 2})
Para avaliar o impacto das portas fracionárias, avaliamos o número de portas não locais (CZ e RZZ para esse backend), juntamente com as profundidades e durações dos circuitos, e comparamos essas métricas com as de um fluxo de trabalho padrão posterior.
nnl_f = [qc.num_nonlocal_gates() for qc in t_qc_f]
depth_f = [qc.depth() for qc in t_qc_f]
duration_f = [
qc.estimate_duration(backend_f.target, unit="u") for qc in t_qc_f
]Passo 3: Execute usando Qiskit primitives
Executamos o circuito transpilado com o backend que suporta portas fracionárias.
sampler_f = AerSampler.from_backend(backend_f)job = sampler_f.run(t_qc_f, shots=shots)
print(job.job_id())Output:
085ce928-767e-4200-93bf-3905e5411cfe
Etapa 4: Pós-processamento e retorno do resultado no formato clássico desejado
Você pode obter o valor da função kernel medindo a probabilidade do bitstring totalmente zero 00...00 na saída.
result = job.result()
fidelity_f = [fidelity(result=res) for res in result]
print(fidelity_f)Output:
[0.929, 0.882, 0.8645, 0.817]
Comparação do fluxo de trabalho e do circuito sem portas fracionárias
Nesta seção, apresentamos o fluxo de trabalho padrão do Qiskit Patterns utilizando um backend que não suporta portas fracionárias. Ao comparar os circuitos transpilados, você perceberá que a versão que utiliza portas fracionárias (da seção anterior) é mais compacta do que aquela sem portas fracionárias.
# step 1: map classical inputs to quantum problem
# `circuits` and `params` from the previous section are reused here# step 2: optimize circuits
backend_c = service.backend(backend_name) # w/o fractional gates
pm_c = generate_preset_pass_manager(
optimization_level=optimization_level, backend=backend_c
)
t_qc_c = pm_c.run(circuits)
print(t_qc_c[0].count_ops())
t_qc_c[0].draw("mpl", fold=-1)Output:
OrderedDict({'rz': 130, 'sx': 80, 'cz': 36, 'measure': 4, 'barrier': 2})
nnl_c = [qc.num_nonlocal_gates() for qc in t_qc_c]
depth_c = [qc.depth() for qc in t_qc_c]
duration_c = [
qc.estimate_duration(backend_c.target, unit="u") for qc in t_qc_c
]# step 3: execute
sampler_c = AerSampler.from_backend(backend_c)job = sampler_c.run(pubs=zip(t_qc_c, params), shots=shots)
print(job.job_id())Output:
f2cca29d-7263-4976-9e51-13a91b75c3ae
# step 4: post-processing
result = job.result()
fidelity_c = [fidelity(res) for res in result]
print(fidelity_c)Output:
[0.8625, 0.7605, 0.702, 0.671]
Comparação entre profundidades, durações e fidelidades
Nesta seção, comparamos o número de portas não locais e as fidelidades entre os circuitos com e sem portas fracionárias. Isso destaca os possíveis benefícios do uso de portas fracionárias em termos de eficiência e qualidade de execução.
plt.plot(qubits, depth_c, "-o", label="no fractional gates")
plt.plot(qubits, depth_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("depth")
plt.title("Comparison of depths")
plt.grid()
plt.legend()Output:
<matplotlib.legend.Legend at 0x116af3cb0>
plt.plot(qubits, duration_c, "-o", label="no fractional gates")
plt.plot(qubits, duration_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("duration (µs)")
plt.title("Comparison of durations")
plt.grid()
plt.legend()Output:
<matplotlib.legend.Legend at 0x11ea4f4d0>
plt.plot(qubits, nnl_c, "-o", label="no fractional gates")
plt.plot(qubits, nnl_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("number of non-local gates")
plt.title("Comparison of numbers of non-local gates")
plt.grid()
plt.legend()Output:
<matplotlib.legend.Legend at 0x1247fc440>
plt.plot(qubits, fidelity_c, "-o", label="no fractional gates")
plt.plot(qubits, fidelity_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("fidelity")
plt.title("Comparison of fidelities")
plt.grid()
plt.legend()Output:
<matplotlib.legend.Legend at 0x120b792b0>
Exemplo de hardware em grande escala
Nesta seção, comparamos o desempenho do fluxo de trabalho do kernel quântico com e sem portas fracionárias em hardware quântico com até 40 qubits.
Etapas 1 a 4 combinadas
O fluxo de trabalho segue a mesma estrutura do exemplo em pequena escala. Compilamos todos os circuitos, com e sem portas fracionárias, coletamos métricas e, em seguida, submetemos os circuitos a hardware quântico real.
# -------------------------Step 1-------------------------
qubits = list(range(4, 44, 4))
circuits = [quantum_kernel(i, reps=reps) for i in qubits]
params = [random_parameters(circ) for circ in circuits]
b_qc = [
circ.assign_parameters(param) for circ, param in zip(circuits, params)
]
def benchmark(b_qc, backend):
# -------------------------Step 2-------------------------
pm = generate_preset_pass_manager(optimization_level, backend=backend)
if "rzz" in backend.target.operation_names:
# workaround until https://github.com/Qiskit/qiskit-ibm-runtime/issues/2441 is resolved
pm.post_optimization = PassManager(
[
FoldRzzAngle(),
Optimize1qGatesDecomposition(target=backend.target),
RemoveIdentityEquivalent(target=backend.target),
]
)
t_qc = pm.run(b_qc)
nnl = [qc.num_nonlocal_gates() for qc in t_qc]
depth = [qc.depth() for qc in t_qc]
duration = [
qc.estimate_duration(backend_f.target, unit="u") for qc in t_qc
]
# -------------------------Step 3-------------------------
sampler = SamplerV2(mode=backend)
sampler.options.dynamical_decoupling.enable = True
sampler.options.dynamical_decoupling.sequence_type = "XY4"
sampler.options.dynamical_decoupling.skip_reset_qubits = True
sampler.options.environment.job_tags = ["TUT_FG"]
job = sampler.run(t_qc, shots=shots)
job_id = job.job_id()
return nnl, depth, duration, job_id
def postprocessing(job_id: str):
# -------------------------Step 4-------------------------
job = service.job(job_id)
result = job.result()
fidelities = [fidelity(result=res) for res in result]
usage = job.usage()
return fidelities, usage
backend_f = service.backend(backend_name, use_fractional_gates=True)
nnl_f, depth_f, duration_f, job_id_f = benchmark(
b_qc, backend_f
) # step 2 & 3
print("job id (w/ fractional gates):", job_id_f)
fidelity_f, usage_f = postprocessing(job_id_f) # step 4Output:
job id (w/ fractional gates): d8uasitbh0os73eqnpig
backend_c = service.backend(backend_name, use_fractional_gates=False)
nnl_c, depth_c, duration_c, job_id_c = benchmark(b_qc, backend_c)
print("job id (w/o fractional gates):", job_id_c)
fidelity_c, usage_c = postprocessing(job_id_c)Output:
job id (w/o fractional gates): d8uav3lposuc738pruug
Em seguida, comparamos as métricas.
plt.plot(qubits, depth_c, "-o", label="no fractional gates")
plt.plot(qubits, depth_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("depth")
plt.title("Comparison of depths")
plt.grid()
plt.legend()Output:
<matplotlib.legend.Legend at 0x12461e660>
plt.plot(qubits, duration_c, "-o", label="no fractional gates")
plt.plot(qubits, duration_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("duration (µs)")
plt.title("Comparison of durations")
plt.grid()
plt.legend()Output:
<matplotlib.legend.Legend at 0x11f2ac980>
plt.plot(qubits, nnl_c, "-o", label="no fractional gates")
plt.plot(qubits, nnl_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("number of non-local gates")
plt.title("Comparison of numbers of non-local gates")
plt.grid()
plt.legend()Output:
<matplotlib.legend.Legend at 0x125c91be0>
plt.plot(qubits, fidelity_c, "-o", label="no fractional gates")
plt.plot(qubits, fidelity_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("fidelity")
plt.title("Comparison of fidelities")
plt.grid()
plt.legend()Output:
<matplotlib.legend.Legend at 0x11fcf6e40>
Comparamos o tempo de uso da QPU com e sem portas fracionárias. Os resultados na célula a seguir mostram que os tempos de uso da QPU são quase idênticos.
print(f"no fractional gates: {usage_c} seconds")
print(f"fractional gates: {usage_f} seconds")Output:
no fractional gates: 8 seconds
fractional gates: 8 seconds
Tópico avançado: Usando apenas portas RX fracionárias
A necessidade de modificar o fluxo de trabalho ao usar portas fracionárias decorre principalmente da restrição dos ângulos de porta RZZ. No entanto, se você usar apenas as portas RX fracionárias e excluir as portas RZZ fracionárias, poderá continuar a seguir o fluxo de trabalho padrão dos padrões Qiskit. Essa abordagem ainda pode oferecer benefícios significativos, especialmente em circuitos que envolvem um grande número de portas RX e portas U, reduzindo o número total de portas e melhorando potencialmente o desempenho. Nesta seção, demonstramos como otimizar seus circuitos usando apenas portas RX fracionárias, omitindo as portas RZZ.
Para dar suporte a isso, fornecemos uma função utilitária que permite desativar uma porta de base específica em um objeto Target. Aqui, nós o usamos para desativar as portas RZZ.
def remove_instruction_from_target(target: Target, gate_name: str) -> Target:
new_target = Target(
description=target.description,
num_qubits=target.num_qubits,
dt=target.dt,
granularity=target.granularity,
min_length=target.min_length,
pulse_alignment=target.pulse_alignment,
acquire_alignment=target.acquire_alignment,
qubit_properties=target.qubit_properties,
concurrent_measurements=target.concurrent_measurements,
)
for name, qarg_map in target.items():
if name == gate_name:
continue
instruction = target.operation_from_name(name)
if qarg_map == {None: None}:
qarg_map = None
new_target.add_instruction(instruction, qarg_map, name=name)
return new_targetUsamos um circuito que consiste em portas U, CZ e RZZ como exemplo.
qc = n_local(3, "u", "cz", "linear", reps=1)
qc.rzz(1.1, 0, 1)
qc.draw("mpl")Output:
Primeiro, transpilamos o circuito para um backend que não oferece suporte a portas fracionárias.
pm_c = generate_preset_pass_manager(
optimization_level=optimization_level, backend=backend_c
)
t_qc = pm_c.run(qc)
print(t_qc.count_ops())
t_qc.draw("mpl")Output:
OrderedDict({'rz': 23, 'sx': 16, 'cz': 4})
Em seguida, transpilamos o mesmo circuito usando portas RX fracionárias, excluindo as portas RZZ. Isso resulta em uma pequena redução na contagem total de portas, graças à implementação mais eficiente das portas RX.
backend_f = service.backend(backend_name, use_fractional_gates=True)
target = remove_instruction_from_target(backend_f.target, "rzz")
pm_f = generate_preset_pass_manager(
optimization_level=optimization_level,
target=target,
)
t_qc = pm_f.run(qc)
print(t_qc.count_ops())
t_qc.draw("mpl")Output:
OrderedDict({'rz': 22, 'sx': 14, 'cz': 4, 'rx': 1})
Otimize portas U com portas RX fracionárias
Nesta seção, demonstramos como otimizar as portas U usando portas RX fracionárias, com base no mesmo circuito apresentado na seção anterior.
Transpilamos o circuito usando apenas portas RX fracionárias, excluindo as portas RZZ. Ao introduzir uma regra de decomposição personalizada, conforme mostrado a seguir, podemos reduzir o número de portas de um único qubit necessárias para implementar uma porta U.
Esse recurso está atualmente em discussão nesta questão do GitHub.
# special decomposition rule for UGate
x = ParameterVector("x", 3)
zxz = QuantumCircuit(1)
zxz.rz(x[2] - np.pi / 2, 0)
zxz.rx(x[0], 0)
zxz.rz(x[1] + np.pi / 2, 0)
DEFAULT_EQUIVALENCE_LIBRARY.add_equivalence(UGate(x[0], x[1], x[2]), zxz)Em seguida, aplicamos o transpiler utilizando constructor-beta a tradução fornecida pelo qiskit-basis-constructor pacote.
Como resultado, o número total de portas é reduzido em comparação com a transpilacão anterior.
pm_f = generate_preset_pass_manager(
optimization_level=optimization_level,
target=target,
translation_method="constructor-beta",
)
t_qc = pm_f.run(qc)
print(t_qc.count_ops())
t_qc.draw("mpl")Output:
OrderedDict({'rz': 16, 'rx': 9, 'cz': 4})
Próximas etapas
Se você achou este trabalho interessante, talvez se interesse pelo material a seguir:
- Guia sobre portas fracionárias
- Quando não usar portas fracionárias
FoldRzzAngleReferência da API da etapa do transpiler- Tutorial de treinamento do kernel do Quantum
- A aula sobre kernels quânticos no curso de aprendizado de máquina quântico