Bantuan melindungi Great Barrier Reef dengan TensorFlow pada Kaggle Bergabung Tantangan

Halo, banyak dunia

Lihat di TensorFlow.org Jalankan di Google Colab Lihat sumber di GitHub Unduh buku catatan

Tutorial ini menunjukkan bagaimana jaringan saraf klasik dapat belajar untuk memperbaiki kesalahan kalibrasi qubit. Memperkenalkan Cirq , kerangka Python untuk membuat, mengedit, dan meminta bising Menengah Skala Quantum (NISQ) sirkuit, dan menunjukkan bagaimana antarmuka Cirq dengan TensorFlow Quantum.

Mempersiapkan

pip install tensorflow==2.4.1

Instal 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'>

Sekarang impor TensorFlow dan dependensi modul:

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. Dasar-dasarnya

1.1 Cirq dan sirkuit kuantum berparameter

Sebelum menjelajahi TensorFlow Quantum (TFQ), mari kita lihat ini di beberapa Cirq dasar-dasar. Cirq adalah pustaka Python untuk komputasi kuantum dari Google. Anda menggunakannya untuk mendefinisikan sirkuit, termasuk gerbang statis dan parameter.

Cirq menggunakan SymPy simbol untuk mewakili parameter bebas.

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

Kode berikut membuat sirkuit dua-qubit menggunakan parameter Anda:

# 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

Untuk mengevaluasi sirkuit, Anda dapat menggunakan cirq.Simulator antarmuka. Anda mengganti parameter bebas dalam sirkuit dengan nomor tertentu dengan melewati dalam cirq.ParamResolver objek. Kode berikut menghitung output vektor keadaan mentah dari sirkuit berparameter Anda:

# 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)

Vektor keadaan tidak dapat diakses secara langsung di luar simulasi (perhatikan bilangan kompleks pada keluaran di atas). Agar realistis secara fisik, Anda harus menentukan pengukuran, yang mengubah vektor keadaan menjadi bilangan real yang dapat dipahami oleh komputer klasik. Cirq menspesifikasikan pengukuran menggunakan kombinasi dari operator Pauli \(\hat{X}\), \(\hat{Y}\), dan \(\hat{Z}\). Sebagai ilustrasi, langkah-langkah kode berikut \(\hat{Z}_0\) dan \(\frac{1}{2}\hat{Z}_0 + \hat{X}_1\) pada vektor negara Anda hanya simulasi:

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 Sirkuit kuantum sebagai tensor

TensorFlow Quantum (TFQ) menyediakan tfq.convert_to_tensor , fungsi yang mengubah Cirq objek ke tensor. Hal ini memungkinkan Anda untuk mengirim Cirq objek untuk kami lapisan kuantum dan ops kuantum . Fungsi tersebut dapat dipanggil pada daftar atau larik Sirkuit Cirq dan 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'>

Mengkodekan ini Cirq objek sebagai tf.string tensor yang tfq operasi decode yang diperlukan.

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

1.3 Simulasi rangkaian batching

TFQ menyediakan metode untuk menghitung nilai harapan, sampel, dan vektor keadaan. Untuk saat ini, mari kita fokus pada nilai-nilai ekspektasi.

Antarmuka tingkat tertinggi untuk menghitung nilai ekspektasi adalah tfq.layers.Expectation lapisan, yang merupakan tf.keras.Layer . Dalam bentuk yang paling sederhana, lapisan ini setara dengan simulasi sirkuit parameter lebih banyak cirq.ParamResolvers ; namun, TFQ memungkinkan pengelompokan mengikuti semantik TensorFlow, dan sirkuit disimulasikan menggunakan kode C++ yang efisien.

Buat batch nilai untuk menggantikan kami a dan b parameter:

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

Eksekusi sirkuit batch pada nilai parameter di Cirq membutuhkan loop:

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]]

Operasi yang sama disederhanakan dalam 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. Optimalisasi kuantum-klasik hibrida

Sekarang bahwa Anda telah melihat dasar-dasar, mari kita gunakan TensorFlow Quantum untuk membangun hibrida kuantum klasik saraf net. Anda akan melatih jaringan saraf klasik untuk mengontrol satu qubit. Kontrol akan dioptimalkan untuk benar mempersiapkan qubit di 0 atau 1 negara, mengatasi kesalahan kalibrasi sistematis simulasi. Gambar ini menunjukkan arsitektur:

Bahkan tanpa jaringan saraf, ini adalah masalah yang mudah dipecahkan, tetapi temanya mirip dengan masalah kontrol kuantum nyata yang mungkin Anda selesaikan menggunakan TFQ. Ini menunjukkan contoh end-to-end dari komputasi kuantum-klasik menggunakan tfq.layers.ControlledPQC (parametrized Quantum Circuit) lapisan dalam dari tf.keras.Model .

Untuk implementasi tutorial ini, arsitektur ini dibagi menjadi 3 bagian:

  • Input rangkaian atau sirkuit datapoint: Tiga pertama \(R\) gerbang.
  • Rangkaian dikendalikan: Tiga lainnya \(R\) gerbang.
  • Controller: The klasik saraf-jaringan pengaturan parameter sirkuit dikendalikan.

2.1 Definisi sirkuit terkontrol

Tentukan rotasi bit tunggal yang dapat dipelajari, seperti yang ditunjukkan pada gambar di atas. Ini akan sesuai dengan sirkuit terkontrol kami.

# 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 Pengontrol

Sekarang tentukan jaringan pengontrol:

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

Diberikan sekumpulan perintah, pengontrol mengeluarkan sekumpulan sinyal kontrol untuk rangkaian yang dikendalikan.

Pengontrol diinisialisasi secara acak sehingga keluaran ini belum berguna.

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

2.3 Hubungkan pengontrol ke sirkuit

Gunakan tfq untuk menghubungkan controller untuk sirkuit dikendalikan, sebagai single keras.Model .

Lihat Keras panduan API Fungsional untuk lebih lanjut tentang gaya definisi Model.

Pertama-tama tentukan input ke model:

# 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')

Selanjutnya terapkan operasi pada input tersebut, untuk menentukan komputasinya.

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])

Sekarang paket perhitungan ini sebagai tf.keras.Model :

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

Arsitektur jaringan ditunjukkan oleh plot model di bawah ini. Bandingkan plot model ini dengan diagram arsitektur untuk memverifikasi kebenarannya.

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

png

Model ini mengambil dua input: Perintah untuk pengontrol, dan sirkuit input yang outputnya coba diperbaiki oleh pengontrol.

2.4 kumpulan data

Model upaya untuk output nilai pengukuran yang benar benar \(\hat{Z}\) untuk setiap perintah. Perintah dan nilai yang benar didefinisikan di bawah ini.

# 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)

Ini bukan keseluruhan kumpulan data pelatihan untuk tugas ini. Setiap titik data dalam kumpulan data juga membutuhkan sirkuit input.

2.4 Definisi sirkuit input

Sirkuit input di bawah ini mendefinisikan kesalahan kalibrasi acak yang akan dipelajari model untuk dikoreksi.

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

Ada dua salinan sirkuit, satu untuk setiap titik data.

datapoint_circuits.shape
TensorShape([2])

2.5 Pelatihan

Dengan input didefinisikan Anda dapat menguji-menjalankan tfq Model.

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

Sekarang menjalankan proses pelatihan standar untuk menyesuaikan nilai-nilai ini menuju 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

Dari plot ini Anda dapat melihat bahwa jaringan saraf telah belajar untuk mengatasi kesalahan kalibrasi yang sistematis.

2.6 Verifikasi keluaran

Sekarang gunakan model terlatih, untuk memperbaiki kesalahan kalibrasi qubit. Dengan 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

Nilai fungsi kerugian selama pelatihan memberikan gambaran kasar tentang seberapa baik model belajar. Semakin rendah kerugian, semakin dekat nilai-nilai harapan dalam sel di atas adalah untuk desired_values . Jika Anda tidak peduli dengan nilai-nilai parameter, Anda selalu dapat memeriksa output dari atas menggunakan tfq :

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

3 Belajar menyiapkan eigenstate dari operator yang berbeda

Pemilihan \(\pm \hat{Z}\) eigenstates sesuai dengan 1 dan 0 adalah sewenang-wenang. Anda bisa saja dengan mudah ingin 1 untuk berkorespondensi dengan \(+ \hat{Z}\) eigenstate dan 0 berkorespondensi ke \(-\hat{X}\) eigenstate. Salah satu cara untuk mencapai ini adalah dengan menentukan operator pengukuran yang berbeda untuk setiap perintah, seperti yang ditunjukkan pada gambar di bawah ini:

Hal ini membutuhkan penggunaan tfq.layers.Expectation . Sekarang input Anda telah berkembang menjadi tiga objek: sirkuit, perintah, dan operator. Outputnya masih nilai ekspektasi.

3.1 Definisi model baru

Mari kita lihat model untuk menyelesaikan tugas ini:

# 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')

Berikut adalah jaringan pengontrol:

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

Menggabungkan sirkuit dan controller ke dalam satu keras.Model menggunakan 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 Kumpulan data

Sekarang Anda juga akan mencakup operator Anda ingin mengukur untuk setiap datapoint Anda menyediakan untuk 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 Pelatihan

Sekarang setelah Anda memiliki input dan output baru, Anda dapat melatih sekali lagi menggunakan 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

Fungsi kerugian telah turun ke nol.

The controller tersedia sebagai model berdiri sendiri. Panggil pengontrol, dan periksa responsnya terhadap setiap sinyal perintah. Ini akan mengambil beberapa pekerjaan untuk benar membandingkan output ini ke isi random_rotations .

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