Помогают защитить Большой Барьерный Риф с TensorFlow на Kaggle Присоединяйтесь вызов

Привет, много миров

Посмотреть на TensorFlow.org Запускаем в Google Colab Посмотреть исходный код на GitHub Скачать блокнот

В этом руководстве показано, как классическая нейронная сеть может научиться исправлять ошибки калибровки кубита. Он вводит Cirq , рамки Python для создания, редактирования и вызывать Noisy Intermediate Scale Quantum (NISQ) схемы, и показывает , как интерфейсы Cirq с TensorFlow Quantum.

Настраивать

pip install tensorflow==2.4.1

Установите 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'>

Теперь импортируйте TensorFlow и зависимости модуля:

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. Основы

1.1 Cirq и параметризованные квантовые схемы

Перед исследованием TensorFlow Quantum (TFQ), давайте посмотрим на некотором Cirq основе. Cirq - это библиотека Python для квантовых вычислений от Google. Вы используете его для определения цепей, включая статические и параметризованные вентили.

Cirq использует SymPy символы для представления свободных параметров.

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

Следующий код создает схему из двух кубитов с использованием ваших параметров:

# 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

Для оценки схем, вы можете использовать cirq.Simulator интерфейс. Вы заменяете свободные параметры в цепи с конкретными числами, передавая cirq.ParamResolver объекта. Следующий код вычисляет необработанный выходной вектор состояния параметризованной схемы:

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

Векторы состояния недоступны напрямую вне симуляции (обратите внимание на комплексные числа в выходных данных выше). Чтобы быть физически реалистичным, вы должны указать измерение, которое преобразует вектор состояния в действительное число, понятное классическим компьютерам. Cirq указывает измерения с использованием комбинации операторов Паули \(\hat{X}\), \(\hat{Y}\)и \(\hat{Z}\). В качестве иллюстрации следующие меры код \(\hat{Z}_0\) и \(\frac{1}{2}\hat{Z}_0 + \hat{X}_1\) на вектор состояния просто моделируемой:

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 Квантовые схемы как тензоры

TensorFlow Квантовые (TFQ) обеспечивает tfq.convert_to_tensor , функцию , которая преобразует Cirq объектов в тензоры. Это позволяет передавать объекты Cirq наших квантовых слоев и квантовые опсам . Функцию можно вызывать из списков или массивов Cirq Circuits и 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'>

Это кодирует Cirq объекты , как tf.string тензоры , что tfq операции декодировать по мере необходимости.

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

1.3 Моделирование схемы дозирования

TFQ предоставляет методы для вычисления ожидаемых значений, выборок и векторов состояний. В настоящее время, давайте сосредоточимся на средних значений.

Интерфейс самого высокого уровня для расчета средних значений является tfq.layers.Expectation слоя, который является tf.keras.Layer . В своей простейшей форме, этот слой эквивалентно имитации параметризованных схем над многим cirq.ParamResolvers ; однако TFQ позволяет выполнять пакетную обработку в соответствии с семантикой TensorFlow, а схемы моделируются с использованием эффективного кода C ++.

Создайте партию из значений для замены нашего a и b параметров:

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

Для выполнения схемы дозирования по значениям параметров в Cirq требуется цикл:

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

Та же операция упрощена в 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. Гибридная квантово-классическая оптимизация.

Теперь, когда вы увидели основы, давайте использовать TensorFlow Quantum построить гибридную квантовоклассическую нейронную сеть. Вы научите классическую нейронную сеть управлять одним кубитом. Управления будет оптимизирована , чтобы правильно подготовить кубит в 0 или 1 состояние, преодоление моделируемой систематической ошибки калибровки. На этом рисунке показана архитектура:

Даже без нейронной сети эту проблему легко решить, но ее тема похожа на реальные проблемы квантового управления, которые вы можете решить с помощью TFQ. Это демонстрирует пример из конца в конец квантовоклассических вычислений с использованием tfq.layers.ControlledPQC слоя внутри (параметризованная квантовая цепь) продуктом в виде tf.keras.Model .

Для реализации этого руководства эта архитектура разделена на 3 части:

  • Входная цепь или цепь DataPoint: Первый три \(R\) ворота.
  • Контролируемой цепи: Остальные три \(R\) ворота.
  • Контроллер: классическая настройка параметров управляемой цепи нейронной сети.

2.1 Определение управляемой цепи

Определите обучаемое вращение одного бита, как показано на рисунке выше. Это будет соответствовать нашей управляемой схеме.

# 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 Контроллер

Теперь определите сеть контроллера:

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

Получив пакет команд, контроллер выдает пакет управляющих сигналов для управляемой схемы.

Контроллер инициализируется случайным образом, поэтому эти выходы пока бесполезны.

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

2.3 Подключите контроллер к цепи

Используйте tfq для подключения контроллера к контролируемой цепи, как единый keras.Model .

См Функциональное руководство по API Keras больше об этом стиле определения модели.

Сначала определите входные данные для модели:

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

Затем примените операции к этим входам, чтобы определить вычисление.

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

Теперь пакет этого вычисления как tf.keras.Model :

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

Сетевая архитектура обозначена графиком модели ниже. Сравните этот модельный график со схемой архитектуры, чтобы убедиться в правильности.

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

PNG

Эта модель принимает два входа: команды для контроллера и входную цепь, выход которой контроллер пытается исправить.

2.4 Набор данных

Модельные попытки выдачи правильного правильное значение измерения \(\hat{Z}\) для каждой команды. Команды и правильные значения определены ниже.

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

Это не весь набор обучающих данных для этой задачи. Каждой точке данных в наборе данных также нужна входная цепь.

2.4 Определение входной цепи

Схема ввода ниже определяет случайную ошибку калибровки, которую модель научится исправлять.

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

Есть две копии схемы, по одной для каждой точки данных.

datapoint_circuits.shape
TensorShape([2])

2.5 Обучение

С входы определены вы можете проверить, запустить tfq модель.

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

Теперь запустите стандартный процесс обучения , чтобы настроить эти значения в сторону 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

Из этого графика видно, что нейронная сеть научилась преодолевать систематическую ошибку калибровки.

2.6 Проверка выходов

Теперь используйте обученную модель, чтобы исправить ошибки калибровки кубита. С 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

Значение функции потерь во время обучения дает приблизительное представление о том, насколько хорошо модель обучается. Меньше потери, тем ближе средние значения в ячейке выше , чтобы desired_values . Если вы не так обеспокоены значениями параметров, вы всегда можете проверить выходы из выше с помощью tfq :

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

3 Обучение подготовке собственных состояний различных операторов

Выбор \(\pm \hat{Z}\) собственных состояний , соответствующих 1 и 0 произвольно. Вы могли бы так же легко хотел 1 , чтобы соответствовать \(+ \hat{Z}\) собственного состояния и 0 , чтобы соответствовать \(-\hat{X}\) собственного состояния. Один из способов добиться этого - указать другого оператора измерения для каждой команды, как показано на рисунке ниже:

Это требует использования tfq.layers.Expectation . Теперь ваш ввод увеличился и теперь включает три объекта: схему, команду и оператор. Результатом все еще остается ожидаемое значение.

3.1 Определение новой модели

Давайте посмотрим на модель для выполнения этой задачи:

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

Вот сеть контроллеров:

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

Зерноуборочный цепь и контроллер в одном keras.Model с использованием 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 Набор данных

Теперь вы будете также включать операторы , которые вы хотите измерить для каждой точки данных вы поставляете для 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 Обучение

Теперь, когда у вас есть новые входы и выходы, вы можете снова тренироваться с помощью 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

Функция потерь упала до нуля.

controller доступен в качестве автономной модели. Вызовите контроллер и проверьте его реакцию на каждый командный сигнал. Потребовалось бы какую - то работу , чтобы правильно сравнить эти выходы к содержимому random_rotations .

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