Aide à protéger la Grande barrière de corail avec tensorflow sur Kaggle Rejoignez Défi

Bonjour, beaucoup de mondes

Voir sur TensorFlow.org Exécuter dans Google Colab Voir la source sur GitHub Télécharger le cahier

Ce tutoriel montre comment un réseau de neurones classique peut apprendre à corriger les erreurs de calibrage de qubit. Elle introduit Cirq , un cadre Python pour créer, modifier, et invoquez Noisy échelle intermédiaire des circuits Quantum (NISQ), et montre comment les interfaces avec Cirq tensorflow Quantum.

Installer

pip install tensorflow==2.4.1

Installez TensorFlow Quantum :

pip install tensorflow-quantum
# Update package resources to account for version changes.
import importlib, pkg_resources
importlib.reload(pkg_resources)
<module 'pkg_resources' from '/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/pkg_resources/__init__.py'>

Importez maintenant TensorFlow et les dépendances du module :

import tensorflow as tf
import tensorflow_quantum as tfq

import cirq
import sympy
import numpy as np

# visualization tools
%matplotlib inline
import matplotlib.pyplot as plt
from cirq.contrib.svg import SVGCircuit
2021-10-12 11:16:10.499542: E tensorflow/stream_executor/cuda/cuda_driver.cc:328] failed call to cuInit: CUDA_ERROR_NO_DEVICE: no CUDA-capable device is detected

1. Les bases

1.1 Cirq et circuits quantiques paramétrés

Avant d' explorer tensorflow Quantum (TFQ), regardons quelques Cirq bases. Cirq est une bibliothèque Python pour l'informatique quantique de Google. Vous l'utilisez pour définir des circuits, y compris des portes statiques et paramétrées.

Cirq utilise sympy symboles pour représenter les paramètres libres.

a, b = sympy.symbols('a b')

Le code suivant crée un circuit à deux qubits à l'aide de vos paramètres :

# Create two qubits
q0, q1 = cirq.GridQubit.rect(1, 2)

# Create a circuit on these qubits using the parameters you created above.
circuit = cirq.Circuit(
    cirq.rx(a).on(q0),
    cirq.ry(b).on(q1), cirq.CNOT(control=q0, target=q1))

SVGCircuit(circuit)
findfont: Font family ['Arial'] not found. Falling back to DejaVu Sans.

svg

Pour évaluer les circuits, vous pouvez utiliser la cirq.Simulator interface. Vous remplacez les paramètres libres dans un circuit avec des numéros de spécifiques en passant dans un cirq.ParamResolver objet. Le code suivant calcule la sortie du vecteur d'état brut de votre circuit paramétré :

# Calculate a state vector with a=0.5 and b=-0.5.
resolver = cirq.ParamResolver({a: 0.5, b: -0.5})
output_state_vector = cirq.Simulator().simulate(circuit, resolver).final_state_vector
output_state_vector
array([ 0.9387913 +0.j        , -0.23971277+0.j        ,

        0.        +0.06120872j,  0.        -0.23971277j], dtype=complex64)

Les vecteurs d'état ne sont pas directement accessibles en dehors de la simulation (notez les nombres complexes dans la sortie ci-dessus). Pour être physiquement réaliste, vous devez spécifier une mesure, qui convertit un vecteur d'état en un nombre réel que les ordinateurs classiques peuvent comprendre. Cirq de mesures en utilisant des combinaisons de des opérateurs de \(\hat{Y}\)\(\hat{Z}\)Pauli \(\hat{X}\), \(\hat{Y}\)et \(\hat{Z}\). A titre d' illustration, les mesures de code suivant \(\hat{Z}_0\) et \(\frac{1}{2}\hat{Z}_0 + \hat{X}_1\) sur le vecteur d'état que vous venez simulé:

z0 = cirq.Z(q0)

qubit_map={q0: 0, q1: 1}

z0.expectation_from_state_vector(output_state_vector, qubit_map).real
0.8775825500488281
z0x1 = 0.5 * z0 + cirq.X(q1)

z0x1.expectation_from_state_vector(output_state_vector, qubit_map).real
-0.04063427448272705

1.2 Circuits quantiques comme tenseurs

Tensorflow Quantum (TFQ) fournit tfq.convert_to_tensor , une fonction qui convertit les objets en Cirq tenseurs. Cela vous permet d'envoyer des objets Cirq à nos couches quantiques et ops quantique . La fonction peut être appelée sur des listes ou des tableaux de Circuits Cirq et Cirq Paulis :

# Rank 1 tensor containing 1 circuit.
circuit_tensor = tfq.convert_to_tensor([circuit])

print(circuit_tensor.shape)
print(circuit_tensor.dtype)
(1,)
<dtype: 'string'>

Ce code les Cirq objets comme tf.string tenseurs que tfq opérations décodent au besoin.

# Rank 1 tensor containing 2 Pauli operators.
pauli_tensor = tfq.convert_to_tensor([z0, z0x1])
pauli_tensor.shape
TensorShape([2])

1.3 Simulation de circuit de dosage

TFQ fournit des méthodes de calcul des valeurs attendues, des échantillons et des vecteurs d'état. Pour l' instant, concentrons -nous sur les valeurs attentes.

L'interface de niveau le plus haut pour le calcul de valeurs moyennes est la tfq.layers.Expectation couche, qui est un tf.keras.Layer . Dans sa forme la plus simple, cette couche est équivalente à la simulation d' un circuit paramétrés sur plusieurs cirq.ParamResolvers ; cependant, TFQ permet le traitement par lots suivant la sémantique TensorFlow, et les circuits sont simulés à l'aide d'un code C++ efficace.

Créer un lot de valeurs à substituer à notre a et b paramètres:

batch_vals = np.array(np.random.uniform(0, 2 * np.pi, (5, 2)), dtype=np.float32)

L'exécution du circuit de traitement par lots sur les valeurs des paramètres dans Cirq nécessite une boucle :

cirq_results = []
cirq_simulator = cirq.Simulator()

for vals in batch_vals:
    resolver = cirq.ParamResolver({a: vals[0], b: vals[1]})
    final_state_vector = cirq_simulator.simulate(circuit, resolver).final_state_vector
    cirq_results.append(
        [z0.expectation_from_state_vector(final_state_vector, {
            q0: 0,
            q1: 1
        }).real])

print('cirq batch results: \n {}'.format(np.array(cirq_results)))
cirq batch results: 
 [[-0.86000198]
 [-0.83201134]
 [-0.93121868]
 [ 0.28362957]
 [ 0.72345072]]

La même opération est simplifiée dans TFQ :

tfq.layers.Expectation()(circuit,
                         symbol_names=[a, b],
                         symbol_values=batch_vals,
                         operators=z0)
<tf.Tensor: shape=(5, 1), dtype=float32, numpy=
array([[-0.8600013 ],
       [-0.83201194],
       [-0.931218  ],
       [ 0.28362915],
       [ 0.7234506 ]], dtype=float32)>

2. Optimisation hybride quantique-classique

Maintenant que vous avez vu les bases, nous allons utiliser tensorflow Quantum pour la construction d' un réseau de neurones quantique classique hybride. Vous entraînerez un réseau de neurones classique à contrôler un seul qubit. La commande sera optimisée pour préparer correctement le qubit dans la 0 ou 1 état, en surmontant une erreur systématique d'étalonnage simulé. Cette figure montre l'architecture :

Même sans réseau de neurones, il s'agit d'un problème simple à résoudre, mais le thème est similaire aux vrais problèmes de contrôle quantique que vous pourriez résoudre en utilisant TFQ. Il démontre une extrémité à bout exemple d'un calcul classique-quantique en utilisant la tfq.layers.ControlledPQC couche (paramétrés circuit Quantum) à l' intérieur d'un tf.keras.Model .

Pour la mise en œuvre de ce tutoriel, cette architecture se décompose en 3 parties :

  • Le circuit d'entrée ou circuit de point de données: Les trois premiers \(R\) portes.
  • Le circuit contrôlé: Les trois autres \(R\) portes.
  • Le dispositif de commande: la mise en réseau de neurones classique les paramètres du circuit contrôlé.

2.1 La définition du circuit contrôlé

Définissez une rotation d'un seul bit pouvant être apprise, comme indiqué dans la figure ci-dessus. Cela correspondra à notre circuit contrôlé.

# Parameters that the classical NN will feed values into.
control_params = sympy.symbols('theta_1 theta_2 theta_3')

# Create the parameterized circuit.
qubit = cirq.GridQubit(0, 0)
model_circuit = cirq.Circuit(
    cirq.rz(control_params[0])(qubit),
    cirq.ry(control_params[1])(qubit),
    cirq.rx(control_params[2])(qubit))

SVGCircuit(model_circuit)

svg

2.2 Le contrôleur

Définissez maintenant le réseau de contrôleurs :

# The classical neural network layers.
controller = tf.keras.Sequential([
    tf.keras.layers.Dense(10, activation='elu'),
    tf.keras.layers.Dense(3)
])

Étant donné un lot de commandes, le contrôleur délivre un lot de signaux de commande pour le circuit commandé.

Le contrôleur est initialisé de manière aléatoire, donc ces sorties ne sont pas encore utiles.

controller(tf.constant([[0.0],[1.0]])).numpy()
array([[ 0.        ,  0.        ,  0.        ],
       [ 0.37945798, -0.31607187,  0.23631476]], dtype=float32)

2.3 Connecter le contrôleur au circuit

Utilisation tfq pour connecter le contrôleur au circuit de commande, comme une seule keras.Model .

Voir le Guide de l' API fonctionnelle Keras pour plus sur ce style de définition du modèle.

Définissez d'abord les entrées du modèle :

# This input is the simulated miscalibration that the model will learn to correct.
circuits_input = tf.keras.Input(shape=(),
                                # The circuit-tensor has dtype `tf.string` 
                                dtype=tf.string,
                                name='circuits_input')

# Commands will be either `0` or `1`, specifying the state to set the qubit to.
commands_input = tf.keras.Input(shape=(1,),
                                dtype=tf.dtypes.float32,
                                name='commands_input')

Ensuite, appliquez des opérations à ces entrées pour définir le calcul.

dense_2 = controller(commands_input)

# TFQ layer for classically controlled circuits.
expectation_layer = tfq.layers.ControlledPQC(model_circuit,
                                             # Observe Z
                                             operators = cirq.Z(qubit))
expectation = expectation_layer([circuits_input, dense_2])

Empaqueter Maintenant ce calcul comme tf.keras.Model :

# The full Keras model is built from our layers.
model = tf.keras.Model(inputs=[circuits_input, commands_input],
                       outputs=expectation)

L'architecture du réseau est indiquée par le tracé du modèle ci-dessous. Comparez ce tracé de modèle au diagramme d'architecture pour vérifier l'exactitude.

tf.keras.utils.plot_model(model, show_shapes=True, dpi=70)

png

Ce modèle prend deux entrées : les commandes pour le contrôleur et le circuit d'entrée dont le contrôleur tente de corriger la sortie.

2.4 L'ensemble de données

Le modèle tente de sortir la valeur de mesure correcte correcte de \(\hat{Z}\) pour chaque commande. Les commandes et les valeurs correctes sont définies ci-dessous.

# The command input values to the classical NN.
commands = np.array([[0], [1]], dtype=np.float32)

# The desired Z expectation value at output of quantum circuit.
expected_outputs = np.array([[1], [-1]], dtype=np.float32)

Il ne s'agit pas de l'ensemble de données d'entraînement pour cette tâche. Chaque point de données dans l'ensemble de données a également besoin d'un circuit d'entrée.

2.4 Définition du circuit d'entrée

Le circuit d'entrée ci-dessous définit le mauvais étalonnage aléatoire que le modèle apprendra à corriger.

random_rotations = np.random.uniform(0, 2 * np.pi, 3)
noisy_preparation = cirq.Circuit(
  cirq.rx(random_rotations[0])(qubit),
  cirq.ry(random_rotations[1])(qubit),
  cirq.rz(random_rotations[2])(qubit)
)
datapoint_circuits = tfq.convert_to_tensor([
  noisy_preparation
] * 2)  # Make two copied of this circuit

Il existe deux copies du circuit, une pour chaque point de données.

datapoint_circuits.shape
TensorShape([2])

2.5 Formation

Avec les entrées définies , vous pouvez exécuter le test tfq modèle.

model([datapoint_circuits, commands]).numpy()
array([[-0.74668354],
       [-0.8791507 ]], dtype=float32)

Maintenant , exécutez un processus de formation standard pour ajuster ces valeurs vers les expected_outputs .

optimizer = tf.keras.optimizers.Adam(learning_rate=0.05)
loss = tf.keras.losses.MeanSquaredError()
model.compile(optimizer=optimizer, loss=loss)
history = model.fit(x=[datapoint_circuits, commands],
                    y=expected_outputs,
                    epochs=30,
                    verbose=0)
plt.plot(history.history['loss'])
plt.title("Learning to Control a Qubit")
plt.xlabel("Iterations")
plt.ylabel("Error in Control")
plt.show()

png

À partir de ce graphique, vous pouvez voir que le réseau de neurones a appris à surmonter le mauvais étalonnage systématique.

2.6 Vérifier les sorties

Utilisez maintenant le modèle entraîné pour corriger les erreurs d'étalonnage des qubits. Avec le Cirq :

def check_error(command_values, desired_values):
  """Based on the value in `command_value` see how well you could prepare
  the full circuit to have `desired_value` when taking expectation w.r.t. Z."""
  params_to_prepare_output = controller(command_values).numpy()
  full_circuit = noisy_preparation + model_circuit

  # Test how well you can prepare a state to get expectation the expectation
  # value in `desired_values`
  for index in [0, 1]:
    state = cirq_simulator.simulate(
        full_circuit,
        {s:v for (s,v) in zip(control_params, params_to_prepare_output[index])}
    ).final_state_vector
    expt = cirq.Z(qubit).expectation_from_state_vector(state, {qubit: 0}).real
    print(f'For a desired output (expectation) of {desired_values[index]} with'
          f' noisy preparation, the controller\nnetwork found the following '
          f'values for theta: {params_to_prepare_output[index]}\nWhich gives an'
          f' actual expectation of: {expt}\n')


check_error(commands, expected_outputs)
For a desired output (expectation) of [1.] with noisy preparation, the controller
network found the following values for theta: [-2.3688276   3.655123    0.24446163]
Which gives an actual expectation of: 0.9826769232749939

For a desired output (expectation) of [-1.] with noisy preparation, the controller
network found the following values for theta: [2.3142653  0.05689991 0.72213745]
Which gives an actual expectation of: -0.988701581954956

La valeur de la fonction de perte pendant la formation donne une idée approximative de la qualité de l'apprentissage du modèle. Plus la perte, plus les valeurs attendues dans la cellule ci - dessus est à desired_values . Si vous n'êtes pas aussi concerné par les valeurs des paramètres, vous pouvez toujours vérifier les sorties ci - dessus en utilisant tfq :

model([datapoint_circuits, commands])
<tf.Tensor: shape=(2, 1), dtype=float32, numpy=
array([[ 0.9826765 ],
       [-0.98870134]], dtype=float32)>

3 Apprendre à préparer les états propres des différents opérateurs

Le choix des \(\pm \hat{Z}\) eigenstates correspondant à 1 et 0 est arbitraire. Vous pourriez avoir aussi facilement voulu 1 correspondre à la \(+ \hat{Z}\) eigenstate et 0 pour correspondre aux \(-\hat{X}\) eigenstate. Une façon d'y parvenir est de spécifier un opérateur de mesure différent pour chaque commande, comme indiqué dans la figure ci-dessous :

Cela nécessite l' utilisation de tfq.layers.Expectation . Maintenant, votre entrée s'est agrandie pour inclure trois objets : circuit, commande et opérateur. La sortie est toujours la valeur attendue.

3.1 Nouvelle définition du modèle

Jetons un coup d'œil au modèle pour accomplir cette tâche :

# Define inputs.
commands_input = tf.keras.layers.Input(shape=(1),
                                       dtype=tf.dtypes.float32,
                                       name='commands_input')
circuits_input = tf.keras.Input(shape=(),
                                # The circuit-tensor has dtype `tf.string` 
                                dtype=tf.dtypes.string,
                                name='circuits_input')
operators_input = tf.keras.Input(shape=(1,),
                                 dtype=tf.dtypes.string,
                                 name='operators_input')

Voici le réseau du contrôleur :

# Define classical NN.
controller = tf.keras.Sequential([
    tf.keras.layers.Dense(10, activation='elu'),
    tf.keras.layers.Dense(3)
])

Combiner le circuit et le dispositif de commande dans un seul keras.Model utilisant tfq :

dense_2 = controller(commands_input)

# Since you aren't using a PQC or ControlledPQC you must append
# your model circuit onto the datapoint circuit tensor manually.
full_circuit = tfq.layers.AddCircuit()(circuits_input, append=model_circuit)
expectation_output = tfq.layers.Expectation()(full_circuit,
                                              symbol_names=control_params,
                                              symbol_values=dense_2,
                                              operators=operators_input)

# Contruct your Keras model.
two_axis_control_model = tf.keras.Model(
    inputs=[circuits_input, commands_input, operators_input],
    outputs=[expectation_output])

3.2 L'ensemble de données

Maintenant , vous allez aussi inclure les opérateurs que vous souhaitez mesurer pour chaque point de données pour vous fournissiez model_circuit :

# The operators to measure, for each command.
operator_data = tfq.convert_to_tensor([[cirq.X(qubit)], [cirq.Z(qubit)]])

# The command input values to the classical NN.
commands = np.array([[0], [1]], dtype=np.float32)

# The desired expectation value at output of quantum circuit.
expected_outputs = np.array([[1], [-1]], dtype=np.float32)

3.3 Formation

Maintenant que vous avez vos nouvelles entrées et sorties, vous pouvez vous entraîner à nouveau en utilisant keras.

optimizer = tf.keras.optimizers.Adam(learning_rate=0.05)
loss = tf.keras.losses.MeanSquaredError()

two_axis_control_model.compile(optimizer=optimizer, loss=loss)

history = two_axis_control_model.fit(
    x=[datapoint_circuits, commands, operator_data],
    y=expected_outputs,
    epochs=30,
    verbose=1)
Epoch 1/30
1/1 [==============================] - 0s 336ms/step - loss: 1.3425
Epoch 2/30
1/1 [==============================] - 0s 3ms/step - loss: 1.0469
Epoch 3/30
1/1 [==============================] - 0s 3ms/step - loss: 0.7611
Epoch 4/30
1/1 [==============================] - 0s 3ms/step - loss: 0.4976
Epoch 5/30
1/1 [==============================] - 0s 2ms/step - loss: 0.3565
Epoch 6/30
1/1 [==============================] - 0s 2ms/step - loss: 0.2611
Epoch 7/30
1/1 [==============================] - 0s 2ms/step - loss: 0.1574
Epoch 8/30
1/1 [==============================] - 0s 2ms/step - loss: 0.0850
Epoch 9/30
1/1 [==============================] - 0s 2ms/step - loss: 0.0568
Epoch 10/30
1/1 [==============================] - 0s 3ms/step - loss: 0.0536
Epoch 11/30
1/1 [==============================] - 0s 2ms/step - loss: 0.0608
Epoch 12/30
1/1 [==============================] - 0s 3ms/step - loss: 0.0648
Epoch 13/30
1/1 [==============================] - 0s 3ms/step - loss: 0.0581
Epoch 14/30
1/1 [==============================] - 0s 3ms/step - loss: 0.0473
Epoch 15/30
1/1 [==============================] - 0s 2ms/step - loss: 0.0396
Epoch 16/30
1/1 [==============================] - 0s 2ms/step - loss: 0.0348
Epoch 17/30
1/1 [==============================] - 0s 2ms/step - loss: 0.0310
Epoch 18/30
1/1 [==============================] - 0s 2ms/step - loss: 0.0278
Epoch 19/30
1/1 [==============================] - 0s 2ms/step - loss: 0.0258
Epoch 20/30
1/1 [==============================] - 0s 2ms/step - loss: 0.0249
Epoch 21/30
1/1 [==============================] - 0s 2ms/step - loss: 0.0230
Epoch 22/30
1/1 [==============================] - 0s 3ms/step - loss: 0.0189
Epoch 23/30
1/1 [==============================] - 0s 2ms/step - loss: 0.0132
Epoch 24/30
1/1 [==============================] - 0s 2ms/step - loss: 0.0078
Epoch 25/30
1/1 [==============================] - 0s 2ms/step - loss: 0.0040
Epoch 26/30
1/1 [==============================] - 0s 2ms/step - loss: 0.0019
Epoch 27/30
1/1 [==============================] - 0s 2ms/step - loss: 8.9138e-04
Epoch 28/30
1/1 [==============================] - 0s 2ms/step - loss: 5.2909e-04
Epoch 29/30
1/1 [==============================] - 0s 3ms/step - loss: 4.9192e-04
Epoch 30/30
1/1 [==============================] - 0s 2ms/step - loss: 6.7420e-04
plt.plot(history.history['loss'])
plt.title("Learning to Control a Qubit")
plt.xlabel("Iterations")
plt.ylabel("Error in Control")
plt.show()

png

La fonction de perte est tombée à zéro.

Le controller est disponible en tant que modèle autonome. Appelez le contrôleur et vérifiez sa réponse à chaque signal de commande. Il faudrait un travail de comparer correctement ces sorties au contenu de random_rotations .

controller.predict(np.array([0,1]))
array([[-2.846111  , -0.89885163, -1.3263428 ],
       [-1.2928145 , -0.22680755, -0.6467615 ]], dtype=float32)