안녕, 많은 세계들

TensorFlow.org에서 보기 Google Colab에서 실행 GitHub에서 소스 보기 노트북 다운로드

이 튜토리얼은 기존 신경망이 큐비트 보정 오류를 수정하는 방법을 학습하는 방법을 보여줍니다. NISQ(Noisy Intermediate Scale Quantum) 회로를 생성, 편집 및 호출하는 Python 프레임워크인 Cirq 를 소개하고 Cirq가 TensorFlow Quantum과 인터페이스하는 방법을 보여줍니다.

설정

pip install tensorflow==2.7.0

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
2022-02-04 12:27:31.677071: E tensorflow/stream_executor/cuda/cuda_driver.cc:271] failed call to cuInit: CUDA_ERROR_NO_DEVICE: no CUDA-capable device is detected

1. 기본

1.1 Cirq 및 매개변수화된 양자 회로

TensorFlow Quantum(TFQ)을 살펴보기 전에 Cirq 의 몇 가지 기본 사항을 살펴보겠습니다. Cirq는 Google의 양자 컴퓨팅을 위한 Python 라이브러리입니다. 이를 사용하여 정적 및 매개변수화된 게이트를 포함한 회로를 정의합니다.

Cirq는 SymPy 기호를 사용하여 자유 매개변수를 나타냅니다.

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

다음 코드는 매개변수를 사용하여 2큐비트 회로를 생성합니다.

# 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는 Pauli 연산자 \(\hat{X}\), \(\hat{Y}\)및 \(\hat{Z}\)의 조합을 사용하여 측정값을 지정합니다. 예를 들어 다음 코드는 방금 시뮬레이션한 상태 벡터에서 l10n \(\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 Quantum(TFQ)은 Cirq 객체를 텐서로 변환하는 함수인 tfq.convert_to_tensor 를 제공합니다. 이를 통해 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 객체를 tfq 연산이 필요에 따라 디코딩하는 tf.string 텐서로 인코딩합니다.

# 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++ 코드를 사용하여 회로를 시뮬레이션합니다.

우리 ab 매개변수를 대체할 값 배치를 작성하십시오.

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.66652703]
 [ 0.49764055]
 [ 0.67326665]
 [-0.95549959]
 [-0.81297827]]

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.666526  ],
       [ 0.49764216],
       [ 0.6732664 ],
       [-0.9554999 ],
       [-0.8129788 ]], dtype=float32)>

2. 하이브리드 양자-고전 최적화

이제 기본 사항을 확인했으므로 TensorFlow Quantum을 사용하여 하이브리드 양자-고전 신경망 을 구성해 보겠습니다. 단일 큐비트를 제어하기 위해 고전적인 신경망을 훈련할 것입니다. 제어는 0 또는 1 상태에서 큐비트를 올바르게 준비하도록 최적화되어 시뮬레이션된 시스템 보정 오류를 극복합니다. 이 그림은 아키텍처를 보여줍니다.

신경망이 없어도 이것은 해결하기 쉬운 문제이지만 주제는 TFQ를 사용하여 해결할 수 있는 실제 양자 제어 문제와 유사합니다. tf.keras.Model 내부의 tfq.layers.ControlledPQC (매개변수 양자 회로) 계층을 사용하는 양자 고전 계산의 종단 간 예제를 보여줍니다.

이 튜토리얼의 구현을 위해 이 아키텍처는 세 부분으로 나뉩니다.

  • 입력 회로 또는 데이터 포인트 회로 : 처음 세 개의 \(R\) 게이트.
  • 제어된 회로 : 다른 3개의 \(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.5815686 , 0.21376055, 0.57181627]], dtype=float32)

2.3 컨트롤러를 회로에 연결

tfq 를 사용하여 컨트롤러를 제어되는 회로에 단일 keras.Model 로 연결합니다.

이 스타일의 모델 정의에 대한 자세한 내용은 Keras Functional API 가이드 를 참조하세요.

먼저 모델에 대한 입력을 정의합니다.

# 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

각 데이터 포인트에 대해 하나씩 2개의 회로 복사본이 있습니다.

datapoint_circuits.shape
TensorShape([2])

2.5 훈련

정의된 입력으로 tfq 모델을 테스트할 수 있습니다.

model([datapoint_circuits, commands]).numpy()
array([[0.95853525],
       [0.6272128 ]], 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: [-0.6788422   0.3395225  -0.59394693]
Which gives an actual expectation of: 0.9171845316886902

For a desired output (expectation) of [-1.] with noisy preparation, the controller
network found the following values for theta: [-5.203663   -0.29528576  3.2887425 ]
Which gives an actual expectation of: -0.9511058330535889

훈련 중 손실 함수의 값은 모델이 얼마나 잘 학습하고 있는지에 대한 대략적인 아이디어를 제공합니다. 손실이 낮을수록 위 셀의 기대값이 desired_values 에 더 가깝습니다. 매개변수 값에 관심이 없다면 항상 tfq 를 사용하여 위의 출력을 확인할 수 있습니다.

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

3 다른 연산자의 고유 상태를 준비하는 방법 학습

1과 0에 해당하는 \(\pm \hat{Z}\) 고유 상태의 선택은 임의적이었습니다. 1이 \(+ \hat{Z}\) \(-\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)
])

tfq 를 사용하여 회로와 컨트롤러를 단일 keras.Model 로 결합합니다.

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 320ms/step - loss: 2.4404
Epoch 2/30
1/1 [==============================] - 0s 3ms/step - loss: 1.8713
Epoch 3/30
1/1 [==============================] - 0s 3ms/step - loss: 1.1400
Epoch 4/30
1/1 [==============================] - 0s 3ms/step - loss: 0.5071
Epoch 5/30
1/1 [==============================] - 0s 3ms/step - loss: 0.1611
Epoch 6/30
1/1 [==============================] - 0s 3ms/step - loss: 0.0426
Epoch 7/30
1/1 [==============================] - 0s 3ms/step - loss: 0.0117
Epoch 8/30
1/1 [==============================] - 0s 3ms/step - loss: 0.0032
Epoch 9/30
1/1 [==============================] - 0s 2ms/step - loss: 0.0147
Epoch 10/30
1/1 [==============================] - 0s 3ms/step - loss: 0.0452
Epoch 11/30
1/1 [==============================] - 0s 3ms/step - loss: 0.0670
Epoch 12/30
1/1 [==============================] - 0s 3ms/step - loss: 0.0648
Epoch 13/30
1/1 [==============================] - 0s 3ms/step - loss: 0.0471
Epoch 14/30
1/1 [==============================] - 0s 3ms/step - loss: 0.0289
Epoch 15/30
1/1 [==============================] - 0s 3ms/step - loss: 0.0180
Epoch 16/30
1/1 [==============================] - 0s 3ms/step - loss: 0.0138
Epoch 17/30
1/1 [==============================] - 0s 2ms/step - loss: 0.0130
Epoch 18/30
1/1 [==============================] - 0s 3ms/step - loss: 0.0137
Epoch 19/30
1/1 [==============================] - 0s 3ms/step - loss: 0.0148
Epoch 20/30
1/1 [==============================] - 0s 3ms/step - loss: 0.0156
Epoch 21/30
1/1 [==============================] - 0s 3ms/step - loss: 0.0157
Epoch 22/30
1/1 [==============================] - 0s 3ms/step - loss: 0.0149
Epoch 23/30
1/1 [==============================] - 0s 3ms/step - loss: 0.0135
Epoch 24/30
1/1 [==============================] - 0s 3ms/step - loss: 0.0119
Epoch 25/30
1/1 [==============================] - 0s 3ms/step - loss: 0.0100
Epoch 26/30
1/1 [==============================] - 0s 3ms/step - loss: 0.0082
Epoch 27/30
1/1 [==============================] - 0s 3ms/step - loss: 0.0064
Epoch 28/30
1/1 [==============================] - 0s 3ms/step - loss: 0.0047
Epoch 29/30
1/1 [==============================] - 0s 3ms/step - loss: 0.0034
Epoch 30/30
1/1 [==============================] - 0s 3ms/step - loss: 0.0024
plt.plot(history.history['loss'])
plt.title("Learning to Control a Qubit")
plt.xlabel("Iterations")
plt.ylabel("Error in Control")
plt.show()

png

손실 함수가 0으로 떨어졌습니다.

controller 는 독립형 모델로 제공됩니다. 컨트롤러를 호출하고 각 명령 신호에 대한 응답을 확인합니다. 이러한 출력을 random_rotations 의 내용과 올바르게 비교하려면 약간의 작업이 필요합니다.

controller.predict(np.array([0,1]))
array([[3.6335812 , 1.8470774 , 0.71675825],
       [5.3085413 , 0.08116499, 2.8337662 ]], dtype=float32)