TensorFlow Quantum
Google's framework for hybrid quantum-classical machine learning with Keras integration
Quick install
pip install tensorflow-quantum Background and History
TensorFlow Quantum (TFQ) was developed by a collaboration between Google Quantum AI and the University of Waterloo, with the project publicly announced and released in March 2020. The lead authors of the accompanying paper included Michael Broughton, Guillaume Verdon, Trevor McCourt, and other members of Google’s quantum computing and TensorFlow teams. TFQ was created to provide a native integration between quantum circuits and TensorFlow’s machine learning infrastructure, enabling researchers to train quantum models using the same Keras workflows and optimizers used for classical deep learning.
The timing of TFQ’s release coincided with a period of intense interest in quantum machine learning (QML). Researchers were exploring whether parameterized quantum circuits could serve as trainable models with advantages over classical neural networks for certain tasks. TFQ made this experimentation accessible by representing Cirq circuits as TensorFlow tensor types and computing gradients through the parameter-shift rule, all within the standard model.compile() and model.fit() Keras training loop.
TFQ reached version 0.7 and remained compatible with TensorFlow 2.11 and specific Cirq versions. However, development activity slowed notably after 2022. The project’s coupling to specific TensorFlow and Cirq version combinations made installation increasingly fragile as both dependencies evolved. Meanwhile, PennyLane, which launched at a similar time with a similar QML focus, gained broader adoption by supporting multiple classical ML frameworks (PyTorch, JAX, and TensorFlow) and multiple quantum backends (not just Cirq).
As of 2025, TFQ’s GitHub repository receives limited maintenance updates. The library remains functional for its supported version combinations and continues to be referenced in quantum machine learning literature. However, new QML projects more commonly choose PennyLane or build custom integrations with JAX and Cirq directly. TFQ’s legacy is significant: it helped establish quantum machine learning as a concrete research direction and demonstrated how quantum circuits could integrate with production ML infrastructure. For teams already committed to the TensorFlow/Cirq stack, it remains a viable tool, but prospective users should evaluate its maintenance status before adopting it for new projects.
Overview
TensorFlow Quantum sits at the intersection of two Google projects: Cirq (quantum circuit construction) and TensorFlow/Keras (classical machine learning). In TFQ, a parameterized quantum circuit becomes a layer in a Keras model. Gradients flow through the quantum circuit using the parameter-shift rule, which enables end-to-end training with standard optimizers such as Adam or SGD.
The primary use cases are quantum neural networks (QNNs), quantum kernel methods, and hybrid models that combine classical preprocessing with a quantum layer.
Installation
pip install tensorflow tensorflow-quantum
TFQ requires a compatible TensorFlow version. As of TFQ 0.7, TensorFlow 2.11 is supported. Check the compatibility table before installing.
Key Imports
import tensorflow as tf
import tensorflow_quantum as tfq
import cirq
import sympy
Core Concepts
Parameterized Circuits with Sympy
Gates in Cirq accept sympy.Symbol objects as parameters. TFQ treats these symbols as trainable variables.
import cirq
import sympy
qubit = cirq.GridQubit(0, 0)
theta = sympy.Symbol('theta')
phi = sympy.Symbol('phi')
# Parameterized single-qubit rotations
circuit = cirq.Circuit([
cirq.ry(theta)(qubit),
cirq.rz(phi)(qubit),
])
print(circuit)
# 0: ---Ry(theta)---Rz(phi)---
Converting Circuits to Tensors
TFQ represents circuits as serialized string tensors. Use tfq.convert_to_tensor to convert Cirq circuits for use as model inputs.
import tensorflow_quantum as tfq
circuit_tensor = tfq.convert_to_tensor([circuit])
print(circuit_tensor.shape) # (1,) - one circuit
PQC Layer
tfq.layers.PQC wraps a parameterized circuit and an observable into a Keras layer. It computes expectation values and supports backpropagation.
import tensorflow as tf
import tensorflow_quantum as tfq
import cirq
import sympy
qubit = cirq.GridQubit(0, 0)
theta = sympy.Symbol('theta')
circuit = cirq.Circuit(cirq.ry(theta)(qubit))
readout = cirq.Z(qubit) # measure expectation of Pauli-Z
model = tf.keras.Sequential([
tf.keras.layers.Input(shape=(), dtype=tf.string),
tfq.layers.PQC(circuit, readout),
])
# Input: a batch of circuit tensors
circuit_tensor = tfq.convert_to_tensor([circuit])
output = model(circuit_tensor)
print(output) # [[expectation_value]] - shape (1, 1)
Expectation Layer
tfq.layers.Expectation is lower-level than PQC. It takes circuits and symbol values separately, useful when input circuits vary per sample or when you want explicit control over symbol injection.
Training a Quantum Model
import tensorflow as tf
import tensorflow_quantum as tfq
import cirq
import sympy
import numpy as np
qubit = cirq.GridQubit(0, 0)
theta = sympy.Symbol('theta')
# Parameterized circuit
circuit = cirq.Circuit(cirq.ry(theta)(qubit))
readout = cirq.Z(qubit)
# Keras model with PQC layer
model = tf.keras.Sequential([
tf.keras.layers.Input(shape=(), dtype=tf.string),
tfq.layers.PQC(circuit, readout),
])
model.compile(
optimizer=tf.keras.optimizers.Adam(learning_rate=0.1),
loss=tf.keras.losses.MeanSquaredError(),
)
# Training data: same circuit input, target expectation value of -1.0
x_train = tfq.convert_to_tensor([circuit] * 20)
y_train = np.full((20, 1), -1.0, dtype=np.float32)
history = model.fit(x_train, y_train, epochs=30, verbose=0)
print(f"Final loss: {history.history['loss'][-1]:.4f}")
# theta trains toward π, where <Z> = -1
Differentiators
Differentiators control how gradients are computed through the quantum circuit.
| Differentiator | Description |
|---|---|
ParameterShift | Exact gradient; two circuit evaluations per parameter |
LinearCombination | Approximation; faster for many parameters |
Adjoint | Simulator-only; efficient for statevector backends |
# Use parameter-shift explicitly
differentiator = tfq.differentiators.ParameterShift()
pqc_layer = tfq.layers.PQC(circuit, readout, differentiator=differentiator)
Observables
Any Cirq PauliSum works as an observable.
# Single Pauli
obs_z = cirq.Z(qubit)
# Multi-qubit observable
q0, q1 = cirq.GridQubit(0, 0), cirq.GridQubit(0, 1)
obs_zz = cirq.Z(q0) * cirq.Z(q1) # ZZ correlation
# Sum of Paulis
obs_sum = cirq.X(q0) + 0.5 * cirq.Z(q1)
Relationship to Cirq
TFQ uses Cirq for all circuit construction. You must build circuits in Cirq before passing them to TFQ. Knowing Cirq’s gate notation, qubit types (GridQubit, LineQubit), and circuit structure is a prerequisite. TFQ does not support Qiskit or other circuit formats.
TFQ vs PennyLane for QML
| Aspect | TFQ | PennyLane |
|---|---|---|
| Backend | Cirq only | Qiskit, Cirq, Braket, and others |
| Classical ML integration | TensorFlow/Keras native | PyTorch, TF, JAX, NumPy |
| Maintenance activity | Lower (as of 2024) | More active |
| Community resources | Moderate | Larger |
| Best fit | Already in TF ecosystem | Framework-agnostic QML |
Limitations
TFQ requires a specific TensorFlow version and the installation can be fragile across Python environments. Development has slowed relative to PennyLane, which has a larger active community and broader hardware backend support. TFQ only works with Cirq circuits, so any existing Qiskit or Braket code must be rewritten. Simulation speed is limited compared to native Cirq for classical tasks, since TFQ adds serialization overhead.