יש שאלה? התחבר לקהילה בפורום הביקור של TensorFlow

שלום רב, עולמות רבים

צפה ב- TensorFlow.org הפעל בגוגל קולאב צפה במקור ב- GitHub הורד מחברת

מדריך זה מראה כיצד רשת עצבית קלאסית יכולה ללמוד לתקן שגיאות כיול בקוביט. הוא מציג את Cirq , מסגרת פייתון ליצירה, עריכה והפעלת מעגלי קוונטום בינוניים רועשים (NISQ) ומדגים כיצד Cirq מתממשק עם TensorFlow Quantum.

להכין

pip install -q tensorflow==2.4.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 מעגלים קוונטיים ופרמטרים

לפני שנבדוק את TensorFlow Quantum (TFQ), בואו נסתכל על כמה יסודות Cirq . Cirq היא ספריית פייתון למחשוב קוונטי של גוגל. אתה משתמש בו כדי להגדיר מעגלים, כולל שערים סטטיים ופרמטרים.

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 Quantum (TFQ) מספק tfq.convert_to_tensor , פונקציה הממירה אובייקטים של Cirq tfq.convert_to_tensor . זה מאפשר לך לשלוח אובייקטים של Cirq לשכבות הקוואנטיות שלנו ול-ops הקוואנטים שלנו . ניתן לקרוא לפונקציה ברשימות או מערכים של מעגלי Cirq ו- 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'>

זה מקודד את האובייקטים tf.string כ- tf.string tfq שפעולות ה- 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 פני הרבה 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.56709254]
 [ 0.06688178]
 [-0.95769906]
 [-0.46601528]
 [-0.98502201]]

אותה פעולה מפשטת ב- 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.56709236],
       [ 0.06688362],
       [-0.9576994 ],
       [-0.46601394],
       [-0.98502195]], dtype=float32)>

2. אופטימיזציה קוונטית קלאסית היברידית

כעת, לאחר שראיתם את היסודות, בואו נשתמש ב- TensorFlow Quantum לבניית רשת עצבית קוונטית-קלאסית היברידית . תוכלו לאמן רשת עצבית קלאסית לשליטה בקוביט יחיד. השליטה תהיה מותאמת להכנה נכונה של הקוביט במצב 0 או 1 , תוך התגברות על שגיאת כיול שיטתית מדומה. איור זה מראה את הארכיטקטורה:

גם ללא רשת עצבית זוהי בעיה פשוטה לפתרון, אך הנושא דומה לבעיות בקרת הקוונטים האמיתיות שעשויות לפתור באמצעות TFQ. זה מדגים דוגמא מקצה לקצה של חישוב קוונטי-קלאסי באמצעות tfq.layers.ControlledPQC (Parametrized Quantum Circuit) בתוך שכבת tf.keras.Model .

לצורך הטמעת הדרכה זו, ארכיטקטורה זו מחולקת לשלושה חלקים:

  • מעגל הקלט או מעגל נקודת הנתונים: שלושת שערים $ 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.4901766,  0.9337233,  1.1685666]], 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 החישוב הזה כ- 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.13463798],
       [0.01099351]], 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.01647221 -1.1232504   0.67117506]
Which gives an actual expectation of: 0.9762156009674072

For a desired output (expectation) of [-1.] with noisy preparation, the controller
network found the following values for theta: [-2.6371388  1.4017701  1.8121954]
Which gives an actual expectation of: -0.9950709342956543

הערך של פונקציית האובדן במהלך האימון מספק מושג גס לגבי מידת הלמידה של המודל. ככל שההפסד נמוך יותר, כך ערכי הציפייה בתא הנ"ל קרובים יותר לערכים desired_values . אם אתה לא מתעניין בערכי הפרמטר, אתה תמיד יכול לבדוק את הפלטים מלמעלה באמצעות tfq :

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

3 לימוד הכנת מצבים עצמיים של אופרטורים שונים

הבחירה ב- $ \ pm \ hat {Z} $ העצמאים המקבילים ל- 1 ו- 0 הייתה שרירותית. היית יכול באותה קלות לרצות ש -1 יתאים ל $ + \ hat {Z} $ eigenstate ו- 0 כדי להתאים ל- $ - \ hat {X} $ eigen state. אחת הדרכים להשיג זאת היא על ידי ציון מפעיל מדידה שונה עבור כל פקודה, כפי שמצוין באיור להלן:

זה דורש שימוש ב- 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 אחת. 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 347ms/step - loss: 0.8254
Epoch 2/30
1/1 [==============================] - 0s 3ms/step - loss: 0.2200
Epoch 3/30
1/1 [==============================] - 0s 2ms/step - loss: 0.0764
Epoch 4/30
1/1 [==============================] - 0s 2ms/step - loss: 0.0471
Epoch 5/30
1/1 [==============================] - 0s 3ms/step - loss: 0.0272
Epoch 6/30
1/1 [==============================] - 0s 2ms/step - loss: 0.0128
Epoch 7/30
1/1 [==============================] - 0s 2ms/step - loss: 0.0043
Epoch 8/30
1/1 [==============================] - 0s 2ms/step - loss: 9.2659e-04
Epoch 9/30
1/1 [==============================] - 0s 2ms/step - loss: 1.8865e-04
Epoch 10/30
1/1 [==============================] - 0s 2ms/step - loss: 5.8759e-04
Epoch 11/30
1/1 [==============================] - 0s 2ms/step - loss: 0.0023
Epoch 12/30
1/1 [==============================] - 0s 2ms/step - loss: 0.0059
Epoch 13/30
1/1 [==============================] - 0s 2ms/step - loss: 0.0110
Epoch 14/30
1/1 [==============================] - 0s 2ms/step - loss: 0.0151
Epoch 15/30
1/1 [==============================] - 0s 2ms/step - loss: 0.0158
Epoch 16/30
1/1 [==============================] - 0s 2ms/step - loss: 0.0130
Epoch 17/30
1/1 [==============================] - 0s 2ms/step - loss: 0.0090
Epoch 18/30
1/1 [==============================] - 0s 2ms/step - loss: 0.0055
Epoch 19/30
1/1 [==============================] - 0s 2ms/step - loss: 0.0029
Epoch 20/30
1/1 [==============================] - 0s 2ms/step - loss: 0.0014
Epoch 21/30
1/1 [==============================] - 0s 2ms/step - loss: 7.8246e-04
Epoch 22/30
1/1 [==============================] - 0s 3ms/step - loss: 0.0011
Epoch 23/30
1/1 [==============================] - 0s 3ms/step - loss: 0.0021
Epoch 24/30
1/1 [==============================] - 0s 2ms/step - loss: 0.0030
Epoch 25/30
1/1 [==============================] - 0s 3ms/step - loss: 0.0029
Epoch 26/30
1/1 [==============================] - 0s 2ms/step - loss: 0.0021
Epoch 27/30
1/1 [==============================] - 0s 2ms/step - loss: 0.0010
Epoch 28/30
1/1 [==============================] - 0s 2ms/step - loss: 3.8834e-04
Epoch 29/30
1/1 [==============================] - 0s 2ms/step - loss: 1.0240e-04
Epoch 30/30
1/1 [==============================] - 0s 2ms/step - loss: 1.7314e-05
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([[-0.86519706,  0.12104976, -0.7411108 ],
       [-0.21088992, -1.4411982 , -2.5981088 ]], dtype=float32)