Skip to main content
IBM Quantum Platform

Compilador cuántico aproximado

qiskit.synthesis.unitary.aqc

Implementación del compilador cuántico aproximado descrito en el artículo [1].


Interfaz

A la interfaz pública principal de este módulo se llega pasando unitary_synthesis_method='aqc' a transpile(). Esto cambiará el método de síntesis para utilizar AQCSynthesisPlugin. Las clases individuales son:

AQC([optimizador, semilla] )Una implementación genérica del Compilador Cuántico Aproximado.
ApproximateCircuit(num_qubits[, nombre] )Una clase base que representa un circuito aproximado.
ApproximatingObjective()Una clase base para la definición de un problema de optimización.
CNOTUnitCircuit(num_qubits, cnots[, tol, nombre] )Clase que representa un circuito aproximado basado en bloques de unidades CNOT.
CNOTUnitObjective(num_qubits, cnots)Una clase base para la definición de un problema basado en la unidad CNOT.
DefaultCNOTUnitObjective(num_qubits, cnots)Una aplicación ingenua de la función objetivo basada en unidades CNOT.
FastCNOTUnitObjective(num_qubits, cnots)Implementación de la función objetivo y la calculadora de gradiente, que es similar a DefaultCNOTUnitObjective pero varias veces más rápida.

Detalle matemático

Estamos interesados en compilar un circuito cuántico, que formalizamos como encontrar la mejor representación del circuito en términos de una secuencia ordenada de puertas de una matriz unitaria objetivo UU(d)U\in U(d), con algunas restricciones de hardware adicionales. En particular, buscamos representaciones que puedan limitarse en términos de conectividad de hardware, así como de profundidad de compuerta, y elegimos una base de compuertas en términos de compuertas CNOT y de rotación. Recordemos que la combinación de CNOT y puertas de rotación es universal en SU(d)SU(d) y, por tanto, no limita la compilación.

Para definir adecuadamente lo que entendemos por «mejor representación del circuito», definimos la métrica como la norma de Frobenius entre la matriz unitaria del circuito compilado VV y la matriz unitaria objetivo UU, es decir, VUF\|V - U\|_{\mathrm{F}}. Esta elección se basa en consideraciones de programación matemática y está relacionada con otras formulaciones que aparecen en la bibliografía especializada. Analicemos el problema con más detalle.

Sea nn el número de qubits y d=2nd=2^n. Dada una estructura CNOT ctct y un vector de ángulos de rotación θ\theta, el circuito paramétrico forma una matriz Vct(θ)SU(d)Vct(\theta)\in SU(d). Si nos dan un circuito paramétrico que forma una matriz USU(d)U\in SU(d), entonces nos gustaría calcular

argmaxθ1dVct(θ),U\mathrm{argmax}_{\theta}\frac{1}{d}|\langle Vct(\theta),U\rangle|

donde el producto interior es el producto interior de Frobenius. Nótese que V,Ud|\langle V,U\rangle|\leq d para todos los unitarios UU y VV, por lo que el objetivo tiene alcance en [0,1][0,1].

Nuestra estrategia consiste en maximizar

1dVct(θ),U\frac{1}{d}\Re \langle Vct(\theta),U\rangle

utilizando su gradiente. A continuación analizaremos los detalles con un ejemplo.

Mientras que el rango de VctVct es un subconjunto de SU(d)SU(d) por construcción, el circuito objetivo puede formar una matriz unitaria general. Sin embargo, para cualquier UU(d)U\in U(d),

exp(2πik/d)det(U)1/dUSU(d) for all k{0,,d1}.\frac{\exp(2\pi i k/d)}{\det(U)^{1/d}}U\in SU(d)\text{ for all }k\in\{0,\ldots,d-1\}.

Por lo tanto, debemos normalizar el circuito de destino por su fase global y, a continuación, compilar aproximadamente el circuito normalizado. Podemos volver a añadir la fase global después.

En el algoritmo, UU' denota la matriz objetivo no normalizada y UU la matriz objetivo normalizada. Ahora que tenemos UU, damos la función gradiente al optimizador del método de Nesterov y calculamos θ\theta.

Para volver a añadir la fase global, podemos formar el circuito de control como

Vct(θ),UVct(θ),UVct(θ).\frac{\langle Vct(\theta),U'\rangle}{|\langle Vct(\theta),U'\rangle|}Vct(\theta).

Obsérvese que, aunque en el artículo optimizamos utilizando el método de Nesterov, lo hicimos por sus garantías de convergencia, no por su velocidad en la práctica. Es mucho más rápido utilizar L-BFGS, que se utiliza como optimizador por defecto en esta implementación.

Una utilización básica del algoritmo AQC debería constar de los siguientes pasos:

# Define a target circuit as a unitary matrix
unitary = ...

# Define a number of qubits for the algorithm, at least 3 qubits
num_qubits = round(math.log2(unitary.shape[0]))

# Choose a layout of the CNOT structure for the approximate circuit, e.g. ``spin`` for
# a linear layout.
layout = options.get("layout") or "spin"

# Choose a connectivity type, e.g. ``full`` for full connectivity between qubits.
connectivity = options.get("connectivity") or "full"

# Define a targeted depth of the approximate circuit in the number of CNOT units.
depth = int(options.get("depth") or 0)

# Generate a network made of CNOT units
cnots = make_cnot_network(
    num_qubits=num_qubits,
    network_layout=layout,
    connectivity_type=connectivity,
    depth=depth
)

# Create an optimizer to be used by AQC
optimizer = partial(scipy.optimize.minimize, method="L-BFGS-B")

# Create an instance
aqc = AQC(optimizer)

# Create a template circuit that will approximate our target circuit
approximate_circuit = CNOTUnitCircuit(num_qubits=num_qubits, cnots=cnots)

# Create an objective that defines our optimization problem
approximating_objective = DefaultCNOTUnitObjective(num_qubits=num_qubits, cnots=cnots)

# Run optimization process to compile the unitary
aqc.compile_unitary(
    target_matrix=unitary,
    approximate_circuit=approximate_circuit,
    approximating_objective=approximating_objective
)

Ahora approximate_circuit es un circuito que se aproxima al unitario objetivo hasta cierto grado y puede utilizarse en lugar de la matriz original.

Para ello se utiliza una función auxiliar, make_cnot_network.

make_cnot_network

qiskit.synthesis.unitary.aqc.make_cnot_network(num_qubits, network_layout='spin', connectivity_type='full', depth=0)

GitHub

Genera una red formada por bloques de construcción, cada uno de los cuales contiene una puerta CNOT y posiblemente algunas de un solo qubit. Esta red modela un operador cuántico en cuestión. Obsérvese que cada bloque de construcción tiene 2 entradas y salidas correspondientes a un par de qubits. Lo que en realidad devolvemos aquí es una cadena de índices de pares de qubits compartidos por cada bloque de construcción de una fila.

Parámetros

  • num_qubits (int) – número de qubits.
  • network_layout (str) – tipo de geometría de la red, {"sequ", "spin", "cart", "cyclic_spin", "cyclic_line"}.
  • connectivity_type (str) – tipo de conectividad inter-qubit, {"full", "line", "star"}.
  • depth (int) – profundidad de la red CNOT, es decir, el número de capas, donde cada capa consiste en un único bloque CNOT; se seleccionará el valor por defecto, si L <= 0.

Devuelve

Una matriz de tamaño (2, N) matriz que define las capas en cnot-network, donde N

es igual L, o está definido por un tipo concreto de la red.

Eleva

ValueError - si se pasa un tipo de diseño de red CNOT o un número de qubits o una combinación de parámetros no admitidos.

Tipo de retorno

ndarray

Se puede aprovechar la versión acelerada de la función objetivo. Implementa el mismo algoritmo matemático que el predeterminado DefaultCNOTUnitObjective pero se ejecuta varias veces más rápido. La instanciación de la clase de función objetivo acelerada es similar al caso por defecto:

# Crear un objetivo que defina nuestro problema de optimización approximating_objective = FastCNOTUnitObjective(num_qubits=num_qubits, cnots=cnots)

El resto del código del ejemplo anterior no cambia.

Referencias

[1] : Liam Madden, Andrea Simonetto, Los mejores problemas aproximados de compilación cuántica.

arXiv:2106.05649

¿Le ha resultado útil esta página?
Informe de un error, de una errata o solicite contenido en GitHub.