diff --git a/epr_experiment.py b/epr_experiment.py index 7685326..5c3ba77 100644 --- a/epr_experiment.py +++ b/epr_experiment.py @@ -2,8 +2,8 @@ ''' /************************/ /* epr_experiment.py */ -/* Version 1.0 */ -/* 2024/05/12 */ +/* Version 1.1 */ +/* 2024/05/28 */ /************************/ ''' import argparse @@ -125,7 +125,7 @@ def __init__(self, parent): case _: raise ValueError( f"Incorrect simulation type {self.simul_type}") - self.current_state = spin.psi + self.spin = spin # Initialize the directions which have a defined rotation between # each other # First apparatus @@ -650,7 +650,7 @@ def measure(self, n): if random_number < 0.5: # Simulate measuring the first apparatus - self.measurement1, self.measurement2 = self.measureSpin( + self.measurement1, self.measurement2 = self.spin.Measure( np.array([self.direction1p[self.button1], self.direction1m[self.button1]]), np.array([self.direction2p[self.button2], @@ -658,7 +658,7 @@ def measure(self, n): True) else: # Simulate measuring the second apparatus - self.measurement2, self.measurement1 = self.measureSpin( + self.measurement2, self.measurement1 = self.spin.Measure( np.array([self.direction1p[self.button1], self.direction1m[self.button1]]), np.array([self.direction2p[self.button2], @@ -677,74 +677,6 @@ def measure(self, n): # redraw self.update() - def measureSpin(self, directions1: np.ndarray, - directions2: np.ndarray, simulate_1: bool): - ''' - Perform the measurement of the spin of two directions 1 and 2, - which one to simulate is decided by the caller. - ''' - def Rho1(psi: np.ndarray): - return np.outer(psi, psi.conj()).reshape( - (2, 2, 2, 2)).trace(axis1=1, axis2=3) - - def Rho2(psi: np.ndarray): - return np.outer(psi, psi.conj()).reshape(( - 2, 2, 2, 2)).trace(axis1=0, axis2=2) - - psi = self.current_state - # Define the projector operator for the "+1" state - direction1_p1 = np.array(directions1[0]) - direction1_m1 = np.array(directions1[1]) - direction2_p1 = np.array(directions2[0]) - direction2_m1 = np.array(directions2[1]) - projector1_p1 = np.outer(direction1_p1, direction1_p1.conj()) - projector1_m1 = np.outer(direction1_m1, direction1_m1.conj()) - projector2_p1 = np.outer(direction2_p1, direction2_p1.conj()) - projector2_m1 = np.outer(direction2_m1, direction2_m1.conj()) - - if simulate_1: - # Create 4x4 projectors for the two-spin system - projector_p1_s = np.kron(projector1_p1, np.eye(2)) - projector_m1_s = np.kron(projector1_m1, np.eye(2)) - projector_p11 = projector1_p1 - projector_p21 = projector2_p1 - - # Calculate the reduced density matrix for the first spin - # which is measured - rho_i = Rho1(psi) - else: - projector_p1_s = np.kron(np.eye(2), projector2_p1) - projector_m1_s = np.kron(np.eye(2), projector2_m1) - projector_p11 = projector2_p1 - projector_p21 = projector1_p1 - rho_i = Rho2(psi) - - # Calculate the probability of this first spin being "+1" - prob_p11 = np.linalg.norm(np.trace(np.dot(projector_p11, rho_i))) - # Generate a random number between 0 and 1 - random_number1 = random.uniform(0, 1) - - # Perform the measurement in apparatus direction - sp1 = 1 if random_number1 < prob_p11 else -1 - - # reduce psi projecting on the direction - psi_r = np.dot(projector_p1_s, psi) if sp1 == 1 else \ - np.dot(projector_m1_s, psi) - # Normalize psi_r - psi_r = psi_r / np.linalg.norm(psi_r) - # Calculate the reduced density matrix for the second spin - # with the collapsed wave function for the first system - rho_j = Rho2(psi_r) if simulate_1 else Rho1(psi_r) - - # Calculate the probability of "system 2" being "+1" - prob_p12 = np.linalg.norm(np.trace(np.dot(projector_p21, rho_j))) - - # Generate a random number between 0 and 1 - random_number2 = random.uniform(0, 1) - - sp2 = 1 if random_number2 < prob_p12 else -1 - return (sp1, sp2) - def update_button1(self, value: int): self.button1 = value self.button1fix = value @@ -882,7 +814,10 @@ def initUI(self): # Set Default self.radioButtonC1.setChecked(True) self.radioButtonC2.setChecked(True) - self.radioButtonFix.setChecked(True) + if cfg.experiment < 0: + self.radioButtonFix.setChecked(True) + else: + self.radioButtonRandom.setChecked(True) self.layout.addLayout(self.gridlayout) diff --git a/mod_spin_operators.py b/mod_spin_operators.py index 639e6b4..90d9122 100644 --- a/mod_spin_operators.py +++ b/mod_spin_operators.py @@ -9,6 +9,7 @@ import cmath import math import numpy as np +import random import sys @@ -208,6 +209,77 @@ def Triplet(self, i: int): case _: raise ValueError("Incorrect index " + str(i)) + def Measure(self, directions1: np.ndarray, + directions2: np.ndarray, simulate_1: bool = True, + update: bool = False): + ''' + Perform the measurement of the spin of two directions 1 and 2, + which one to simulate is decided by the caller. + ''' + def Rho1(psi: np.ndarray): + return np.outer(psi, psi.conj()).reshape( + (2, 2, 2, 2)).trace(axis1=1, axis2=3) + + def Rho2(psi: np.ndarray): + return np.outer(psi, psi.conj()).reshape(( + 2, 2, 2, 2)).trace(axis1=0, axis2=2) + + psi = self.__state + # Define the projector operator for the "+1" state + direction1_p1 = np.array(directions1[0]) + direction1_m1 = np.array(directions1[1]) + direction2_p1 = np.array(directions2[0]) + direction2_m1 = np.array(directions2[1]) + projector1_p1 = np.outer(direction1_p1, direction1_p1.conj()) + projector1_m1 = np.outer(direction1_m1, direction1_m1.conj()) + projector2_p1 = np.outer(direction2_p1, direction2_p1.conj()) + projector2_m1 = np.outer(direction2_m1, direction2_m1.conj()) + + if simulate_1: + # Create 4x4 projectors for the two-spin system + projector_p1_s = np.kron(projector1_p1, np.eye(2)) + projector_m1_s = np.kron(projector1_m1, np.eye(2)) + projector_p11 = projector1_p1 + projector_p21 = projector2_p1 + + # Calculate the reduced density matrix for the first spin + # which is measured + rho_i = Rho1(psi) + else: + projector_p1_s = np.kron(np.eye(2), projector2_p1) + projector_m1_s = np.kron(np.eye(2), projector2_m1) + projector_p11 = projector2_p1 + projector_p21 = projector1_p1 + rho_i = Rho2(psi) + + # Calculate the probability of this first spin being "+1" + prob_p11 = np.linalg.norm(np.trace(np.dot(projector_p11, rho_i))) + # Generate a random number between 0 and 1 + random_number1 = random.uniform(0, 1) + + # Perform the measurement in apparatus direction + sp1 = 1 if random_number1 < prob_p11 else -1 + + # reduce psi projecting on the direction + psi_r = np.dot(projector_p1_s, psi) if sp1 == 1 else \ + np.dot(projector_m1_s, psi) + # Normalize psi_r + psi_r = psi_r / np.linalg.norm(psi_r) + # Calculate the reduced density matrix for the second spin + # with the collapsed wave function for the first system + rho_j = Rho2(psi_r) if simulate_1 else Rho1(psi_r) + + # Calculate the probability of "system 2" being "+1" + prob_p12 = np.linalg.norm(np.trace(np.dot(projector_p21, rho_j))) + + # Generate a random number between 0 and 1 + random_number2 = random.uniform(0, 1) + + sp2 = 1 if random_number2 < prob_p12 else -1 + if update: + self.__state = psi + return (sp1, sp2) + if __name__ == '__main__': if sys.version_info[0] < 3: diff --git a/single_spin_sim.py b/single_spin_sim.py index d6e2a43..edbda77 100644 --- a/single_spin_sim.py +++ b/single_spin_sim.py @@ -2,8 +2,8 @@ ''' /************************/ /* single_spin_sim.py */ -/* Version 1.0 */ -/* 2024/05/11 */ +/* Version 1.1 */ +/* 2024/05/28 */ /************************/ ''' import argparse diff --git a/two_spin_sim.py b/two_spin_sim.py index c392e7f..d5a47ba 100644 --- a/two_spin_sim.py +++ b/two_spin_sim.py @@ -2,8 +2,8 @@ ''' /************************/ /* two_spin_sim.py */ -/* Version 1.0 */ -/* 2024/05/11 */ +/* Version 1.1 */ +/* 2024/05/28 */ /************************/ ''' import argparse @@ -128,7 +128,7 @@ def __init__(self, parent): self.measurementB = None self.count_p1B = 0 self.count_m1B = 0 - self.current_state = None + self.spin = TwoSpin() self.sigma = {'A': {'x': [], 'y': [], 'z': [], 'th_ph': []}, 'B': {'x': [], 'y': [], 'z': [], 'th_ph': []} } @@ -352,10 +352,10 @@ def updateCountA(self, value: int): self.measurementA = -1 def measureAB(self, current_state: np.ndarray, measureA: bool): - self.current_state = current_state + self.spin.psi = current_state for axis in ['z', 'x', 'y']: - sp = self.measureSpin(self.directions[axis], - self.directions[axis], True) + sp = self.spin.Measure(self.directions[axis], + self.directions[axis], True) self.sigma['A'][axis].append(sp[0]) self.sigma['B'][axis].append(sp[1]) @@ -382,16 +382,16 @@ def measureAB(self, current_state: np.ndarray, measureA: bool): np.pi / 2 + self.a_thetaB * cfg.bloch_t / 2)]) if measureA: - sp = self.measureSpin(np.array([directionAp, directionAm]), - np.array([directionBp, directionBm]), True) + sp = self.spin.Measure(np.array([directionAp, directionAm]), + np.array([directionBp, directionBm]), True) self.sigma['A']['th_ph'].append(sp[0]) self.updateCountA(sp[0]) if cfg.m: self.sigma['B']['th_ph'].append(sp[1]) self.updateCountB(sp[1]) else: - sp = self.measureSpin(np.array([directionAp, directionAm]), - np.array([directionBp, directionBm]), False) + sp = self.spin.Measure(np.array([directionAp, directionAm]), + np.array([directionBp, directionBm]), False) self.sigma['B']['th_ph'].append(sp[1]) self.updateCountB(sp[1]) if cfg.m: @@ -419,75 +419,6 @@ def updateCountB(self, value: int): self.count_m1B += 1 self.measurementB = -1 - def measureSpin(self, directionsA: np.ndarray, - directionsB: np.ndarray, simulate_A: bool): - ''' - Perform the measurement of the spin of two systems A and B, - which one to simulate is decided by the caller. - ''' - def RhoA(psi: np.ndarray): - return np.outer(psi, psi.conj()).reshape( - (2, 2, 2, 2)).trace(axis1=1, axis2=3) - - def RhoB(psi: np.ndarray): - return np.outer(psi, psi.conj()).reshape(( - 2, 2, 2, 2)).trace(axis1=0, axis2=2) - - psi = self.current_state - # Define the projector operator for the "+1" state - directionA_p1 = np.array(directionsA[0]) - directionA_m1 = np.array(directionsA[1]) - directionB_p1 = np.array(directionsB[0]) - directionB_m1 = np.array(directionsB[1]) - - projectorA_p1 = np.outer(directionA_p1, directionA_p1.conj()) - projectorA_m1 = np.outer(directionA_m1, directionA_m1.conj()) - projectorB_p1 = np.outer(directionB_p1, directionB_p1.conj()) - projectorB_m1 = np.outer(directionB_m1, directionB_m1.conj()) - - if simulate_A: - # Create 4x4 projectors for the two-spin system - projector_p1_s = np.kron(projectorA_p1, np.eye(2)) - projector_m1_s = np.kron(projectorA_m1, np.eye(2)) - projector_p11 = projectorA_p1 - projector_p21 = projectorB_p1 - - # Calculate the reduced density matrix for the first spin - # which is measured - rho1 = RhoA(psi) - else: - projector_p1_s = np.kron(np.eye(2), projectorB_p1) - projector_m1_s = np.kron(np.eye(2), projectorB_m1) - projector_p11 = projectorB_p1 - projector_p21 = projectorA_p1 - rho1 = RhoB(psi) - - # Calculate the probability of this first spin being "+1" - prob_p11 = np.linalg.norm(np.trace(np.dot(projector_p11, rho1))) - # Generate a random number between 0 and 1 - random_number1 = random.uniform(0, 1) - - # Perform the measurement in apparatus direction - sp1 = 1 if random_number1 < prob_p11 else -1 - - # reduce psi projecting on the direction - psi_r = np.dot(projector_p1_s, psi) if sp1 == 1 else \ - np.dot(projector_m1_s, psi) - # Normalize psi_r - psi_r = psi_r / np.linalg.norm(psi_r) - # Calculate the reduced density matrix for the second spin - # with the collapsed wave function for the first system - rho2 = RhoB(psi_r) if simulate_A else RhoA(psi_r) - - # Calculate the probability of "system 2" being "+1" - prob_p12 = np.linalg.norm(np.trace(np.dot(projector_p21, rho2))) - - # Generate a random number between 0 and 1 - random_number2 = random.uniform(0, 1) - - sp2 = 1 if random_number2 < prob_p12 else -1 - return (sp1, sp2) if simulate_A else (sp2, sp1) - class MainWindow(QWidget):