Halaman ini diterjemahkan oleh Cloud Translation API.
Switch to English

Halo, banyak dunia

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

Tutorial ini menunjukkan bagaimana jaringan neural klasik dapat belajar memperbaiki kesalahan kalibrasi qubit. Ini memperkenalkan Cirq , kerangka kerja Python untuk membuat, mengedit, dan memanggil sirkuit Noisy Intermediate Scale Quantum (NISQ), dan mendemonstrasikan bagaimana Cirq berinteraksi dengan TensorFlow Quantum.

Mempersiapkan

pip install -q tensorflow==2.1.0

Instal TensorFlow Quantum:

pip install -q tensorflow-quantum
ERROR: After October 2020 you may experience errors when installing or updating packages. This is because pip will change the way that it resolves dependency conflicts.

We recommend you use --use-feature=2020-resolver to test your packages with the new resolver before it becomes the default.

tensorflow-metadata 0.23.0 requires absl-py<0.9,>=0.7, but you'll have absl-py 0.9.0 which is incompatible.
google-api-core 1.22.1 requires protobuf>=3.12.0, but you'll have protobuf 3.8.0 which is incompatible.

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

1. Dasar-dasar

1.1 Cirq dan sirkuit kuantum berparameter

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

Cirq menggunakan simbol SymPy untuk mewakili parameter gratis.

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 antarmuka cirq.Simulator . Anda mengganti parameter gratis di sirkuit dengan angka tertentu dengan meneruskan objek cirq.ParamResolver . Kode berikut menghitung keluaran vektor keadaan mentah dari rangkaian parameter 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
output_state_vector
array([ 0.9387913 +0.j        , -0.23971277+0.j        ,

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

Vektor status tidak dapat langsung diakses 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 komputer klasik. Cirq menentukan pengukuran menggunakan kombinasi operator Pauli $ \ hat {X} $, $ \ hat {Y} $, dan $ \ hat {Z} $. Sebagai ilustrasi, kode berikut mengukur $ \ hat {Z} _0 $ dan $ \ frac {1} {2} \ hat {Z} _0 + \ hat {X} _1 $ pada vektor status yang baru saja Anda simulasikan:

z0 = cirq.Z(q0)

qubit_map={q0: 0, q1: 1}

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

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

1.2 Rangkaian kuantum sebagai tensor

TensorFlow Quantum (TFQ) menyediakan tfq.convert_to_tensor , sebuah fungsi yang mengubah objek Cirq menjadi tensor. Ini memungkinkan Anda mengirim objek Cirq ke lapisan kuantum dan operasi kuantum kami . Fungsi tersebut dapat dipanggil pada list atau array Cirq Circuit 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 batch

TFQ menyediakan metode untuk menghitung nilai ekspektasi, sampel, dan vektor status. Untuk saat ini, mari fokus pada nilai ekspektasi .

Antarmuka tingkat tertinggi untuk menghitung nilai ekspektasi adalah lapisan tfq.layers.Expectation , yang merupakan tf.keras.Layer . Dalam bentuk yang paling sederhana, lapisan ini setara dengan simulasi rangkaian parameter pada 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 rangkaian batch atas 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 = cirq_simulator.simulate(circuit, resolver).final_state
    cirq_results.append(
        [z0.expectation_from_wavefunction(final_state, {
            q0: 0,
            q1: 1
        }).real])

print('cirq batch results: \n {}'.format(np.array(cirq_results)))
cirq batch results: 
 [[-0.83266681]
 [ 0.99945712]
 [-0.93167216]
 [-0.0446538 ]
 [ 0.81493288]]

Operasi yang sama disederhanakan di 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.83266693],
       [ 0.9994572 ],
       [-0.9316722 ],
       [-0.04465387],
       [ 0.81493276]], dtype=float32)>

2. Optimalisasi klasik kuantum hibrid

Sekarang Anda telah mengetahui dasar-dasarnya, mari gunakan TensorFlow Quantum untuk membuat jaringan neural klasik kuantum hybrid . Anda akan melatih jaringan saraf klasik untuk mengontrol satu qubit. Kontrol akan dioptimalkan untuk menyiapkan qubit dengan benar dalam keadaan 0 atau 1 , mengatasi kesalahan kalibrasi sistematis tersimulasi. Gambar ini menunjukkan arsitekturnya:

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

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

  • Rangkaian input atau rangkaian titik data : Tiga gerbang $ R $ pertama.
  • Sirkuit terkontrol : Tiga gerbang $ R $ lainnya.
  • Pengontrol : Jaringan saraf klasik yang mengatur parameter sirkuit yang dikontrol.

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 terkontrol.

Kontroler diinisialisasi secara acak sehingga keluaran ini belum berguna.

controller(tf.constant([[0.0],[1.0]])).numpy()
array([[0.        , 0.        , 0.        ],
       [0.54516   , 0.22012806, 0.09697416]], dtype=float32)

2.3 Hubungkan pengontrol ke sirkuit

Gunakan tfq untuk menghubungkan pengontrol ke sirkuit terkontrol, sebagai satu keras.Model .

Lihat panduan API Fungsional Keras untuk selengkapnya tentang gaya definisi model ini.

Pertama 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 ke input tersebut, untuk menentukan komputasi.

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 paketkan komputasi 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)
.dll

png

Model ini mengambil dua masukan: Perintah untuk pengontrol, dan rangkaian masukan yang keluarannya dicoba diperbaiki pengontrol.

2.4 Dataset

Model tersebut mencoba mengeluarkan nilai pengukuran yang benar $ \ hat {Z} $ untuk setiap perintah. Perintah dan nilai yang benar ditentukan 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 set data pelatihan untuk tugas ini. Setiap titik data dalam dataset juga membutuhkan rangkaian input.

2.4 Definisi rangkaian masukan

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

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 yang ditentukan, Anda dapat menguji tfq model tfq .

model([datapoint_circuits, commands]).numpy()
array([[ 0.00428568],
       [-0.13666134]], dtype=float32)

Sekarang jalankan 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 sistematis.

2.6 Memverifikasi 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
    expectation = z0.expectation_from_wavefunction(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: {expectation}\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: [ 0.6296288  1.1365356 -1.5962224]
Which gives an actual expectation of: 0.9621249437332153

For a desired output (expectation) of [-1.] with noisy preparation, the controller
network found the following values for theta: [1.0543389 1.18372   0.7693251]
Which gives an actual expectation of: -0.9606054425239563


Nilai fungsi kerugian selama pelatihan memberikan gambaran kasar tentang seberapa baik model tersebut dipelajari. Semakin rendah kerugian, semakin dekat nilai ekspektasi pada sel di atas dengan desired_values . Jika Anda tidak terlalu peduli dengan nilai parameter, Anda selalu dapat memeriksa keluaran dari atas menggunakan tfq :

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

3 Belajar mempersiapkan eigenstates dari operator yang berbeda

Pilihan $ \ pm \ hat {Z} $ eigenstates yang sesuai dengan 1 dan 0 berubah-ubah. Anda dapat dengan mudah menginginkan 1 untuk menyesuaikan dengan $ + \ hat {Z} $ eigenstate dan 0 untuk menyesuaikan dengan $ - \ hat {X} $ eigenstate. Salah satu cara untuk melakukannya adalah dengan menentukan operator pengukuran yang berbeda untuk setiap perintah, seperti yang ditunjukkan pada gambar di bawah ini:

Ini membutuhkan penggunaan tfq.layers.Expectation . Sekarang masukan Anda telah berkembang menjadi tiga objek: sirkuit, perintah, dan operator. Outputnya tetaplah 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 jaringan pengontrolnya:

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

Gabungkan sirkuit dan pengontrol menjadi 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 Dataset

Sekarang Anda juga akan menyertakan operator yang ingin Anda ukur untuk setiap titik data yang Anda berikan 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 bisa berlatih 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)
Train on 2 samples
Epoch 1/30
2/2 [==============================] - 1s 311ms/sample - loss: 1.0348
Epoch 2/30
2/2 [==============================] - 0s 1ms/sample - loss: 0.6338
Epoch 3/30
2/2 [==============================] - 0s 1ms/sample - loss: 0.4026
Epoch 4/30
2/2 [==============================] - 0s 1ms/sample - loss: 0.3120
Epoch 5/30
2/2 [==============================] - 0s 1ms/sample - loss: 0.2221
Epoch 6/30
2/2 [==============================] - 0s 1ms/sample - loss: 0.1057
Epoch 7/30
2/2 [==============================] - 0s 1ms/sample - loss: 0.0341
Epoch 8/30
2/2 [==============================] - 0s 1ms/sample - loss: 0.0091
Epoch 9/30
2/2 [==============================] - 0s 1ms/sample - loss: 0.0020
Epoch 10/30
2/2 [==============================] - 0s 2ms/sample - loss: 6.2119e-04
Epoch 11/30
2/2 [==============================] - 0s 2ms/sample - loss: 0.0038
Epoch 12/30
2/2 [==============================] - 0s 1ms/sample - loss: 0.0114
Epoch 13/30
2/2 [==============================] - 0s 1ms/sample - loss: 0.0174
Epoch 14/30
2/2 [==============================] - 0s 1ms/sample - loss: 0.0158
Epoch 15/30
2/2 [==============================] - 0s 1ms/sample - loss: 0.0115
Epoch 16/30
2/2 [==============================] - 0s 1ms/sample - loss: 0.0119
Epoch 17/30
2/2 [==============================] - 0s 1ms/sample - loss: 0.0179
Epoch 18/30
2/2 [==============================] - 0s 1ms/sample - loss: 0.0245
Epoch 19/30
2/2 [==============================] - 0s 1ms/sample - loss: 0.0272
Epoch 20/30
2/2 [==============================] - 0s 1ms/sample - loss: 0.0247
Epoch 21/30
2/2 [==============================] - 0s 1ms/sample - loss: 0.0189
Epoch 22/30
2/2 [==============================] - 0s 1ms/sample - loss: 0.0125
Epoch 23/30
2/2 [==============================] - 0s 1ms/sample - loss: 0.0076
Epoch 24/30
2/2 [==============================] - 0s 1ms/sample - loss: 0.0047
Epoch 25/30
2/2 [==============================] - 0s 1ms/sample - loss: 0.0033
Epoch 26/30
2/2 [==============================] - 0s 1ms/sample - loss: 0.0025
Epoch 27/30
2/2 [==============================] - 0s 1ms/sample - loss: 0.0019
Epoch 28/30
2/2 [==============================] - 0s 1ms/sample - loss: 0.0013
Epoch 29/30
2/2 [==============================] - 0s 1ms/sample - loss: 8.3354e-04
Epoch 30/30
2/2 [==============================] - 0s 1ms/sample - loss: 5.6316e-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 turun ke nol.

controller tersedia sebagai model yang berdiri sendiri. Panggil pengontrol, dan periksa responsnya untuk setiap sinyal perintah. Diperlukan kerja keras untuk membandingkan keluaran ini dengan konten random_rotations .

controller.predict(np.array([0,1]))
array([[ 1.9317125 , -0.03036583, -0.46449944],
       [ 2.1391845 ,  1.4074974 , -0.42245713]], dtype=float32)