Spanish
Idiomas
English
Bengali
French
Hindi
Italian
Japanese
Korean
Malayalam
Russian
Spanish
Tamil
Turkish
Vietnamese
Shortcuts

Nota

Esta página fue generada a partir de docs/tutorials/01_neural_networks.ipynb.

Redes Neuronales Cuánticas

Este cuaderno muestra las diferentes implementaciones genéricas de redes neuronales cuánticas (quantum neural network, QNN) proporcionadas en Qiskit Machine Learning. Las redes están pensadas como unidades computacionales independientes de la aplicación en las que se pueden utilizar para muchos casos de uso diferentes. Dependiendo de la aplicación, un tipo particular de red puede ser más o menos adecuado y puede ser necesario configurarlo de una manera particular. Las siguientes redes neuronales disponibles diferentes se discutirán ahora con más detalle:

  1. NeuralNetwork: La interfaz para redes neuronales.

  2. OpflowQNN: Una red basada en la evaluación de observables de la mecánica cuántica.

  3. TwoLayerQNN: Una implementación especial de OpflowQNN para mayor conveniencia.

  4. CircuitQNN: Una red basada en los ejemplos resultantes de medir un circuito cuántico.

[1]:
import numpy as np

from qiskit import Aer, QuantumCircuit
from qiskit.circuit import Parameter
from qiskit.circuit.library import RealAmplitudes, ZZFeatureMap
from qiskit.opflow import StateFn, PauliSumOp, AerPauliExpectation, ListOp, Gradient
from qiskit.utils import QuantumInstance, algorithm_globals

algorithm_globals.random_seed = 42
[2]:
# set method to calculcate expected values
expval = AerPauliExpectation()

# define gradient method
gradient = Gradient()

# define quantum instances (statevector and sample based)
qi_sv = QuantumInstance(Aer.get_backend("aer_simulator_statevector"))

# we set shots to 10 as this will determine the number of samples later on.
qi_qasm = QuantumInstance(Aer.get_backend("aer_simulator"), shots=10)

1. NeuralNetwork

El NeuralNetwork representa la interfaz para todas las redes neuronales disponibles en Qiskit Machine Learning. Este muestra un pase hacia adelante y un pase hacia atrás tomando las muestras y ponderaciones entrenables como entrada. Un NeuralNetwork no contiene ninguna capacidad de entrenamiento, esto se le asigna a los algoritmos y a las aplicaciones. Por ello, un NeuralNetwork tampoco guarda los valores de las ponderaciones entrenables. Enseguida, se introducen las diferentes implementaciones de estas interfaces.

Supongamos que tenemos un NeuralNetwork llamado nn. A continuación, el paso nn.forward (input, weights) toma cualquiera de las entradas planas para los datos y las ponderaciones del tamaño nn.num_inputs y nn.num_weights, respectivamente. NeuralNetwork es compatible con la carga de entradas y devuelve lotes de salida de la forma correspondiente.

2. OpflowQNN

El OpflowQNN toma un operador (parametrizado) de Qiskit y aprovecha el entorno de trabajo de gradiente de Qiskit para proporcionar el pase hacia atrás. Tal operador puede ser, por ejemplo, un valor esperado de un observable en la mecánica cuántica con respecto a un estado cuántico parametrizado. Los parámetros se pueden utilizar para cargar datos clásicos y representar pesos entrenables. El OpflowQNN también permite listas de operadores y estructuras más complejas para construir QNN más complejas.

[3]:
from qiskit_machine_learning.neural_networks import OpflowQNN
[4]:
# construct parametrized circuit
params1 = [Parameter("input1"), Parameter("weight1")]
qc1 = QuantumCircuit(1)
qc1.h(0)
qc1.ry(params1[0], 0)
qc1.rx(params1[1], 0)
qc_sfn1 = StateFn(qc1)

# construct cost operator
H1 = StateFn(PauliSumOp.from_list([("Z", 1.0), ("X", 1.0)]))

# combine operator and circuit to objective function
op1 = ~H1 @ qc_sfn1
print(op1)
ComposedOp([
  OperatorMeasurement(1.0 * Z
  + 1.0 * X),
  CircuitStateFn(
       ┌───┐┌────────────┐┌─────────────┐
  q_0: ┤ H ├┤ Ry(input1) ├┤ Rx(weight1) ├
       └───┘└────────────┘└─────────────┘
  )
])
[5]:
# construct OpflowQNN with the operator, the input parameters, the weight parameters,
# the expected value, gradient, and quantum instance.
qnn1 = OpflowQNN(op1, [params1[0]], [params1[1]], expval, gradient, qi_sv)
[6]:
# define (random) input and weights
input1 = algorithm_globals.random.random(qnn1.num_inputs)
weights1 = algorithm_globals.random.random(qnn1.num_weights)
[7]:
# QNN forward pass
qnn1.forward(input1, weights1)
[7]:
array([[0.08242345]])
[8]:
# QNN batched forward pass
qnn1.forward([input1, input1], weights1)
[8]:
array([[0.08242345],
       [0.08242345]])
[9]:
# QNN backward pass
qnn1.backward(input1, weights1)
[9]:
(None, array([[[0.2970094]]]))
[10]:
# QNN batched backward pass
qnn1.backward([input1, input1], weights1)
[10]:
(None,
 array([[[0.2970094]],

        [[0.2970094]]]))

La combinación de multiples observables en un ListOp también permite crear QNNs más complejos

[11]:
op2 = ListOp([op1, op1])
qnn2 = OpflowQNN(op2, [params1[0]], [params1[1]], expval, gradient, qi_sv)
[12]:
# QNN forward pass
qnn2.forward(input1, weights1)
[12]:
array([[0.08242345, 0.08242345]])
[13]:
# QNN backward pass
qnn2.backward(input1, weights1)
[13]:
(None,
 array([[[0.2970094],
         [0.2970094]]]))

3. TwoLayerQNN

El TwoLayerQNN es un OpflowQNN especial en \(n\) qubits qué consiste en primer lugar de un mapa de características para insertar datos y en segundo de un ansatz entrenado. El observable predeterminado es \(Z^{\otimes n}\), es decir, paridad.

[14]:
from qiskit_machine_learning.neural_networks import TwoLayerQNN
[15]:
# specify the number of qubits
num_qubits = 3
[16]:
# specify the feature map
fm = ZZFeatureMap(num_qubits, reps=2)
fm.draw(output="mpl")
[16]:
../_images/tutorials_01_neural_networks_20_0.png
[17]:
# specify the ansatz
ansatz = RealAmplitudes(num_qubits, reps=1)
ansatz.draw(output="mpl")
[17]:
../_images/tutorials_01_neural_networks_21_0.png
[18]:
# specify the observable
observable = PauliSumOp.from_list([("Z" * num_qubits, 1)])
print(observable)
1.0 * ZZZ
[19]:
# define two layer QNN
qnn3 = TwoLayerQNN(
    num_qubits, feature_map=fm, ansatz=ansatz, observable=observable, quantum_instance=qi_sv
)
[20]:
# define (random) input and weights
input3 = algorithm_globals.random.random(qnn3.num_inputs)
weights3 = algorithm_globals.random.random(qnn3.num_weights)
[21]:
# QNN forward pass
qnn3.forward(input3, weights3)
[21]:
array([[0.18276559]])
[22]:
# QNN backward pass
qnn3.backward(input3, weights3)
[22]:
(None,
 array([[[ 0.10231208,  0.10656571,  0.41017902,  0.16528909,
          -0.27780262,  0.41365763]]]))

4. CircuitQNN

El CircuitQNN está basado en un QuantumCircuit (parametrizado). Esto puede tomar tanto parámetros de entrada como de ponderación y producir muestras a partir de la medición. Las muestras pueden interpretarse como probabilidades de medir el índice entero correspondiente a una cadena de bits o directamente como un lote de salida binaria. En el caso de las probabilidades, los gradientes se pueden estimar de manera eficiente y el CircuitQNN también proporciona un pase hacia atrás. En el caso de las muestras, la diferenciación no es posible y el pase hacia atrás devuelve (None, None).

Además, el CircuitQNN permite especificar una función interpret para procesar las muestras. Se espera que esto tome un entero medido (de una cadena de bits) y que se mapee con un nuevo índice, es decir, un entero no negativo. En este caso, es necesario proporcionar la forma de la salida y las probabilidades se agregan en consecuencia.

Un CircuitQNN``puede ser configurado para devolver los vectores de probabilidad dispersos, así como densos. Si no se utiliza la función ``interpret, la dimensión del vector de probabilidad escala exponencialmente con el número de qubits y normalmente se sugiere una recomendación dispersa. En caso de que una función interpret se utilice, va a depender del resultado esperado. Si, por ejemplo, un índice se correlaciona con la paridad de la cadena de bits correspondiente, es decir, a 0 o 1, una salida densa tiene sentido y el resultado será un vector de probabilidad de longitud 2.

[23]:
from qiskit_machine_learning.neural_networks import CircuitQNN
[24]:
qc = RealAmplitudes(num_qubits, entanglement="linear", reps=1)
qc.draw(output="mpl")
[24]:
../_images/tutorials_01_neural_networks_29_0.png

4.1 Salida: probabilidades de enteros dispersos

[25]:
# specify circuit QNN
qnn4 = CircuitQNN(qc, [], qc.parameters, sparse=True, quantum_instance=qi_qasm)
[26]:
# define (random) input and weights
input4 = algorithm_globals.random.random(qnn4.num_inputs)
weights4 = algorithm_globals.random.random(qnn4.num_weights)
[27]:
# QNN forward pass
qnn4.forward(input4, weights4).todense()  # returned as a sparse matrix
[27]:
array([[0.6, 0.1, 0. , 0. , 0. , 0. , 0. , 0.3]])
[28]:
# QNN backward pass, returns a tuple of sparse matrices
qnn4.backward(input4, weights4)
[28]:
(None, <COO: shape=(1, 8, 6), dtype=float64, nnz=33, fill_value=0.0>)

4.2 Salida: probabilidades de paridad densa

[29]:
# specify circuit QNN
parity = lambda x: "{:b}".format(x).count("1") % 2
output_shape = 2  # this is required in case of a callable with dense output
qnn6 = CircuitQNN(
    qc,
    [],
    qc.parameters,
    sparse=False,
    interpret=parity,
    output_shape=output_shape,
    quantum_instance=qi_qasm,
)
[30]:
# define (random) input and weights
input6 = algorithm_globals.random.random(qnn6.num_inputs)
weights6 = algorithm_globals.random.random(qnn6.num_weights)
[31]:
# QNN forward pass
qnn6.forward(input6, weights6)
[31]:
array([[0.5, 0.5]])
[32]:
# QNN backward pass
qnn6.backward(input6, weights6)
[32]:
(None,
 array([[[-0.2 ,  0.1 , -0.15, -0.15,  0.15, -0.15],
         [ 0.2 , -0.1 ,  0.15,  0.15, -0.15,  0.15]]]))

4.3 Salida: Muestras

[33]:
# specify circuit QNN
qnn7 = CircuitQNN(qc, [], qc.parameters, sampling=True, quantum_instance=qi_qasm)
[34]:
# define (random) input and weights
input7 = algorithm_globals.random.random(qnn7.num_inputs)
weights7 = algorithm_globals.random.random(qnn7.num_weights)
[35]:
# QNN forward pass, results in samples of measured bit strings mapped to integers
qnn7.forward(input7, weights7)
[35]:
array([[[0.],
        [1.],
        [0.],
        [7.],
        [0.],
        [6.],
        [0.],
        [0.],
        [1.],
        [6.]]])
[36]:
# QNN backward pass
qnn7.backward(input7, weights7)
[36]:
(None, None)

4.4 Salida: Muestras de Paridad

[37]:
# specify circuit QNN
qnn8 = CircuitQNN(qc, [], qc.parameters, sampling=True, interpret=parity, quantum_instance=qi_qasm)
[38]:
# define (random) input and weights
input8 = algorithm_globals.random.random(qnn8.num_inputs)
weights8 = algorithm_globals.random.random(qnn8.num_weights)
[39]:
# QNN forward pass, results in samples of measured bit strings
qnn8.forward(input8, weights8)
[39]:
array([[[0.],
        [1.],
        [1.],
        [0.],
        [1.],
        [0.],
        [0.],
        [0.],
        [1.],
        [1.]]])
[40]:
# QNN backward pass
qnn8.backward(input8, weights8)
[40]:
(None, None)
[43]:
import qiskit.tools.jupyter

%qiskit_version_table
%qiskit_copyright

Version Information

Qiskit SoftwareVersion
qiskit-terra0.19.0
qiskit-aer0.9.0
qiskit-ignis0.7.0
qiskit-ibmq-provider0.17.0
qiskit-aqua0.10.0
qiskit-machine-learning0.3.0
System information
Python3.8.10 (default, Jun 2 2021, 10:49:15) [GCC 9.4.0]
OSLinux
CPUs4
Memory (Gb)7.6849517822265625
Sat Aug 28 01:04:52 2021 IST

This code is a part of Qiskit

© Copyright IBM 2017, 2021.

This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.

Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.

[ ]: