¿Qué método de ejecutar una puerta de control múltiple es más eficiente?

Oct 24 2020

Estoy interesado en ejecutar puertas de control múltiple y he encontrado dos métodos para hacerlo, como se explica a continuación.

  1. El primero está tomado de Nielsen y Chuang (2010) de su Figura 4.10. Este método requiere un auxiliar n-1 adicional (o funciona como lo llaman) qubits junto con$2(n-1)$ Puertas Toffoli y una sola $CU$ puerta donde $n$ es el número de qubits de control.

.

  1. El segundo método de Barenco et al. (1995) (en su página 17) utiliza un total de$2^{n}-1$ $V$ puertas y $2^{n}-2$ Puertas CNOT donde $n$ es el número de qubits de control y $V^4=U$ en este ejemplo.

Me parece que el método Nielsen usa menos puertas pero tiene la desventaja de requerir qubits auxiliares adicionales. Entonces, ¿cuál de estos métodos es más eficiente? ¿O hay otro método que debería considerar usar? En caso de que ayude, estoy buscando usar uno de estos métodos en mi circuito qiskit para ejecutarlo en el hardware cuántico real.

Respuestas

2 luciano Oct 24 2020 at 02:06

Como muchas cosas en la vida, la respuesta es "depende".

Si el backend tiene soporte para puertas Toffoli (es decir, en lenguaje Qiskit, son parte de su conjunto de puertas base), entonces la opción 1 es mejor. Si, como en la mayoría de los backends de IBM en este momento, solo tiene CX, entonces la opción 2 parece mejor. Por no hablar de consideraciones de topología como el mapa de acoplamiento.

Si un método para descomponer una MCU es lo suficientemente general, entonces debería ser una tarea para el compilador de circuitos . El compilador debe ser lo suficientemente inteligente como para tener en cuenta todos los elementos (la base de destino, el mapa de conectividad de backend, etc.) y brindarle la mejor descomposición (envíe un problema si cree que el compilador puede hacer un mejor trabajo). Por ejemplo:

from qiskit import QuantumCircuit, transpile
from qiskit.circuit.library.standard_gates import C3XGate
qc = QuantumCircuit(4)
qc.append(C3XGate(), [0, 1, 2, 3])
print(qc)
q_0: ──■──
       │  
q_1: ──■──
       │  
q_2: ──■──
     ┌─┴─┐
q_3: ┤ X ├
     └───┘

Con optimization_level=3debería darte el mejor resultado, considerando una ['u', 'cx']base:

transpiled = transpile(qc, basis_gates=['u', 'cx'], optimization_level=3)
print('depth:', transpiled.depth())
print('gates:', sum(transpiled.count_ops().values()))
depth: 35
gates: 42

Si también agrega un mapa de acoplamiento:

transpiled = transpile(qc, basis_gates=['u', 'cx'], coupling_map=[[0,1], [1,2], [2,3]], optimization_level=3)
print('depth:', transpiled.depth())
print('gates:', sum(transpiled.count_ops().values()))
depth: 79
gates: 127

Si tiene un backend concreto:

from qiskit import IBMQ

IBMQ.load_account()
provider = IBMQ.get_provider(hub='ibm-q')
backend = provider.get_backend('ibmq_16_melbourne')
transpiled = transpile(qc, backend=backend, optimization_level=3)
print('depth:', transpiled.depth())
print('gates:', sum(transpiled.count_ops().values()))
depth: 48
gates: 68