.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "examples/measurement_devices/example_002_coincidence_detection.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code. .. rst-class:: sphx-glr-example-title .. _sphx_glr_examples_measurement_devices_example_002_coincidence_detection.py: HOM Coincidence Observation Example =================================== This example prepares two identical single-photon inputs, interferes them on a 50/50 beam splitter, and observes the joint output photon-number distribution across both output ports. For indistinguishable photons, the Hong-Ou-Mandel effect suppresses the (1, 1) coincidence event. .. GENERATED FROM PYTHON SOURCE LINES 12-30 .. code-block:: Python from __future__ import annotations import numpy as np from symop.core.protocols.states.base import State from symop.devices.models.beamsplitters.beamsplitter import BeamSplitter from symop.devices.models.detectors.coincidence_detector import ( CoincidenceDetector, ) from symop.devices.models.paths.delay import Delay from symop.devices.models.sources.number_state_source import NumberStateSource from symop.modes.envelopes.gaussian import GaussianEnvelope from symop.modes.labels.path import Path from symop.modes.labels.polarization import Polarization from symop.polynomial.state.ket import KetPolyState import symop.viz as VI .. GENERATED FROM PYTHON SOURCE LINES 31-34 **Setup** Two identical single-photon sources and a coincidence detector. .. GENERATED FROM PYTHON SOURCE LINES 34-46 .. code-block:: Python src = NumberStateSource( envelope=GaussianEnvelope(omega0=100.0, sigma=50.0, tau=0.0), polarization=Polarization.H(), n=1, ) coinc = CoincidenceDetector(input_ports=("out0", "out1")) bs = BeamSplitter(theta=np.pi / 4) .. GENERATED FROM PYTHON SOURCE LINES 47-50 **Prepare the input state** One photon in each input path. .. GENERATED FROM PYTHON SOURCE LINES 50-60 .. code-block:: Python vac = KetPolyState.vacuum() state_a = src(vac, ports={"out": Path("src_a")}).with_label("a") state_b = src(vac, ports={"out": Path("src_b")}).with_label("b") state_in = state_a.join(state_b).with_label("input") VI.display(state_in) .. raw:: html
2026-04-24T11:36:49.213932 image/svg+xml Matplotlib v3.10.9, https://matplotlib.org/


.. GENERATED FROM PYTHON SOURCE LINES 61-63 **Interfere on a 50/50 beam splitter** .. GENERATED FROM PYTHON SOURCE LINES 63-77 .. code-block:: Python state_out = bs( state_in, ports={ "in0": Path("src_a"), "in1": Path("src_b"), "out0": Path("bs_out0"), "out1": Path("bs_out1"), }, ).with_label("after_bs") VI.display(state_out) .. raw:: html
2026-04-24T11:36:49.289062 image/svg+xml Matplotlib v3.10.9, https://matplotlib.org/


.. GENERATED FROM PYTHON SOURCE LINES 78-83 **Observe the joint output distribution** This is the HOM signature: the joint (1, 1) event should be suppressed for perfectly indistinguishable photons. .. GENERATED FROM PYTHON SOURCE LINES 83-95 .. code-block:: Python observation = coinc.observe( state=state_out, ports={ "out0": Path("bs_out0"), "out1": Path("bs_out1"), }, ) VI.display(observation) print(observation) .. rst-class:: sphx-glr-script-out .. code-block:: none ObservationResult(action=ObserveAction(measurement_spec=ProjectiveNumberMeasurementSpec(target=MeasurementTarget(selections=(MeasurementSelection(port_name='out0', paths=(Path(name='bs_out0'),), mode_sigs=()), MeasurementSelection(port_name='out1', paths=(Path(name='bs_out1'),), mode_sigs=()))), resolution=MeasurementResolution(axes=('path',), grouping='joint_ports', readout='number')), destructive=False), metadata={}, probabilities={JointOutcome(outcomes_by_port=(('out0', NumberOutcome(count=0)), ('out1', NumberOutcome(count=2)))): 0.5, JointOutcome(outcomes_by_port=(('out0', NumberOutcome(count=2)), ('out1', NumberOutcome(count=0)))): 0.5}, expectation=None) .. GENERATED FROM PYTHON SOURCE LINES 96-100 **Detect one joint output event** Detection samples one concrete joint outcome from the HOM output distribution and returns the corresponding post-measurement state. .. GENERATED FROM PYTHON SOURCE LINES 100-112 .. code-block:: Python detection = coinc.detect( state=state_out, ports={ "out0": Path("bs_out0"), "out1": Path("bs_out1"), }, ) VI.display(detection) print(detection) .. rst-class:: sphx-glr-script-out .. code-block:: none DetectionResult(action=DetectAction(measurement_spec=ProjectiveNumberMeasurementSpec(target=MeasurementTarget(selections=(MeasurementSelection(port_name='out0', paths=(Path(name='bs_out0'),), mode_sigs=()), MeasurementSelection(port_name='out1', paths=(Path(name='bs_out1'),), mode_sigs=()))), resolution=MeasurementResolution(axes=('path',), grouping='joint_ports', readout='number')), destructive=True), metadata={}, record=JointOutcome(outcomes_by_port=(('out0', NumberOutcome(count=0)), ('out1', NumberOutcome(count=2)))), outcome=JointOutcome(outcomes_by_port=(('out0', NumberOutcome(count=0)), ('out1', NumberOutcome(count=2)))), probability=0.5, state=DensityPolyState(rho=DensityPoly(terms=(DensityTerm(coeff=(1+0j), left=Monomial(creators=(), annihilators=()), right=Monomial(creators=(), annihilators=())),)), label=None, index=25)) .. GENERATED FROM PYTHON SOURCE LINES 113-114 **Post-measurement state after joint detection** .. GENERATED FROM PYTHON SOURCE LINES 114-118 .. code-block:: Python VI.display(detection.state) print(detection.state) .. rst-class:: sphx-glr-script-out .. code-block:: none DensityPolyState(rho=DensityPoly(terms=(DensityTerm(coeff=(1+0j), left=Monomial(creators=(), annihilators=()), right=Monomial(creators=(), annihilators=())),)), label=None, index=25) .. GENERATED FROM PYTHON SOURCE LINES 119-123 **Hong-Ou-Mandel coincidence scan** We delay one input arm, interfere the two photons on the 50/50 beam splitter, and record the coincidence probability P(1,1) at the outputs. .. GENERATED FROM PYTHON SOURCE LINES 123-200 .. code-block:: Python import matplotlib.pyplot as plt from symop.devices.measurement.outcomes import JointOutcome, NumberOutcome def modify_delay_on_path(state: State, path: Path, delay: float) -> State: delay_dev = Delay(dt=delay) return delay_dev(state, ports={"in": path, "out": path}) def joint_outcome_probability( observation, *, out0_count: int, out1_count: int, ) -> float: """Extract P(out0=out0_count, out1=out1_count) from a joint observation.""" for outcome, prob in observation.probabilities.items(): if not isinstance(outcome, JointOutcome): continue counts = { port_name: port_outcome.count for port_name, port_outcome in outcome.outcomes_by_port if isinstance(port_outcome, NumberOutcome) } if ( counts.get("out0") == out0_count and counts.get("out1") == out1_count ): return prob return 0.0 def hom_coincidence_probability(delay: float) -> float: """Return the HOM coincidence probability P(1,1) for a given delay.""" vac = KetPolyState.vacuum() state_a = src(vac, ports={"out": Path("src_a")}).with_label("a") state_b = src(vac, ports={"out": Path("src_b")}).with_label("b") # Delay one arm before the beamsplitter. state_b_delayed = modify_delay_on_path( state_b, Path("src_b"), delay ).with_label("b_delayed") state_in = state_a.join(state_b_delayed).with_label("input") state_out = bs( state_in, ports={ "in0": Path("src_a"), "in1": Path("src_b"), "out0": Path("bs_out0"), "out1": Path("bs_out1"), }, ).with_label("after_bs") VI.display(state_out) observation = coinc.observe( state=state_out, ports={ "out0": Path("bs_out0"), "out1": Path("bs_out1"), }, ) return joint_outcome_probability( observation, out0_count=1, out1_count=1, ) .. GENERATED FROM PYTHON SOURCE LINES 201-203 Choose a delay window around the coherence scale. For Gaussian spectral width sigma, a good first guess is +/- 6 / sigma. .. GENERATED FROM PYTHON SOURCE LINES 203-215 .. code-block:: Python delay_max = 6.0 * src.envelope.sigma delays = np.linspace(-delay_max, delay_max, 121) coincidences = np.array([hom_coincidence_probability(dt) for dt in delays]) plt.figure(figsize=(7, 4)) plt.plot(delays, coincidences) plt.xlabel("Delay") plt.ylabel("Coincidence probability P(1,1)") plt.title("Hong-Ou-Mandel coincidence curve") plt.grid(True) plt.show() .. image-sg:: /examples/measurement_devices/images/sphx_glr_example_002_coincidence_detection_001.png :alt: Hong-Ou-Mandel coincidence curve :srcset: /examples/measurement_devices/images/sphx_glr_example_002_coincidence_detection_001.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 0.455 seconds) .. _sphx_glr_download_examples_measurement_devices_example_002_coincidence_detection.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: example_002_coincidence_detection.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: example_002_coincidence_detection.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: example_002_coincidence_detection.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_