Spaces:
Sleeping
Sleeping
import numpy as np | |
import pandas as pd | |
import streamlit as st | |
from matplotlib import pyplot as plt | |
from physics.particles import OneBitEntangledPhoton, LocalDeterministicPhoton | |
def main(): | |
st.write("# Object Oriented Bell Violation") | |
st.write( | |
""" | |
This is a part of the coding challenge for [EPR Labs](https://epr-labs.com) data-science internship for physicists. | |
The challenge is to implement a non-local model of a pair of entangled particles that recreates | |
the results of Bell-violating EPR experiments through a Monte Carlo simulation. | |
""" | |
) | |
st.write( | |
""" | |
Particle class should inherid `PolarizedParticleBase` – for simplicity you can assume | |
it's a photon, and we're interested in measuring polarization given a detector at an angle. | |
""" | |
) | |
code = """ | |
class PolarizedParticleBase(ABC): | |
@abstractmethod | |
def measure_polarization(self, detector_angle_rad: float) -> PolarizationMeasurementOutcome: | |
pass | |
""" | |
st.code(code, language="python") | |
st.write( | |
""" | |
The Monte Carlo simulation should perform at least 10k measurements of an entangled | |
pair with random detector angles. We're interested in tracking the information about | |
detector angles and measurement outputs. Here's an overview of what the simulation loop can look like: | |
""" | |
) | |
code = """ | |
results = [] | |
for it in range(100_000): | |
a_photon = YourPhoton(...) | |
b_photon = YourPhoton(...) | |
# Your entanglement mechanism | |
a_photon.entangle(b_photon) | |
# We are interested only in the angle difference in range [0, pi/2] | |
a_detector_angle = 0 | |
b_detector_angle = np.random.random() * np.pi / 2 | |
a_polarization_status = a_photon.measure_polarization(a_detector_angle) | |
b_polarization_status = b_photon.measure_polarization(b_detector_angle) | |
result = { | |
"a_outcome": a_polarization_status.value, | |
"b_outcome": b_polarization_status.value, | |
"a_detector": a_detector_angle, | |
"b_detector": b_detector_angle, | |
} | |
results.append(result) | |
""" | |
st.code(code, language="python") | |
st.write( | |
""" | |
Below you can see results for two different simulations. | |
One for a local hidden variables model, which fails to violate Bell inequalities, | |
and one for a model with 1-bit non-local communication, that violates Bell inequalities, but | |
fails to reproduce empirical results. | |
""" | |
) | |
st.write("## Local Photon Model") | |
st.write("What Einstein hoped for 🥲") | |
df = run_local_experiment() | |
df["angle_bin"] = df.angle_diff.round(2) | |
# TODO: Write an experiment results wrapper class & nice chart | |
angle_agreement = df.groupby("angle_bin").agreement.mean().reset_index() | |
fig = draw_polarization_agreement_chart(agreement_df=angle_agreement) | |
st.pyplot(fig) | |
st.write("## 1-bit superluminal communication") | |
st.write( | |
"This simulation recreates a model proposed by T.Maudlin in 1992 [[1](https://www.jstor.org/stable/192771)]." | |
) | |
df = run_entangled_experiment() | |
df["angle_bin"] = df.angle_diff.round(2) | |
# TODO: Write an experiment results wrapper class & nice chart | |
angle_agreement = df.groupby("angle_bin").agreement.mean().reset_index() | |
fig = draw_polarization_agreement_chart(agreement_df=angle_agreement) | |
st.pyplot(fig) | |
st.write("---") | |
st.write( | |
""" | |
Recruitment has not yet started. If this seems interesting to you, follow us on linkedin for updates: | |
https://linkedin.com/company/epr-labs | |
""" | |
) | |
# show_refs() | |
def show_refs(): | |
title = "The Communication Cost of Simulating Bell Correlations" | |
link = "https://arxiv.org/abs/quant-ph/0304076" | |
st.write(f"[{title}]({link})") | |
def draw_polarization_agreement_chart(agreement_df: pd.DataFrame): | |
fig, ax = plt.subplots(figsize=[9, 4]) | |
x = agreement_df.angle_bin.values * 180 / np.pi | |
ax.plot( | |
x, | |
agreement_df.agreement, | |
".", | |
ms=7, | |
label="this simulation", | |
color="indigo", | |
) | |
ax.plot( | |
x, | |
np.cos(agreement_df.angle_bin) ** 2, | |
label="EPR experiments", | |
color="teal", | |
) | |
ax.set_xlabel("Detectors angle difference [degrees]", fontsize=14) | |
ax.set_ylabel( | |
"Proportion of agreement in\nspace time separated\npolarization angle measurements", | |
fontsize=14, | |
) | |
ax.set_ylim(0, 1) | |
ax.set_xlim(0, x.max()) | |
ax.legend() | |
y_down = np.zeros_like(x) | |
# y_up = np.ones_like(x) | |
y_diagonal = np.linspace(1, 0, len(x)) | |
ax.fill_between(x, y_down, y_diagonal, color="gray", alpha=0.2) | |
ax.text(10.3, 0.3, "Possible with\nBell-local theory", fontsize=20, rotation=0) | |
# ax.fill_between(x, y_diagonal, y_up, color="gray", alpha=0.4) | |
ax.text(54.3, 0.50, "Not possible with\nBell-local theory", fontsize=20, rotation=0) | |
return fig | |
def run_entangled_experiment(n_steps: int = 100_000) -> pd.DataFrame: | |
results = [] | |
for it in range(n_steps): | |
reference_angle = np.random.random() * np.pi | |
a_photon = OneBitEntangledPhoton(reference_angle) | |
b_photon = OneBitEntangledPhoton(reference_angle) | |
a_photon.entangle(b_photon) | |
b_photon.entangle(a_photon) | |
# We are interested in the angle difference between detectors | |
# and we only want to measure within range of [0 - pi/2] ... | |
a_detector_angle = 0 | |
# ... so we only allow the movement of the second detector within that range | |
b_detector_angle = np.random.random() * np.pi / 2 | |
a_polarization_status = a_photon.measure_polarization(a_detector_angle) | |
b_polarization_status = b_photon.measure_polarization(b_detector_angle) | |
result = { | |
"a_outcome": a_polarization_status.value, | |
"b_outcome": b_polarization_status.value, | |
"a_detector": a_detector_angle, | |
"b_detector": b_detector_angle, | |
} | |
results.append(result) | |
df = pd.DataFrame(results) | |
df["agreement"] = df.a_outcome == df.b_outcome | |
df["angle_diff"] = df.b_detector - df.a_detector | |
return df | |
def run_local_experiment(n_steps: int = 100_000) -> pd.DataFrame: | |
results = [] | |
for it in range(n_steps): | |
# In the experimental setup we have photons with same polarization | |
polarization_angle = np.random.random() * np.pi | |
a_photon = LocalDeterministicPhoton(polarization_angle) | |
b_photon = LocalDeterministicPhoton(polarization_angle) | |
# We are interested in the angle difference between detectors | |
# and we only want to measure within range of [0 - pi/2] ... | |
a_detector_angle = 0 | |
# ... so we only allow the movement of the second detector within that range | |
b_detector_angle = np.random.random() * np.pi / 2 | |
# Without entanglement order of measurement doesn't matter | |
# (does it matter with entangled particles? :thinking:) | |
a_polarization_status = a_photon.measure_polarization(a_detector_angle) | |
b_polarization_status = b_photon.measure_polarization(b_detector_angle) | |
result = { | |
"a_outcome": a_polarization_status.value, | |
"b_outcome": b_polarization_status.value, | |
"a_detector": a_detector_angle, | |
"b_detector": b_detector_angle, | |
} | |
results.append(result) | |
df = pd.DataFrame(results) | |
df["agreement"] = df.a_outcome == df.b_outcome | |
df["angle_diff"] = df.b_detector - df.a_detector | |
return df | |
if __name__ == "__main__": | |
main() | |