Advertisement
chemoelectric

EPR-B simulation analysis

Aug 10th, 2023
879
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 8.18 KB | Source Code | 0 0
  1. #!/bin/env -S python3
  2.  
  3. #
  4. # Reference:
  5. #
  6. #   A. F. Kracklauer, ‘EPR-B correlations: non-locality or geometry?’,
  7. #   J. Nonlinear Math. Phys. 11 (Supp.) 104–109 (2004).
  8. #   https://doi.org/10.2991/jnmp.2004.11.s1.13 (Open access, CC BY-NC)
  9. #
  10.  
  11. import sys
  12. from math import atan2, cos, floor, pi, radians, sin, sqrt
  13.  
  14. class PulseData():
  15.     '''The data for one light pulse.'''
  16.  
  17.     def __init__(self, logS, logL, logR, detectedL1, detectedL2,
  18.                  detectedR1, detectedR2):
  19.         self.logS = logS
  20.         self.logL = logL
  21.         self.logR = logR
  22.         self.detectedL1 = detectedL1
  23.         self.detectedL2 = detectedL2
  24.         self.detectedR1 = detectedR1
  25.         self.detectedR2 = detectedR2
  26.  
  27.     def __repr__(self):
  28.         return ("PulseData(" + str(self.logS) + ", "
  29.                 + str(self.logL) + ", "
  30.                 + str(self.logR) + ", "
  31.                 + str(self.detectedL1) + ", "
  32.                 + str(self.detectedL2) + ", "
  33.                 + str(self.detectedR1) + ", "
  34.                 + str(self.detectedR2) + ")")
  35.  
  36.     def swap_L_channels(self):
  37.         '''Swap detectedL1 and detectedL2.'''
  38.         (self.detectedL1, self.detectedL2) = \
  39.             (self.detectedL2, self.detectedL1)
  40.  
  41.     def swap_R_channels(self):
  42.         '''Swap detectedR1 and detectedR2.'''
  43.         (self.detectedR1, self.detectedR2) = \
  44.             (self.detectedR2, self.detectedR1)
  45.  
  46.     def swap_LR_channels(self):
  47.         '''Swap channels on both right and left. This is done if the
  48.        light source was (1,90°) instead of (1,0°). For in that case
  49.        the orientations of the polarizing beam splitters, relative to
  50.        the light source, is different by 90°.'''
  51.         self.swap_L_channels()
  52.         self.swap_R_channels()
  53.  
  54. def split_data(predicate, data):
  55.     '''Split the data into two subsets, according to whether a set
  56.    item satisfies a predicate. The return value is a tuple, with
  57.    those items satisfying the predicate in the first tuple entry, the
  58.    other items in the second entry.
  59.  
  60.    '''
  61.     subset1 = {x for x in data if predicate(x)}
  62.     subset2 = data - subset1
  63.     return (subset1, subset2)
  64.  
  65. def adjust_data_for_light_pulse_orientation(data):
  66.     '''Some data items are for a (1,0°) light pulse. The others are
  67.    for a (1,90°) light pulse. Thus the light pulses are oriented
  68.    differently with respect to the polarizing beam splitters. We
  69.    adjust for that distinction here.'''
  70.     data0, data90 = split_data(lambda item: item.logS == 0, data)
  71.     for item in data90:
  72.         item.swap_LR_channels()
  73.     return (data0 | data90)     # Set union.
  74.  
  75. def split_data_according_to_PBS_setting(data):
  76.     '''Split the data into four subsets: one subset for each
  77.    arrangement of the two polarizing beam splitters.'''
  78.     dataL1, dataL2 = split_data(lambda item: item.logL == 0, data)
  79.     dataL1R1, dataL1R2 = \
  80.         split_data(lambda item: item.logR == 0, dataL1)
  81.     dataL2R1, dataL2R2 = \
  82.         split_data(lambda item: item.logR == 0, dataL2)
  83.     return (dataL1R1, dataL1R2, dataL2R1, dataL2R2)
  84.  
  85. def compute_correlation_coefficient(angleL, angleR, data):
  86.     '''Compute the correlation coefficient for the subset of the data
  87.    that corresponding to a particular setting of the polarizing beam
  88.    splitters.'''
  89.  
  90.     # We make certain the orientations of beam splitters are
  91.     # represented by perpendicular angles in the first and fourth
  92.     # quadrant. This restriction causes no loss of generality, because
  93.     # the orientation of the beam splitter is actually a rotated "X".
  94.     assert (all(0 <= x and x < 90 for x in (angleL, angleR)))
  95.     #perpendicularL = angleL - 90 # In Quadrant 4.
  96.     #perpendicularR = angleR - 90 # In Quadrant 4.
  97.  
  98.     # Note that the sine is non-negative in Quadrant 1, and the cosine
  99.     # is non-negative in Quadrant 4. Thus we can use the following
  100.     # estimates for cosine and sine. This is Equation (2.4) in the
  101.     # reference. (Note, one can also use Quadrants 1 and 2 and reverse
  102.     # the roles of cosine and sine. And so on like that.)
  103.     N = len(data)
  104.     NL1 = 0
  105.     NL2 = 0
  106.     NR1 = 0
  107.     NR2 = 0
  108.     for item in data:
  109.         NL1 += item.detectedL1
  110.         NL2 += item.detectedL2
  111.         NR1 += item.detectedR1
  112.         NR2 += item.detectedR2
  113.     sinL = sqrt(NL1 / N)
  114.     cosL = sqrt(NL2 / N)
  115.     sinR = sqrt(NR1 / N)
  116.     cosR = sqrt(NR2 / N)
  117.  
  118.     # Now we can apply the reference's Equation (2.3).
  119.     cosLR = (cosR * cosL) + (sinR * sinL)
  120.     sinLR = (sinR * cosL) - (cosR * sinL)
  121.  
  122.     # And then Equation (2.5).
  123.     kappa = (cosLR * cosLR) - (sinLR * sinLR)
  124.  
  125.     return kappa
  126.  
  127. def read_raw_data(filename):
  128.     '''Read the raw data. Its order does not actually matter, so we
  129.    return the data as a set.'''
  130.  
  131.     def make_record(line):
  132.         x = line.split()
  133.         record = PulseData(logS = int(x[0]),
  134.                            logL = int(x[1]),
  135.                            logR = int(x[2]),
  136.                            detectedL1 = int(x[3]),
  137.                            detectedL2 = int(x[4]),
  138.                            detectedR1 = int(x[5]),
  139.                            detectedR2 = int(x[6]))
  140.         return record
  141.  
  142.     def read_data(f):
  143.         num_pulses = int(f.readline())
  144.         data = set()
  145.         for i in range(num_pulses):
  146.             data.add(make_record(f.readline()))
  147.         return data
  148.  
  149.     if filename != "-":
  150.         with open(filename, "r") as f:
  151.             data = read_data(f)
  152.     else:
  153.         data = read_data(sys.stdin)
  154.     return data
  155.  
  156. if __name__ == '__main__':
  157.  
  158.     if len(sys.argv) != 1 and len(sys.argv) != 2:
  159.         print("Usage: " + sys.argv[0] + " [RAW_DATA_FILE]")
  160.         sys.exit(1)
  161.     raw_data_filename = (sys.argv[1] if len(sys.argv) == 2 else "-")
  162.  
  163.     # Polarizing beam splitter orientations commonly used in actual
  164.     # experiments. These must match the values used in the simulation
  165.     # itself. They are by design all either zero degrees or in the
  166.     # first quadrant.
  167.     anglesL = (0.0, 45.0)
  168.     anglesR = (22.5, 67.5)
  169.     assert (all(0 <= x and x < 90 for x in anglesL + anglesR))
  170.  
  171.     data = read_raw_data(raw_data_filename)
  172.     data = adjust_data_for_light_pulse_orientation(data)
  173.     dataL1R1, dataL1R2, dataL2R1, dataL2R2 = \
  174.         split_data_according_to_PBS_setting(data)
  175.  
  176.     kappaL1R1 = \
  177.         compute_correlation_coefficient (anglesL[0], anglesR[0],
  178.                                          dataL1R1)
  179.     kappaL1R2 = \
  180.         compute_correlation_coefficient (anglesL[0], anglesR[1],
  181.                                          dataL1R2)
  182.     kappaL2R1 = \
  183.         compute_correlation_coefficient (anglesL[1], anglesR[0],
  184.                                          dataL2R1)
  185.     kappaL2R2 = \
  186.         compute_correlation_coefficient (anglesL[1], anglesR[1],
  187.                                          dataL2R2)
  188.  
  189.     chsh_contrast = -kappaL1R1 + kappaL1R2 + kappaL2R1 + kappaL2R2
  190.  
  191.     # The nominal value of the CHSH contrast for the chosen polarizer
  192.     # orientations is 2*sqrt(2).
  193.     chsh_contrast_nominal = 2 * sqrt(2.0)
  194.  
  195.     print()
  196.     print("   light pulse events   %9d" % len(data))
  197.     print()
  198.     print("    correlation coefs")
  199.     print("          %4.1f° %4.1f°   %+9.6f" % (anglesL[0], anglesR[0],
  200.                                                 kappaL1R1))
  201.     print("          %4.1f° %4.1f°   %+9.6f" % (anglesL[0], anglesR[1],
  202.                                                 kappaL1R2))
  203.     print("          %4.1f° %4.1f°   %+9.6f" % (anglesL[1], anglesR[0],
  204.                                                 kappaL2R1))
  205.     print("          %4.1f° %4.1f°   %+9.6f" % (anglesL[1], anglesR[1],
  206.                                                 kappaL2R2))
  207.     print()
  208.     print("        CHSH contrast   %+9.6f" % chsh_contrast)
  209.     print("  2*sqrt(2) = nominal   %+9.6f" % chsh_contrast_nominal)
  210.     print("           difference   %+9.6f" % (chsh_contrast -
  211.                                               chsh_contrast_nominal))
  212.  
  213.     # A "CHSH violation" occurs if the CHSH contrast is >2.
  214.     # https://en.wikipedia.org/w/index.php?title=CHSH_inequality&oldid=1142431418
  215.     print()
  216.     print("       CHSH violation   %+9.6f" % (chsh_contrast - 2))
  217.     print()
  218.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement