def parallel_tempering(graph, function, X, T, iterr, prev_E, history,
                       swap_function, nswaps, nbefore, process_number,
                       process_count, queues_A, pipes_swap, send_ret_val):
    best_path = []
    best_path_value = float('inf')

    for step in range(iterr):
        # Run nbefore steps of simulated annealing
        X, prev_E, delta_E, history, _ = anneal_once(graph, function, X, T,
                                                  prev_E, history,
                                                  swap_function, nswaps)
        if prev_E < best_path_value:
            best_path = X
            best_path_value = prev_E

        # Decide which chains, if any, to exchange
        if step % nbefore == 0:
            #Each process can swap with prior processes
            if process_number < process_count - 1:
                queues_A[process_number].put(delta_E)
                queues_A[process_number].put(T)
            # Acceptance probability
            if process_number > 0:
                next_delta_E = queues_A[process_number - 1].get()
                next_T = queues_A[process_number - 1].get()
                #0 = ln(1)                
                A = np.exp(min(0, ((delta_E - next_delta_E)/T) +
                                  ((next_delta_E - delta_E)/next_T)))

            #Finish any started swap before going to next one
            if process_number < process_count - 1:
                #Check if need to swap with subsequent process
                swap_bool = pipes_swap[0].recv()
                if swap_bool:
                    #If so, publish info then retrieve info
                    pipes_swap[0].send([prev_E, X])
                    prev_E, X = pipes_swap[0].recv()

            #Only initiate swap from higher-indexed process, i.e. not first process
            if process_number > 0:
                if np.random.uniform() < A:
                    #Swap is happening
                    pipes_swap[-1].send(True)
                    #Send info to next process, then retrieve info
                    pipes_swap[-1].send([prev_E, X])
                    prev_E, X = pipes_swap[-1].recv()
                else:
                    #No swap necessary
                    pipes_swap[-1].send(False)

    send_ret_val.send([best_path, history])
    return
def serial_parallel_tempering(graph, function, initial_Xs, initial_temps, 
                              iterr, swap_function, nswaps, nbefore):
    # Make sure inputs are as expected
    assert(len(initial_temps) == len(initial_Xs)), "Mismatched input dimensions"
    assert(initial_temps[0] == 1), "First temperature should be one"

    # Initialize stuff
    nsystems = len(initial_temps)
    Xs = list(initial_Xs)
    Ts = initial_temps
    prev_Es = [function(graph, Xs[i]) for i in range(nsystems)]
    delta_Es = [0] * nsystems
    history = [[] for i in xrange(nsystems)]
    best_path = []
    best_path_value = float('inf')
    current_time = time.time()

    for i in xrange(nsystems):
        history[i].append([prev_Es[i], initial_Xs[i], Ts[i], current_time])

    for step in range(iterr):
        for i in range(nsystems):
            # Run nbefore steps of simulated annealing
            Xs[i], prev_Es[i], delta_Es[i], history[i], _ =\
                    anneal_once(graph, function, Xs[i], Ts[i], prev_Es[i],
                                history[i], swap_function, nswaps)
            # Store best path
            if prev_Es[i] < best_path_value:
                best_path = Xs[i]
                best_path_value = prev_Es[i]

        # Decide which chains, if any, to exchange
        if step % nbefore == 0:
            for i in range(nsystems - 1, 0, -1):
                # Acceptance probability
                # 0 = ln(1)                
                A = np.exp(min(0, ((delta_Es[i] - delta_Es[i-1])/Ts[i]) +
                              ((delta_Es[i-1] - delta_Es[i])/Ts[i-1])))
                if np.random.uniform() < A:
                    # Exchange most recent updates and paths
                    prev_Es[i], prev_Es[i-1] = prev_Es[i-1], prev_Es[i]
                    Xs[i], Xs[i-1] = Xs[i-1], Xs[i]

    return best_path, history