Squeezed States and Quadrature Measurements in Strawberry Fields
Learn to create squeezed, displaced, and rotated states in Strawberry Fields, measure position and momentum quadratures via homodyne detection, and visualize the results with Wigner functions.
Squeezed states are among the most important resources in continuous-variable (CV) quantum computing. By reducing the uncertainty in one quadrature below the vacuum level, squeezed states enable precision measurement, entanglement generation, and quantum error correction in photonic systems. In this tutorial, you will create and manipulate squeezed states using Strawberry Fields, measure their quadratures with homodyne detection, and visualize the resulting quantum states in phase space using Wigner functions.
Prerequisites
Make sure Strawberry Fields is installed along with NumPy and Matplotlib for visualization:
pip install strawberryfields numpy matplotlib
Quadratures and the Heisenberg Uncertainty Principle
In CV quantum computing, each optical mode is described by two conjugate quadrature operators: position and momentum . These satisfy the canonical commutation relation , which gives rise to the Heisenberg uncertainty principle for quadratures:
The vacuum state saturates this bound with equal uncertainty in both quadratures: . Squeezing reduces the uncertainty in one quadrature at the expense of increasing it in the other, while still satisfying the uncertainty relation.
Creating a Squeezed Vacuum State
The squeezing gate Sgate(r, phi) transforms the vacuum into a squeezed state. The parameter r controls the squeezing strength, and phi controls the squeezing angle. When phi=0, position uncertainty is reduced by a factor of while momentum uncertainty grows by .
import strawberryfields as sf
from strawberryfields.ops import Sgate
import numpy as np
# Create a single-mode squeezed vacuum state
prog = sf.Program(1)
with prog.context as q:
Sgate(0.8, 0.0) | q[0] # Squeeze with r=0.8, phi=0 (squeeze in X)
eng = sf.Engine("gaussian")
result = eng.run(prog)
state = result.state
# Check the quadrature variances
# For the Gaussian backend, the covariance matrix encodes all second moments
cov = state.cov()
print(f"Covariance matrix:\n{cov}")
# Extract variances (diagonal elements)
# Strawberry Fields uses the xp-ordering: [x0, x1, ..., p0, p1, ...]
var_x = cov[0, 0] / 2 # Variance of X quadrature
var_p = cov[1, 1] / 2 # Variance of P quadrature
print(f"\nVariance in X: {var_x:.4f}")
print(f"Variance in P: {var_p:.4f}")
print(f"Product ΔX·ΔP: {np.sqrt(var_x * var_p):.4f}")
print(f"Heisenberg bound: 0.5000")
The squeezed vacuum is a minimum uncertainty state, so the product equals exactly . The key point is that one quadrature has been traded for the other.
Comparing Vacuum, Coherent, and Squeezed States
To build intuition, let us compare three fundamental CV states side by side: the vacuum, a coherent state (created by displacement), and a squeezed state.
import strawberryfields as sf
from strawberryfields.ops import Sgate, Dgate
import numpy as np
import matplotlib.pyplot as plt
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
states_info = [
("Vacuum |0⟩", []),
("Coherent |α=1.5⟩", [("Dgate", (1.5, 0.0))]),
("Squeezed S(0.8)|0⟩", [("Sgate", (0.8, 0.0))]),
]
for idx, (title, ops_list) in enumerate(states_info):
prog = sf.Program(1)
with prog.context as q:
for op_name, params in ops_list:
if op_name == "Dgate":
Dgate(*params) | q[0]
elif op_name == "Sgate":
Sgate(*params) | q[0]
eng = sf.Engine("gaussian")
result = eng.run(prog)
state = result.state
# Compute Wigner function over a grid
xvec = np.arange(-4, 4, 0.1)
pvec = np.arange(-4, 4, 0.1)
W = state.wigner(0, xvec, pvec)
X, P = np.meshgrid(xvec, pvec)
axes[idx].contourf(X, P, W, levels=50, cmap="RdBu_r")
axes[idx].set_title(title)
axes[idx].set_xlabel("X quadrature")
axes[idx].set_ylabel("P quadrature")
axes[idx].set_aspect("equal")
plt.tight_layout()
plt.savefig("quadrature_comparison.png", dpi=150)
plt.show()
In this visualization, the vacuum appears as a symmetric circle centered at the origin. The coherent state is the same circle but displaced to . The squeezed state is an ellipse, compressed along the axis and stretched along . All three are Gaussian states with positive Wigner functions everywhere.
Displacement and Rotation of Squeezed States
Combining squeezing with displacement (Dgate) and rotation (Rgate) allows you to position and orient the squeezed ellipse anywhere in phase space. This is the foundation of many CV quantum protocols.
import strawberryfields as sf
from strawberryfields.ops import Sgate, Dgate, Rgate
import numpy as np
import matplotlib.pyplot as plt
prog = sf.Program(1)
with prog.context as q:
Sgate(1.0, 0.0) | q[0] # Squeeze in X
Rgate(np.pi / 4) | q[0] # Rotate by 45 degrees in phase space
Dgate(2.0, np.pi / 6) | q[0] # Displace to (2.0, pi/6) in polar coordinates
eng = sf.Engine("gaussian")
result = eng.run(prog)
state = result.state
# Compute and plot the Wigner function
xvec = np.arange(-5, 5, 0.1)
pvec = np.arange(-5, 5, 0.1)
W = state.wigner(0, xvec, pvec)
X, P = np.meshgrid(xvec, pvec)
plt.figure(figsize=(7, 6))
plt.contourf(X, P, W, levels=50, cmap="RdBu_r")
plt.colorbar(label="W(x, p)")
plt.title("Displaced and Rotated Squeezed State")
plt.xlabel("X quadrature")
plt.ylabel("P quadrature")
plt.axis("equal")
plt.tight_layout()
plt.savefig("displaced_rotated_squeezed.png", dpi=150)
plt.show()
# Print state properties
means = state.means()
print(f"Mean X: {means[0]:.4f}")
print(f"Mean P: {means[1]:.4f}")
print(f"Mean photon number: {state.mean_photon(0)[0]:.4f}")
The Rgate(theta) rotates the squeezing ellipse by angle theta in phase space. After rotation, the reduced uncertainty axis is no longer aligned with or but lies along a rotated direction. This is useful for protocols that require squeezing along a specific measurement basis.
Homodyne Detection: Measuring Quadratures
Homodyne detection measures a single quadrature of the optical mode. In Strawberry Fields, MeasureHomodyne(phi) measures the quadrature at angle phi, where phi=0 corresponds to the quadrature and phi=pi/2 corresponds to . The shorthand operators MeasureX and MeasureP measure and directly.
import strawberryfields as sf
from strawberryfields.ops import Sgate, Dgate, MeasureHomodyne
import numpy as np
# Measure X quadrature of a displaced squeezed state
prog_x = sf.Program(1)
with prog_x.context as q:
Sgate(1.0, 0.0) | q[0]
Dgate(2.0, 0.0) | q[0]
MeasureHomodyne(0) | q[0] # Measure X quadrature (phi=0)
eng = sf.Engine("gaussian")
result_x = eng.run(prog_x, shots=1000)
x_samples = result_x.samples[:, 0]
print(f"X quadrature: mean = {np.mean(x_samples):.4f}, std = {np.std(x_samples):.4f}")
# Measure P quadrature of the same state
prog_p = sf.Program(1)
with prog_p.context as q:
Sgate(1.0, 0.0) | q[0]
Dgate(2.0, 0.0) | q[0]
MeasureHomodyne(np.pi / 2) | q[0] # Measure P quadrature (phi=pi/2)
eng_p = sf.Engine("gaussian")
result_p = eng_p.run(prog_p, shots=1000)
p_samples = result_p.samples[:, 0]
print(f"P quadrature: mean = {np.mean(p_samples):.4f}, std = {np.std(p_samples):.4f}")
print(f"\nExpected: X mean ~ 2*sqrt(2*hbar) due to displacement")
print(f"Expected: X std < P std due to squeezing in X")
The key observation is that the standard deviation of the measurements is significantly smaller than the standard deviation of the measurements. This is the direct signature of squeezing: we have reduced noise in at the cost of increased noise in .
Visualizing the Wigner Function in Detail
The Wigner function is a quasi-probability distribution in phase space. For Gaussian states, it is always non-negative and takes the shape of a 2D Gaussian. The contour plot of the Wigner function directly reveals the state’s mean position, orientation, and squeezing parameters.
import strawberryfields as sf
from strawberryfields.ops import Sgate, Dgate, Rgate
import numpy as np
import matplotlib.pyplot as plt
# Create four states to compare Wigner functions
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
configs = [
("Vacuum", [], (0, 0)),
("Squeezed r=0.5", [("S", 0.5, 0.0)], (0, 1)),
("Squeezed r=1.0", [("S", 1.0, 0.0)], (1, 0)),
("Squeezed r=1.5", [("S", 1.5, 0.0)], (1, 1)),
]
xvec = np.arange(-5, 5, 0.05)
pvec = np.arange(-5, 5, 0.05)
for title, ops, (row, col) in configs:
prog = sf.Program(1)
with prog.context as q:
for op in ops:
if op[0] == "S":
Sgate(op[1], op[2]) | q[0]
eng = sf.Engine("gaussian")
result = eng.run(prog)
state = result.state
W = state.wigner(0, xvec, pvec)
X, P = np.meshgrid(xvec, pvec)
ax = axes[row][col]
im = ax.contourf(X, P, W, levels=50, cmap="RdBu_r")
ax.set_title(title)
ax.set_xlabel("X")
ax.set_ylabel("P")
ax.set_aspect("equal")
plt.colorbar(im, ax=ax)
plt.suptitle("Wigner Functions: Increasing Squeezing", fontsize=14)
plt.tight_layout()
plt.savefig("wigner_squeezing_comparison.png", dpi=150)
plt.show()
As the squeezing parameter increases, the ellipse becomes progressively narrower in and wider in . At (about 13 dB of squeezing), the position uncertainty is reduced to times the vacuum level.
Squeezing in Decibels
Experimentally, squeezing is often reported in decibels. The conversion is:
Current state-of-the-art experiments achieve around 15 dB of squeezing (corresponding to ). Xanadu’s photonic processors typically operate with a few dB of squeezing per mode.
import numpy as np
# Convert between squeezing parameter r and dB
r_values = [0.1, 0.5, 1.0, 1.5, 2.0]
for r in r_values:
db = 20 * r * np.log10(np.e)
print(f"r = {r:.1f} → {db:.1f} dB squeezing")
Summary and Next Steps
In this tutorial, you learned how to create squeezed states using the Sgate operator, position them in phase space with Dgate and Rgate, and measure their quadratures using homodyne detection. The Wigner function visualization provides a powerful tool for understanding the shape and orientation of CV quantum states.
Squeezed states are foundational to many CV quantum protocols. Two-mode squeezing generates entanglement (EPR states), which is essential for CV quantum teleportation and cluster state computation. In measurement-based quantum computing, large entangled cluster states built from squeezed modes provide the substrate for universal CV quantum computation.
For further exploration, consider investigating two-mode squeezed states and their entanglement properties, or look into the sf.apps module for practical applications of squeezing in Gaussian boson sampling.
Was this tutorial helpful?