import numpy as np

def objective_function(x):
    return (x[0] - 1.0)**2 + (x[1] + 2)**2

def create_individual(bounds):
    return np.array([np.random.uniform(low, high) for low, high in bounds])

def exchange_acceptance(delta, temp1, temp2):
    return np.exp(delta * (1/temp1 - 1/temp2))

def replica_exchange_monte_carlo(objective_function, bounds, num_replicas, num_iterations, temperatures):
    replicas = [create_individual(bounds) for _ in range(num_replicas)]
    best_individual = replicas[0]
    best_score = objective_function(best_individual)

    for iteration in range(num_iterations):
        for i in range(num_replicas):
            new_individual = create_individual(bounds)
            delta = objective_function(new_individual) - objective_function(replicas[i])
            if delta < 0 or np.random.rand() < np.exp(-delta / temperatures[i]):
                replicas[i] = new_individual

        for i in range(num_replicas - 1):
            delta = objective_function(replicas[i+1]) - objective_function(replicas[i])
            if np.random.rand() < exchange_acceptance(delta, temperatures[i], temperatures[i+1]):
                replicas[i], replicas[i+1] = replicas[i+1], replicas[i]

        current_best = min(replicas, key=objective_function)
        current_best_score = objective_function(current_best)
        if current_best_score < best_score:
            best_individual, best_score = current_best, current_best_score

    return best_individual, best_score

# 例として、2次関数の最小値を求める
bounds = [(-10, 10), (-10, 10)]
num_replicas = 10
num_iterations = 10000
temperatures = np.linspace(1, 10, num_replicas)

best_individual, best_score = replica_exchange_monte_carlo(objective_function, bounds, num_replicas, num_iterations, temperatures)
print(f"Best individual: {best_individual}")
print(f"Best score: {best_score}")
