Note

Click here to download the full example code

# Squeezer characterization¶

In this tutorial we collect photon-number event samples from the `X8_01`

chip in an effort to
characterize its 4 non-degenerate squeezers A similar task is automatically performed each morning to
record the stability of our hardware. We begin with the necessary imports.

From hereon, we refer to one wavelength mode of the two mode squeezed vacuum states produced by our squeezers as the signal, and the other wavelength mode the idler, in keeping with standard terminology.

```
import numpy as np
import strawberryfields as sf
```

## Collecting the hardware samples¶

To start with, let’s define a general function to execute a job on the `X8_01`

chip
that returns information needed to characterize the squeezers.

The function `run_job`

accepts a unitary and set of
squeezing amplitudes for the 4 squeezers (presently limited to 0 or 1),
and requested number of samples. It first creates a Strawberry Fields
program implementing the supplied unitary and squeezing amplitudes, and
subsequently runs it on `X8_01`

, returning a `results`

object that we can use to view
the resulting samples.

We write the function somewhat generally, even
though here we will always run it on `X8_01`

, a chip with 4 squeezers and
4 spatial modes.

```
def run_job(
job_name="default_name",
n_spatial_modes=4,
unitary=None,
squeezing_amplitudes=None,
n_samples=500000,
):
prog = sf.Program(n_spatial_modes * 2, name=job_name)
# If no unitary is provided, default to the identity
if unitary is None:
unitary = np.identity(n_spatial_modes)
# If no squeezing amplitudes are provided, default to all of them being off
if squeezing_amplitudes is None:
squeezing_amplitudes = [0] * n_spatial_modes
with prog.context as q:
for i in range(n_spatial_modes):
sf.ops.S2gate(squeezing_amplitudes[i]) | (q[i], q[i + n_spatial_modes])
for qumodes in (q[:n_spatial_modes], q[n_spatial_modes:]):
sf.ops.Interferometer(unitary) | qumodes
sf.ops.MeasureFock() | q
eng = sf.RemoteEngine("X8_01")
return eng.run(prog, shots=n_samples, disable_port_permutation=True)
```

With this function defined, it is easy to collect samples that will allow us to characterize each squeezer.

```
squeezer0 = run_job(job_name="squeezer0_char", squeezing_amplitudes=[1, 0, 0, 0])
```

## Analysis¶

The samples span 8 (4 spatial x 2 frequency) detector channels, and we can easily calculate the mean photon number of each.

```
n_means = np.mean(squeezer0.samples, axis=0)
print(f"The mean photon numbers <n> of each channel are:\n{n_means}")
```

Out:

```
The mean photon numbers <n> of each channel are:
[0.30781 0.01342 0.018746 0.022222 0.311482 0.02154 0.01833 0.018452].
```

From which we see that the signal and idler arms of squeezer 0 have been directed to the detectors in channel 0 and channel 4, as expected when the implemented unitary is the identity. More relevant to the present example, we can use such pairs of channels to determine that we have indeed produced quantum light, using a quantity known as the quantum noise reduction factor or NRF. Each squeezer generates an entangled mode pair, and we expect the photon numbers of each half of this pair to be correlated. As detailed in [1] and references therein, we can measure this as the variance of the photon number difference between signal and idler channels of a squeezer being suppressed below what would be expected for uncorrelated modes with Poisson photon statistics or, more precisely, \(\text{NRF}=V_{n_{S} - n_{I}}/ \langle n_{S} + n_{I} \rangle\), and is \(\geq 1\) for classical states.

```
def NRF(sig, idl):
return np.var(sig - idl) / np.mean(sig + idl)
NRF_sq0 = NRF(squeezer0.samples[:, 0], squeezer0.samples[:, 4])
print(f"The NRF of squeezer 0 is {NRF_sq0:.2f}.")
```

Out:

```
The NRF of squeezer 0 is 0.86.
```

The fact that it is less than 1 indicates that squeezer 0 has produced non-classical light.

An additional squeezer property of interest is the temporal mode structure of the generated light. Ideally, each squeezer should generate squeezing in a single temporal mode, as any multi-modedness can effectively act as a source of unwanted noise in various applications. As detailed in [2], we can measure such multi-modedness via a second-order correlation, or \(g^{(2)}\), measurement, via \(g^{(2)} = 1 + 1 / K\) where \(K\) is a quantity known as the Schmidt number. The Schmidt number counts the effective number of modes, and is therefore 1 if there is a single temporal mode. Thus, the nearer to 2 one measures \(g^{(2)}\) to be, the nearer to a single temporal mode the squeezer is operating.

```
def g2(samples):
return (np.mean(samples ** 2) - np.mean(samples)) / np.mean(samples) ** 2
print(f"The g2 of the signal arm of squeezer 0 is {g2(squeezer0.samples[:, 0]):.2f}.")
print(f"The g2 of the idler arm of squeezer 0 is {g2(squeezer0.samples[:, 4]):.2f}.")
```

Out:

```
The g2 of the signal arm of squeezer 0 is 1.79.
The g2 of the idler arm of squeezer 0 is 1.78.
```

We note that similar data can be taken for the 3 other squeezers.

```
squeezer1 = run_job(job_name="squeezer1_char", squeezing_amplitudes=[0, 1, 0, 0])
squeezer2 = run_job(job_name="squeezer2_char", squeezing_amplitudes=[0, 0, 1, 0])
squeezer3 = run_job(job_name="squeezer3_char", squeezing_amplitudes=[0, 0, 0, 1])
print(f"The NRF of squeezer 1 is {NRF(squeezer1.samples[:, 1], squeezer1.samples[:, 5]):.2f}.")
print(f"The NRF of squeezer 2 is {NRF(squeezer2.samples[:, 2], squeezer2.samples[:, 6]):.2f}.")
print(f"The NRF of squeezer 3 is {NRF(squeezer3.samples[:, 3], squeezer3.samples[:, 7]):.2f}.")
```

Out:

```
The NRF of squeezer 1 is 0.89.
The NRF of squeezer 2 is 0.85.
The NRF of squeezer 3 is 0.87.
```

```
print(f"The g2 of the signal arm of squeezer 1 is {g2(squeezer1.samples[:, 1]):.2f}.")
print(f"The g2 of the idler arm of squeezer 1 is {g2(squeezer1.samples[:, 5]):.2f}.")
print(f"The g2 of the signal arm of squeezer 2 is {g2(squeezer2.samples[:, 2]):.2f}.")
print(f"The g2 of the idler arm of squeezer 2 is {g2(squeezer2.samples[:, 6]):.2f}.")
print(f"The g2 of the signal arm of squeezer 3 is {g2(squeezer3.samples[:, 3]):.2f}.")
print(f"The g2 of the idler arm of squeezer 3 is {g2(squeezer3.samples[:, 7]):.2f}.")
```

Out:

```
The g2 of the signal arm of squeezer 1 is 1.84.
The g2 of the idler arm of squeezer 1 is 1.81.
The g2 of the signal arm of squeezer 2 is 1.83.
The g2 of the idler arm of squeezer 2 is 1.83.
The g2 of the signal arm of squeezer 3 is 1.68.
The g2 of the idler arm of squeezer 3 is 1.73.
```

## References¶

- 1
V.D. Vaidya, B. Morrison, L.G. Helt, R. Shahrokhshahi, D.H. Mahler, M.J. Collins, K. Tan, J. Lavoie, A. Repingon, M. Menotti, N. Quesada, R.C. Pooser, A.E. Lita, T. Gerrits, S.W. Nam, and Z. Vernon. “Broadband quadrature-squeezed vacuum and nonclassical photon number correlations from a nanophotonic device.” arXiv:1904.07833 (2019).

- 2
A. Christ, K. Laiho, A. Eckstein, K. N. Cassemiro, and C. Silberhorn. “Probing multimode squeezing with correlation functions.” New Journal of Physics 13, 033027 (2011).

**Total running time of the script:** ( 0 minutes 0.000 seconds)

## Contents

## Downloads

## Related tutorials