![]() | ![]() | ![]() | ![]() |
이 튜토리얼은 기존 신경망이 큐 비트 교정 오류를 수정하는 방법을 학습하는 방법을 보여줍니다. NISQ (Noisy Intermediate Scale Quantum) 회로를 생성, 편집 및 호출하는 Python 프레임 워크 인 Cirq를 소개하고 Cirq 가 TensorFlow Quantum과 인터페이스하는 방법을 보여줍니다.
설정
pip install -q tensorflow==2.3.1
TensorFlow Quantum 설치 :
pip install -q tensorflow-quantum
이제 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
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.
회로를 평가하기 위해 cirq.Simulator
인터페이스를 사용할 수 있습니다. cirq.ParamResolver
객체를 전달하여 회로의 자유 매개 변수를 특정 숫자로 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} $의 조합을 사용하여 측정을 지정합니다. 예시로, 다음 코드는 방금 시뮬레이션 한 상태 벡터에서 $ \ 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는 다음과 같이 객체 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
A는 층 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.97633868] [ 0.68726736] [-0.67144978] [-0.92119527] [ 0.41925651]]
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.9763384 ], [ 0.6872687 ], [-0.67144877], [-0.9211948 ], [ 0.41925824]], dtype=float32)>
2. 하이브리드 양자 고전적 최적화
이제 기본 사항을 확인 했으므로 TensorFlow Quantum을 사용하여 하이브리드 양자 클래식 신경망 을 구성 해 보겠습니다. 단일 큐 비트를 제어하기 위해 고전적인 신경망을 훈련합니다. 제어는 0
또는 1
상태에서 큐 비트를 올바르게 준비하도록 최적화되어 시뮬레이션 된 체계적 교정 오류를 극복합니다. 이 그림은 아키텍처를 보여줍니다.
신경망이 없어도 해결하기 쉬운 문제이지만, 주제는 TFQ를 사용하여 해결할 수있는 실제 양자 제어 문제와 유사합니다. 그것은 사용 양자 고전 연산 종단 예를 보여 tfq.layers.ControlledPQC
(A)의 (매개 변수화 양자 회로) 층 내부 tf.keras.Model
.
이 튜토리얼의 구현을 위해 이것은 아키텍처가 세 부분으로 나뉩니다.
- 입력 회로 또는 데이터 포인트 회로 : 처음 세 개의 $ 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)
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.08031327, 0.03002123, 0.0143298 ]], 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)
이 모델은 두 가지 입력을받습니다. 컨트롤러에 대한 명령과 컨트롤러가 수정하려는 출력의 입력 회로입니다.
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.14540803], [-0.16774747]], 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()
이 플롯에서 신경망이 체계적인 오 보정을 극복하는 방법을 배웠 음을 알 수 있습니다.
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: [-1.8619336 -1.523817 -3.2972674] Which gives an actual expectation of: 0.9911799430847168 For a desired output (expectation) of [-1.] with noisy preparation, the controller network found the following values for theta: [1.0268601 1.7161715 0.44205332] Which gives an actual expectation of: -0.9540284872055054
학습 중 손실 함수의 값은 모델이 얼마나 잘 학습하고 있는지 대략적인 아이디어를 제공합니다. 손실이 낮을수록 위 셀의 기대 값이 desired_values
에 가까워집니다. 매개 변수 값에 관심이 tfq
사용하여 항상 위의 출력을 확인할 수 있습니다.
model([datapoint_circuits, commands])
<tf.Tensor: shape=(2, 1), dtype=float32, numpy= array([[ 0.9911797], [-0.9540284]], dtype=float32)>
3 다른 연산자의 고유 상태를 준비하는 방법 배우기
1과 0에 해당하는 $ \ pm \ hat {Z} $ 고유 상태의 선택은 임의적이었습니다. 1이 $ + \ hat {Z} $ 고유 상태에 해당하고 0이 $-\ hat {X} $ 고유 상태에 해당하는 것처럼 쉽게 원할 수 있습니다. 이를 수행하는 한 가지 방법은 아래 그림에 표시된대로 각 명령에 대해 다른 측정 연산자를 지정하는 것입니다.
이를 위해서는 tfq.layers.Expectation
을 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 1ms/step - loss: 1.7510 Epoch 2/30 1/1 [==============================] - 0s 759us/step - loss: 0.8703 Epoch 3/30 1/1 [==============================] - 0s 1ms/step - loss: 0.3938 Epoch 4/30 1/1 [==============================] - 0s 749us/step - loss: 0.1695 Epoch 5/30 1/1 [==============================] - 0s 722us/step - loss: 0.0820 Epoch 6/30 1/1 [==============================] - 0s 685us/step - loss: 0.0609 Epoch 7/30 1/1 [==============================] - 0s 676us/step - loss: 0.0704 Epoch 8/30 1/1 [==============================] - 0s 734us/step - loss: 0.0826 Epoch 9/30 1/1 [==============================] - 0s 732us/step - loss: 0.0723 Epoch 10/30 1/1 [==============================] - 0s 907us/step - loss: 0.0431 Epoch 11/30 1/1 [==============================] - 0s 731us/step - loss: 0.0178 Epoch 12/30 1/1 [==============================] - 0s 859us/step - loss: 0.0064 Epoch 13/30 1/1 [==============================] - 0s 763us/step - loss: 0.0035 Epoch 14/30 1/1 [==============================] - 0s 684us/step - loss: 0.0031 Epoch 15/30 1/1 [==============================] - 0s 710us/step - loss: 0.0030 Epoch 16/30 1/1 [==============================] - 0s 671us/step - loss: 0.0038 Epoch 17/30 1/1 [==============================] - 0s 716us/step - loss: 0.0062 Epoch 18/30 1/1 [==============================] - 0s 707us/step - loss: 0.0095 Epoch 19/30 1/1 [==============================] - 0s 700us/step - loss: 0.0120 Epoch 20/30 1/1 [==============================] - 0s 703us/step - loss: 0.0117 Epoch 21/30 1/1 [==============================] - 0s 697us/step - loss: 0.0089 Epoch 22/30 1/1 [==============================] - 0s 717us/step - loss: 0.0053 Epoch 23/30 1/1 [==============================] - 0s 739us/step - loss: 0.0025 Epoch 24/30 1/1 [==============================] - 0s 725us/step - loss: 9.8351e-04 Epoch 25/30 1/1 [==============================] - 0s 709us/step - loss: 3.7235e-04 Epoch 26/30 1/1 [==============================] - 0s 660us/step - loss: 3.4534e-04 Epoch 27/30 1/1 [==============================] - 0s 744us/step - loss: 6.7898e-04 Epoch 28/30 1/1 [==============================] - 0s 740us/step - loss: 0.0013 Epoch 29/30 1/1 [==============================] - 0s 770us/step - loss: 0.0020 Epoch 30/30 1/1 [==============================] - 0s 756us/step - loss: 0.0028
plt.plot(history.history['loss'])
plt.title("Learning to Control a Qubit")
plt.xlabel("Iterations")
plt.ylabel("Error in Control")
plt.show()
손실 함수가 0으로 떨어졌습니다.
controller
는 독립형 모델로 제공됩니다. 컨트롤러를 호출하고 각 명령 신호에 대한 응답을 확인하십시오. 이러한 출력을 random_rotations
의 내용과 올바르게 비교하려면 약간의 작업이 필요합니다.
controller.predict(np.array([0,1]))
array([[ 1.741399 , -0.22516271, 0.7868666 ], [-0.31710118, 1.6385193 , 1.8622308 ]], dtype=float32)