diff --git a/toqito/nonlocal_games/nonlocal_game.py b/toqito/nonlocal_games/nonlocal_game.py index 0536ae4b..abd7326d 100644 --- a/toqito/nonlocal_games/nonlocal_game.py +++ b/toqito/nonlocal_games/nonlocal_game.py @@ -1,5 +1,6 @@ """Two-player nonlocal game.""" +import multiprocessing from collections import defaultdict import cvxpy @@ -120,6 +121,29 @@ def from_bcs_game(cls, constraints: list[np.ndarray], reps: int = 1) -> "Nonloca return cls(prob_mat, pred_mat, reps) + def process_iteration(i:int, num_bob_outputs:int, num_bob_inputs:int, pred_mat_copy:np.ndarray, + num_alice_outputs:int, num_alice_inputs:int)-> float: + """Help the classical_value function as a helper method. + + :return: A value between [0, 1] representing the tgval. + """ + number = i + base = num_bob_outputs + digits = num_bob_inputs + b_ind = np.zeros(digits) + + for j in range(digits - 1, -1, -1): + number, remainder = divmod(number, base) + b_ind[j] = remainder + + pred_alice = np.zeros((num_alice_outputs, num_alice_inputs)) + + for y_bob_in in range(num_bob_inputs): + pred_alice += pred_mat_copy[:, :, int(b_ind[y_bob_in]), y_bob_in] + + tgval = np.sum(np.amax(pred_alice, axis=0)) + return tgval + def classical_value(self) -> float: """Compute the classical value of the nonlocal game. @@ -153,20 +177,24 @@ def classical_value(self) -> float: ) = pred_mat_copy.shape pred_mat_copy = np.transpose(pred_mat_copy, (0, 2, 1, 3)) - for i in range(num_alice_outputs**num_bob_inputs): - number = i - base = num_bob_outputs - digits = num_bob_inputs - b_ind = np.zeros(digits) - for j in range(digits): - b_ind[digits - j - 1] = np.mod(number, base) - number = np.floor(number / base) - pred_alice = np.zeros((num_alice_outputs, num_alice_inputs)) + num_iterations = num_alice_outputs**num_bob_inputs + + # we parallelize for large problems only + if num_iterations > 1000: + with multiprocessing.Pool() as pool: # Creating a pool of processes for parallelization + tgvals = pool.starmap( + NonlocalGame.process_iteration, + [(i, num_bob_outputs, num_bob_inputs, pred_mat_copy, num_alice_outputs, num_alice_inputs) + for i in range(num_iterations)] + ) + p_win = max(tgvals) + # Using single core implementation for small problems + else: + for i in range(num_iterations): + tgval = NonlocalGame.process_iteration(i, num_bob_outputs, num_bob_inputs, pred_mat_copy, + num_alice_outputs, num_alice_inputs) + p_win = max(p_win, tgval) - for y_bob_in in range(num_bob_inputs): - pred_alice = pred_alice + pred_mat_copy[:, :, int(b_ind[y_bob_in]), y_bob_in] - tgval = np.sum(np.amax(pred_alice, axis=0)) - p_win = max(p_win, tgval) return p_win def quantum_value_lower_bound(