# Threshold detector probabilities of Gaussian states¶

Many quantum optics experiments use photon detectors which distinguish between vacuum or non-vacuum events, often known as threshold detectors. We sometimes describe the non-vacuum outcome as a ‘click’. Superconducting nanowire single-photon detectors (SNSPDs) and single-photon avalanche diodes (SPADs) are common examples of threshold detectors.

Here, we will show how we can calculate threshold detector probabilities for any Gaussian state. Let’s say we have an experiment where we create a two-mode squeezed vacuum (TMSV) state (more details on two-mode squeezing can be found here).

import numpy as np
from strawberryfields import Engine, Program
from strawberryfields.ops import S2gate

M = 2
prog = Program(2)
eng = Engine("gaussian")

with prog.context as q:
S2gate(0.1) | (q, q)

results = eng.run(prog)
state = results.state


We can use Strawberry Fields to calculate the probability of Fock state projectors, e.g.,

p_fock11 = state.fock_prob([1, 1])


However, if we wish to see the probability of threshold detector outcomes, we must instead use a function from The Walrus library, which is installed alongside Strawberry Fields.

Because our state is Gaussian and we are using the Engine with the gaussian backend, we can get the Gaussian covariance matrix and displacement vector of our state.

mu = state.means()
cov = state.cov()

print(mu)
print(cov)


Out:

[0. 0. 0. 0.]
[[ 1.02006676  0.201336    0.          0.        ]
[ 0.201336    1.02006676  0.          0.        ]
[ 0.          0.          1.02006676 -0.201336  ]
[ 0.          0.         -0.201336    1.02006676]]


These are then passed to the threshold_detection_prob function, which takes as arguments: the displacement vector, mu, the covariance matrix, cov, and a list describing the detector outcomes. A vacuum outcome is labelled as 0 and a click is labelled by a 1. We pass in [1, 1] to get the probability that both of the two modes give a click from the threshold detectors.

from thewalrus import threshold_detection_prob

p_click11 = threshold_detection_prob(mu, cov, [1, 1])

print(f"single-photon coincidence probability = {p_fock11}")
print(f"click coincidence probability = {np.real_if_close(p_click11)}")


Out:

single-photon coincidence probability = 0.00983503057503257
click coincidence probability = 0.009933709152560139


We see that these probabilities are similar (both are close to 1%), but not the same. If we use a higher squeezing parameter, we can see that these probabilities diverge further.

prog = Program(2)
eng = Engine("gaussian")

with prog.context as q:
S2gate(1) | (q, q)

results = eng.run(prog)
state = results.state

p_fock11 = state.fock_prob([1, 1])

mu = state.means()
cov = state.cov()

p_click11 = threshold_detection_prob(mu, cov, [1, 1])

print(f"single-photon coincidence probability = {p_fock11}")
print(f"click coincidence probability = {p_click11.real}")


Out:

single-photon coincidence probability = 0.24359589399989143
click coincidence probability = 0.5800256583859738


We see that when we increase the squeezing, the click coincidence probability increases much more than the single-photon coincidence probability. This extra probability is due to higher-order Fock state terms in the TMSV state. This extra probability is sometimes known as multiphoton contamination because these higher-order terms mean that our threshold detector statistics are no longer well-approximated by single-photon projectors . From this example, we can also see that multiphoton contamination can be a more severe problem for high levels of squeezing.

Sometimes multiphoton contamination can be a problem. To see why, let’s investigate Hong-Ou-Mandel (HOM) interference  between photons from a TMSV state:

from strawberryfields.ops import BSgate

def HOM_probs(r):

prog = Program(2)
eng = Engine("gaussian")

with prog.context as q:
S2gate(r) | (q, q)
BSgate(0.25 * np.pi) | (q, q)

results = eng.run(prog)
state = results.state

p_fock = state.fock_prob([1, 1])

mu = state.means()
cov = state.cov()

p_click = threshold_detection_prob(mu, cov, [1, 1])

return p_fock, p_click

r = 0.1
p_fock, p_click = HOM_probs(r)
print(f"squeezing: r = {r}")
print(f"single-photon coincidence probability = {np.round(p_fock,14)}")
print(f"threshold detector coincidence probability = {np.round(p_click.real, 14)}")

r = 1
p_fock, p_click = HOM_probs(r)
print()
print(f"squeezing: r = {r}")
print(f"single-photon coincidence probability = {np.round(p_fock,14)}")
print(f"threshold detector coincidence probability = {np.round(p_click.real, 14)}")


Out:

squeezing: r = 0.1
single-photon coincidence probability = 0.0
threshold detector coincidence probability = 2.479294099e-05

squeezing: r = 1
single-photon coincidence probability = 0.0
threshold detector coincidence probability = 0.12386579428626


In ideal HOM interference, if we send two photons onto a 50/50 beamsplitter (a BSgate with $$\theta=\pi/4$$ ), we should never see two photons at the output. To understand what is happening here, we should consider that TMSV creates correlated pairs of photons across its two modes. The HOM interference occurs perfectly when we have single-photon projectors, independent of what our squeezing parameter is. This is because we know exactly how many photons must have arrived at the beamsplitter. However, when we have threshold detectors, we cannot perfectly count the photons, so we cannot distinguish terms in the TMSV state. This leads to multiphoton contamination which degrades our interference. HOM interference underpins many important photonic protocols, e.g., Bell state measurement, and so degradation in the interference visibility can lead to poor fidelity of operations.

## Displaced Gaussian States¶

In the examples considered so far, we have only explored states which contain no displacement. However, the threshold_detection_prob function is compatible with displaced Gaussian states.

from strawberryfields.ops import Dgate

prog = Program(3)
eng = Engine("gaussian")

alpha = 1
r = 1

with prog.context as q:
S2gate(r) | (q, q)
Dgate(alpha) | q
BSgate(0.25 * np.pi) | (q, q)

results = eng.run(prog)
state = results.state

p_fock = state.fock_prob([1, 1, 1])

mu = state.means()
cov = state.cov()
print(mu)
print(cov)

p_click = threshold_detection_prob(mu, cov, [1, 1, 1])

print(f"single-photon coincidence probability = {np.round(p_fock,14)}")
print(f"threshold detector coincidence probability = {np.round(p_click.real, 14)}")


Out:

[ 0.         -1.41421356  1.41421356  0.          0.          0.        ]
[[ 3.76219569  2.56457759  2.56457759  0.          0.          0.        ]
[ 2.56457759  2.38109785  1.38109785  0.          0.          0.        ]
[ 2.56457759  1.38109785  2.38109785  0.          0.          0.        ]
[ 0.          0.          0.          3.76219569 -2.56457759 -2.56457759]
[ 0.          0.          0.         -2.56457759  2.38109785  1.38109785]
[ 0.          0.          0.         -2.56457759  1.38109785  2.38109785]]
single-photon coincidence probability = 0.0
threshold detector coincidence probability = 0.20933477023044


Here we see a similar phenomenon but between the 3-mode coincidence measurements where we interfere one mode of the TMSV with a coherent state. When we have threshold detectors, this coincidence is not completely suppressed.