Measurements

All the built-in measurement instructions in Piquasso. Measurement instructions should be placed at the end of the Piquasso program.

Note

When multiple shots are specified and the evolution of the choses simulated state is possible, the measurement outcome corresponding to the last shot is used to evolve the state.

class ParticleNumberMeasurement

Particle number measurement.

A non-Gaussian projective measurement with the probability density given by

\[p(n) = \operatorname{Tr} \left [ \rho | n \rangle \langle n | \right ]\]

The generated samples are non-negative integer values corresponding to the detected photon number.

Note

When used with GaussianState, the state is not evolved, since that would be non-Gaussian.

class ThresholdMeasurement

Threshold measurement.

Similar to ParticleNumberMeasurement, but only measuring whether or not the measured mode contains any photon.

The generated samples contain \(0\) or \(1\), where \(0\) corresponds to no photon being detected, and \(1\) corresponds to detection of at least one photon.

class GeneraldyneMeasurement(detection_covariance: ndarray)

General-dyne measurement.

The probability density is given by

\[p(r_m) = \frac{ \exp \left ( (r_m - r)^T \frac{1}{\sigma + \sigma_m} (r_m - r) \right ) }{ \pi^d \sqrt{ \operatorname{det} (\sigma + \sigma_m) } },\]

where \(r_m \in \mathbb{C}^d\), \(\sigma\) is the covariance matrix of the current state, \(r \in \mathbb{C}^d\) is the first moment of the current state, and \(\sigma_m\) is the covariance corresponding to a non-displaced Gaussian state characterizing the general-dyne detection. Notably, the heterodyne detection would correspond to a non-displaced Gaussian state with covariance \(\sigma_m = I_{d \times d}\).

Parameters:

detection_covariance (numpy.ndarray) – A \(2\times2\) symplectic matrix corresponding to a purely quadratic Hamiltonian.

Raises:

InvalidParameter – When the detection covariance does not satisfy the Robertson-Schrödinger uncertainty relation.

class HomodyneMeasurement(phi: float = 0.0, z: float = 0.0001)

Homodyne measurement.

Corresponds to measurement of the quadrature operator

\[\hat{x}_{\phi} = \cos \phi \hat{x} + \sin \phi \hat{p}\]

with outcome probability density given by

\[p(x_{\phi}) = \langle x_{\phi} | \rho | x_{\phi} \rangle,\]

where \(x_{\phi}\) correspond to the eigenvalues of \(\hat{x}_{\phi}\).

In optical setups, the measurement is performed by mixing the state \(\rho\) with a strong coherent state \(| \alpha \rangle\), where \(\alpha >> 1\), then subtracting the detected intensities of the two outputs. The mixing is performed with a 50:50 beamsplitter.

Parameters:
  • phi (float) – Phase space rotation angle.

  • z (float) – Squeezing amplitude. In the limit of z going to infinity one would recover the pure homodyne measurement in the so-called strong oscillator limit. Conversely, setting z = 1 would correspond to HeterodyneMeasurement.

class HeterodyneMeasurement

Heterodyne measurement.

The probability density is given by

\[p(\alpha) = \frac{1}{\pi} \operatorname{Tr} ( \rho | \alpha \rangle \langle \alpha | ).\]

In optical setups, the measurement is performed by mixing the state \(\rho\) with a vacuum state \(| 0 \rangle\), then subtracting the detected intensities of the two outputs. The mixing is performed with a 50:50 beamsplitter.

class PostSelectPhotons(photon_counts: Tuple[int, ...])

Post-selection on detected photon numbers.

Example usage:

with pq.Program() as program:
    pq.Q(all) | pq.StateVector([0, 1, 0]) * np.sqrt(0.2)
    pq.Q(all) | pq.StateVector([1, 1, 0]) * np.sqrt(0.3)
    pq.Q(all) | pq.StateVector([2, 1, 0]) * np.sqrt(0.5)

    pq.Q(0) | pq.Phaseshifter(np.pi)

    pq.Q(1, 2) | pq.Beamsplitter(np.pi / 8)
    pq.Q(0, 1) | pq.Beamsplitter(1.1437)
    pq.Q(1, 2) | pq.Beamsplitter(-np.pi / 8)

    pq.Q(1, 2) | pq.PostSelectPhotons(photon_counts=(1, 0))

simulator = pq.PureFockSimulator(d=3, config=pq.Config(cutoff=4))

state = simulator.execute(program).state

Note

The resulting state is not normalized. To normalize it, use normalize(). Moreover, it is a state over the remaining modes, i.e., if k modes are post-selected, then the resulting state will be defined on a system with k modes less.

Parameters:

photon_counts (Tuple[int, ...]) – The desired photon numbers on the specified modes.

class ImperfectPostSelectPhotons(photon_counts: Tuple[int, ...], detector_efficiency_matrix: ndarray)

Post-selection on detected photon numbers.

Example usage:

P = np.array(
    [
        [1.0, 0.1, 0.2],
        [0.0, 0.9, 0.2],
        [0.0, 0.0, 0.6],
    ],
)

with pq.Program() as program:
    pq.Q(all) | pq.StateVector([0, 1, 0]) * np.sqrt(0.2)
    pq.Q(all) | pq.StateVector([1, 1, 0]) * np.sqrt(0.3)
    pq.Q(all) | pq.StateVector([2, 1, 0]) * np.sqrt(0.5)

    pq.Q(0) | pq.Phaseshifter(np.pi)

    pq.Q(1, 2) | pq.Beamsplitter(np.pi / 8)
    pq.Q(0, 1) | pq.Beamsplitter(1.1437)
    pq.Q(1, 2) | pq.Beamsplitter(-np.pi / 8)

    pq.Q(1, 2) | pq.ImperfectPostSelectPhotons(
        photon_counts=(1, 0),
        detector_efficiency_matrix=P,
    )

simulator = pq.PureFockSimulator(d=3, config=pq.Config(cutoff=4))

state = simulator.execute(program).state

The detector efficiency matrix \(P\) is defined elementwise as

\[P_{n m} = p(\text{detected photon count } = n| \text{actual photon count } = m)\]

Note

The resulting state is not normalized. To normalize it, use normalize(). Moreover, it is a state over the remaining modes, i.e., if k modes are post-selected, then the resulting state will be defined on a system with k modes less.

_summary_

Parameters:
  • photon_counts (Tuple[int, ...]) – The desired photon numbers on the specified modes.

  • detector_efficiency_matrix (np.ndarray) – Detector efficiency matrix.